Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Development

Enter the reproducible development environment and install ordinary mutable Composer dependencies for interactive work:

nix develop
composer install

Before submitting a change, run the complete normal validation suite:

nix flake check --keep-going -L

That command runs the supported-PHP PHPUnit matrix, PHPStan, php-cs-fixer, Composer and PHP linting, documentation formatting, the mdBook build and link check, Larastan and minimum-PHPStan consumer checks, and the pinned Laravel runtime audits. It does not run mutation testing.

Focused Composer commands remain useful while developing:

composer exec phpunit
composer exec phpstan analyse
composer cs
composer validate --strict

Choose a test layer with Testing and Runtime Verification.

Documentation

Build and check the mdBook:

composer docs
composer docs:check
composer docs:serve

composer docs:check builds the book and fails on broken same-site relative links in the generated HTML.

The sidebar keeps every page’s h2/h3 outline open. mdBook only injects headings for the active page, so docs/theme/phpstan-laravel-validation.js adds the remaining outlines from headingsByChapter. Update that map when public headings change.

The optional Heliogenesis control is mounted from docs/theme/phpstan-laravel-validation.js. The unmodified Doctrine runtime lives under docs/pages/assets/heliogenesis/. The theme marks the reading plane so the event can light the article and run document tomography.

The Document Looks Back integration is mounted separately from docs/pages/assets/document-looks-back/. After it mounts, window.documentLooksBack is the Doctrine controller, so window.documentLooksBack.summon() requests one immediate eye. A mount or renderer failure of either integration leaves the documentation usable.

The copied-runtime tests live in the documentation PHPUnit group. They compare those assets to the root Composer Doctrine pin. Laravel-matrix and minimum-PHPStan Nix jobs exclude the group because those lockfiles do not install that pin.

Akashi formats inline PHP fences in the README, changelog, and docs/:

composer docs:format
composer docs:format:fix

composer cs:fix formats PHP source and those documentation fences.

Akashi checks formatting. It does not execute illustrative fragments as standalone programs.

The published site is https://jbboehr.github.io/phpstan-laravel-validation/. .github/workflows/pages.yml builds the book on develop and master, and deploys only from master.

Mutation testing

Mutation testing uses an isolated toolchain because Infection requires PHP 8.3 or newer while this package supports PHP 8.1. It is excluded from nix flake check. Run it explicitly:

nix build -L .#mutation

The package supplies PHP 8.5 with PCOV and preserves the thresholds, timeouts, test exclusions, and worker count from infection.json5.dist. It divides the source into four cached shard derivations, each using four Infection workers, then aggregates the project-wide thresholds. GitHub builds those shards serially so a four-core runner is not oversubscribed.

PHPStan type-inference fixtures that cover extension code must run their first gatherAssertTypes() analysis inside the test body. Data providers run before PHPUnit starts coverage and can warm PHPStan’s process-level caches. The test-only AssertsFixtureUnderCoverage trait implements this pattern. Infection runs only the individual test cases that cover each mutant.

Tests in the subprocess group remain in the normal suite but are excluded from mutation testing because child processes cannot observe the active in-process mutant. The property group is also excluded: rerunning hundreds of generated cases for each mutant would be disproportionate. Promoted deterministic regressions remain available to Infection.

The Infection configuration also ignores mutations that make RuleTreeNode::resolvePath() recurse without consuming input. Those mutants can exhaust PHP’s native stack before Infection’s timeout can stop the process.

The php85 Nix development shell includes PCOV for focused manual investigation.

Nix dependency hashes

Nix builds offline Composer repositories from committed lockfiles. When Composer dependencies change, update the hashes in nix/vendor-hashes.nix as described in CONTRIBUTING.md.

CI

.github/workflows/ci.yml has two surfaces:

  • a conventional PHP baseline job on PHP 8.5 (Composer, PHPUnit, PHPStan, php-cs-fixer);
  • an exhaustive Nix matrix generated from flake checks, plus mutation.

A documentation failure is a flake-check failure. It is not silent.

Downstream investigations

Pinned application investigations live under docs/development/. They are evidence for specific experiments, not user-facing support promises.