netresearch/php-modernization
v1.23.6(MIT AND CC-BY-SA-4.0)
PHP 8.x modernization patterns with type safety and PHPStan
Changelog
All notable changes to php-modernization-skill are documented in this file.
The format follows Keep a Changelog 1.1.0, and this project adheres to Semantic Versioning.
Section ordering within a release: Added → Changed → Deprecated → Removed → Fixed → Security.
Unreleased
Fixed
- PM-05 (checkpoint and verifier) no longer fails a project whose
.php-cs-fixer.dist.phprequires the sharednetresearch/typo3-ci-workflowsconfig: that config enables@PER-CS3x0inconfig/php-cs-fixer/rules.php, but the project file carries no literal@PER-CS, and the resolved rule set is not readable beforecomposer install. New regression fixturestypo3-extension-shared-cs-configandtypo3-extension-shared-cs-config-comment. Only arequireof the shared config counts; a comment naming its path, a commented-outrequireor a copied usage docblock does not satisfy PM-05. Neither does@PER-CSinside a comment (new fixturetypo3-extension-per-cs-comment) - PM-10, PM-11 and PM-12 accept a
requireof the shared Rector config ofnetresearch/typo3-ci-workflows, which appliesLevelSetList::UP_TO_PHP_82,SetList::DEAD_CODEandSetList::CODE_QUALITYunconditionally - PM-30 accepts a
requireof the shared php-cs-fixer factory (config/php-cs-fixer/config.php), which setsheader_commentfrom the header the project passes - PM-06 no longer passes on the config file's own
declare(strict_types=1): it needs thedeclare_strict_typesrule switched on, directly or via the shared config. In PM-05, PM-06, PM-10/11/12, PM-24 and PM-30 a rule, set orrequireinside a comment (a comment line, a docblock or a trailing// ...) no longer counts, and PM-10 needsLevelSetList::rather than itsuseimport - PM-24 no longer passes on
setRiskyAllowed(true), a comment or another:riskyset: it needs a PER-CS risky set by name, switched on with=> trueoutside a comment. The shared config enables@PER-CS3x0, not its risky variant, so it does not satisfy PM-24
1.23.6 - 2026-09-27
Added
references/composer-package-metadata.md: avcsrepository entry with a GitHub URL for a package that is also on Packagist makes dependency resolution (composer update, orcomposer installwithout acomposer.lock) read versions throughapi.github.com, so CI fails with HTTP 429 once the unauthenticated limit of 60 requests per hour is spent. The new trap paragraph gives the Packagist check, acomposer update --dry-run -vvvverification with its exit status, the fix (delete the entry), and the fallbacks when the source must stay VCS ("no-api": trueor agithub-oauthtoken)
1.23.5 - 2026-09-23
Added
- Evals:
parse-user-supplied-date-refuses-rollover
Fixed
references/request-dtos.md: the date helpers caught the constructor's exception and nothing else, so2026-02-30was accepted as 2026-03-02 and24:00as the next day. They now checkDateTimeImmutable::getLastErrors()after the constructor, and a new "Safe Date Handling" section explains why, the PHP 8.1 difference, and thatcreateFromFormat()needs the same check
1.23.4 - 2026-09-20
Added
references/lexing-php-across-versions.md: how token streams differ between PHP versions and what that costs a tool that reads them, so a hand-rolled splitter is recognised as re-implementingnikic/php-parser's emulative lexer before it is written
Changed
references/static-analysis-tools.mdnames one authoritative style tool instead of listing several as equals, and CaptainHook as the hook runner- The oracle catch in the reference examples is narrowed to the exception it is meant to handle, and
TMPis created before it is used
1.23.3 - 2026-09-16
Fixed
checkpoints.yamldeclared no preconditions, so/assessran every check against any repository. On a Python repository without acomposer.jsonit reported 13 failures out of 29 checks that were not about that repository. The skill is now gated oncomposer.jsonexisting, matching the scopeSKILL.mdalready declares incompatibility: "Requires php 8.1+, composer.". Any Composer project still runs every check, TYPO3 or not
1.23.2 - 2026-09-12
Added
- Type safety:
preg_match()returnsfalse, not0, when the subject is not valid UTF-8 under/u— the two comparison shapes that misread it, thepreg_match('//u', ...)encoding check that separates the states, and why an unvalidated value aborts a run inUnicodeStringrather than failing at the branch
1.23.1 - 2026-09-11
Added
- README: the
/plugin installstep missing from the marketplace section, and a "Without a marketplace" section covering the skills-directory route - Evals:
read-a-php-config-file-without-running-itandconst-expr-fallback-must-not-resolve-any-constant
1.23.0 - 2026-09-05
Added
reading-php-config-safely.md: read a PHP config file without executing it
Fixed
php:S2003is a false positive on a value-returning include- Reading a PHP config handles return-based files, targets the right assignment, and allowlists constants
1.22.3 - 2026-09-03
Fixed
verifier: the PHP-CS-Fixer config is searched for underBuild/- Checkpoints accept
Build/tooling paths and theci:*script convention
Documentation
- Composer package metadata reference added
composer: the trait branch dropped, the eval made directional, a fresh temp dir per runcomposer: the implements check widened; an empty result is called a first pass
[1.22.2] - 2026-08-27
Fixed
- Checkpoints PM-01/PM-02/PM-44 brought onto the runner contract
[1.22.1] - 2026-08-20
Changed
- The multi-version adapter reference no longer names the repository its example came from. The pattern applies to any extension whose dependency ships incompatible major versions; the repository name was the only part that did not travel, and that repository is an evaluation target in netresearch/agent-system-evals, where this skill is part of the fleet under test (#94).
[1.22.0] - 2026-08-08
Added
- Add Agent Plugins 1.0.0 portable manifest (manifest)
1.21.0 - 2026-08-03
Added
PM-44checkpoint: detect self-referencing class constants.
Changed
- Reference guidance updates: fluent wither over new constructor param on consistent-constructor DTOs (PHPStan),
PHP_BINARYempty under non-CLI SAPIs (Symfony patterns), DEAD_CODE Rector rewrites can change intent, PHP-CS-Fixer pre-push gotchas (cache, unused imports), refreshing a committed lock at the minimum PHP version, reviewing lines Rector adds.
Fixed
- SonarCloud quality gate: includes-block boundary matching restructured out of an anchored-alternation regex (
python:S5850/S6395); synthetic fixtures excluded from Automatic Analysis via.sonarcloud.properties(text:S8567).
1.19.0 - 2026-06-11
Added
PM-43(LLM review): PSR-15 middleware broad catch blocks —catch(\Throwable),catch(\Exception), and multi-catch forms — must re-throw framework control-flow exceptions before handling the error. Covers TYPO3 (ImmediateResponseException,PropagateResponseException,StatusException) and Symfony (HttpExceptionInterface,AccessDeniedException), with correct re-throw guard examples for both stacks. Surfaced by a production incident where a middleware swallowed control-flow exceptions and broke custom error pages.
Fixed
PM-43descuses single-quoted YAML so the literal backslash in\Throwabledoes not break YAML parsing.
1.18.0 - 2026-05-28
This entry collects the cumulative work landed on main after the v1.15.1 tag — the agent-harness refocus that turns the skill into an executable contract instead of a static reference bundle. Release notes will be assigned a version when the next tag is cut.
Added
Agent-contract executable layer
skills/php-modernization/scripts/verify_php_project.py— primary mechanical verifier. PEP 723, runnable viauv run. Emits JSON (schema_version: 1.0.0), SARIF 2.1.0, or JUnit XML. Supports--summary,--check PM-XX,--no-tools,--no-cache,--root. Includes machine-readableagent_actions[]recommendations so downstream agents can plan fixes without re-reading the rule descriptions.skills/php-modernization/scripts/modernize_loop.py— orchestrator that chains PHP-CS-Fixer, Rector, PHPStan, and Infection (PR-diff mode) into a single transcript. Defaults to--mode dry-run;applymode requires--confirm.skills/php-modernization/scripts/introspect.py— cheap first-touch profiler. Detects archetype, PHP version constraint, tooling fingerprints, and PSR-4 layout without invoking subprocesses (other thanphp --version). Always exits 0.skills/php-modernization/scripts/_common.py— shared archetype detection, composer parsing, and version helpers used by the verifier and introspector.skills/php-modernization/scripts/verify-php-project.shrewritten as a thin Bash wrapper that dispatches to eitherverify_php_project.py(default) orintrospect.py(introspectsubcommand), with apython3fallback whenuvis missing.
JSON Schemas (public output contract)
schemas/verification-result.schema.json— JSON Schema 2020-12 for verifier output.schemas/project-profile.schema.json— JSON Schema 2020-12 for introspector output.
Reference documents
skills/php-modernization/references/php-8.4.md— property hooks, asymmetric visibility, lazy objects,array_find/array_any/array_all.skills/php-modernization/references/php-8.5.md— pipe operator|>,array_first/array_last,#[\NoDiscard].skills/php-modernization/references/immutability-boundaries.md— whenreadonlyapplies vs. property hooks vs. classic mutation.skills/php-modernization/references/doctrine-modernization-edges.md— Doctrine ORM 2.x/3.x, mapped superclasses, embeddables; explicitly forbidsreadonlyon entities.skills/php-modernization/references/mutation-testing.md— Infection diff-mode workflow, MSI thresholds, baseline strategy.skills/php-modernization/references/api-platform-edges.md— API Platform 3.x/4.x state-providers, processors, resource separation.skills/php-modernization/references/psr15-middleware-architecture.md— PSR-15 middleware stacking, request-handler patterns, framework integration.
Checkpoints
PM-39(mechanical):readonlykeyword presence on Doctrine entity classes — fails whenreadonly classcollides with#[Entity].PM-40(LLM review): broadreadonlyreview covering inheritance, embeddables, and serialization edges.PM-41(LLM review): Infection diff-mode wiring — checks that PR-mode mutation testing is configured.PM-42(LLM review): API Platform resource separation — entity vs. API resource boundary.
Templates
skills/php-modernization/templates/composer-scripts.json— drop-inscriptsblock (cs:fix,cs:check,phpstan,rector,rector:check,phpat,audit,skill:inspect,skill:verify,skill:fix,skill:qa).skills/php-modernization/templates/README.md— consumption guide and per-script reference.skills/php-modernization/templates/github-actions/php-modernization.yml— copy-and-modify reusable workflow that runs the verifier, uploads SARIF to the GitHub code-scanning tab, runs the orchestrator in dry-run mode, and posts a PR summary comment on failure.
Regression suite
fixtures/generic-composer-minimal/,fixtures/symfony-app-minimal/,fixtures/typo3-extension-minimal/,fixtures/monorepo-minimal/,fixtures/fully-modern/— synthetic project shapes covering the four detected archetypes plus a positive control.fixtures/<name>/expected/verifier.json— golden snapshots for each fixture.scripts/test_fixtures.py— golden-snapshot diff runner with normalization for non-deterministic fields (generated_at,project_root,environment.php_runtime,tool_runs[]).
Project-archetype detection
- Four archetypes auto-detected by the verifier and orchestrator:
generic-composer,typo3-extension,symfony-app,monorepo-package(plusunknown).
Other
sonar-project.properties— excludesfixtures/from SonarCloud analysis..gitignoreentries for Python bytecode caches.
Changed
SKILL.mdrestructured as an agent router with hard guardrails: discover → drill → apply → references decision flow, reference-routing table, and explicit refusal cases. Long-form content was moved into the references;SKILL.mditself is now a short contract.- Verifier output is now treated as a public schema. Checkpoint IDs are stable; renumbering is forbidden.
verify-php-project.shis now a wrapper, not the verifier — the implementation lives in Python.
Deprecated
- Checkpoint IDs
PM-26,PM-27,PM-28,PM-33,PM-35,PM-36are permanently retired in this skill. They MUST NOT be reused. Equivalent checks now live intypo3-conformanceandtypo3-extension-upgrade.
Removed
skills/php-modernization/references/typo3-psr-patterns.md— TYPO3-specific PSR guidance migrated to the TYPO3 skill set as part of the dedup.- The TYPO3-specific checkpoints listed above (relocated, see Deprecated).
Fixed
- (No standalone bug-fix entries since v1.15.1; behaviour changes are folded into Added/Changed.)
Security
- (No security advisories since v1.15.1.)
1.15.1 - 2026-04-25
(historical — pre-CHANGELOG)