Skip to content

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.php requires the shared netresearch/typo3-ci-workflows config: that config enables @PER-CS3x0 in config/php-cs-fixer/rules.php, but the project file carries no literal @PER-CS, and the resolved rule set is not readable before composer install. New regression fixtures typo3-extension-shared-cs-config and typo3-extension-shared-cs-config-comment. Only a require of the shared config counts; a comment naming its path, a commented-out require or a copied usage docblock does not satisfy PM-05. Neither does @PER-CS inside a comment (new fixture typo3-extension-per-cs-comment)
  • PM-10, PM-11 and PM-12 accept a require of the shared Rector config of netresearch/typo3-ci-workflows, which applies LevelSetList::UP_TO_PHP_82, SetList::DEAD_CODE and SetList::CODE_QUALITY unconditionally
  • PM-30 accepts a require of the shared php-cs-fixer factory (config/php-cs-fixer/config.php), which sets header_comment from the header the project passes
  • PM-06 no longer passes on the config file's own declare(strict_types=1): it needs the declare_strict_types rule 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 or require inside a comment (a comment line, a docblock or a trailing // ...) no longer counts, and PM-10 needs LevelSetList:: rather than its use import
  • PM-24 no longer passes on setRiskyAllowed(true), a comment or another :risky set: it needs a PER-CS risky set by name, switched on with => true outside 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: a vcs repository entry with a GitHub URL for a package that is also on Packagist makes dependency resolution (composer update, or composer install without a composer.lock) read versions through api.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, a composer update --dry-run -vvv verification with its exit status, the fix (delete the entry), and the fallbacks when the source must stay VCS ("no-api": true or a github-oauth token)

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, so 2026-02-30 was accepted as 2026-03-02 and 24:00 as the next day. They now check DateTimeImmutable::getLastErrors() after the constructor, and a new "Safe Date Handling" section explains why, the PHP 8.1 difference, and that createFromFormat() 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-implementing nikic/php-parser's emulative lexer before it is written

Changed

  • references/static-analysis-tools.md names 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 TMP is created before it is used

1.23.3 - 2026-09-16

Fixed

  • checkpoints.yaml declared no preconditions, so /assess ran every check against any repository. On a Python repository without a composer.json it reported 13 failures out of 29 checks that were not about that repository. The skill is now gated on composer.json existing, matching the scope SKILL.md already declares in compatibility: "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() returns false, not 0, when the subject is not valid UTF-8 under /u — the two comparison shapes that misread it, the preg_match('//u', ...) encoding check that separates the states, and why an unvalidated value aborts a run in UnicodeString rather than failing at the branch

1.23.1 - 2026-09-11

Added

  • README: the /plugin install step missing from the marketplace section, and a "Without a marketplace" section covering the skills-directory route
  • Evals: read-a-php-config-file-without-running-it and const-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:S2003 is 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 under Build/
  • Checkpoints accept Build/ tooling paths and the ci:* script convention

Documentation

  • Composer package metadata reference added
  • composer: the trait branch dropped, the eval made directional, a fresh temp dir per run
  • composer: 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-44 checkpoint: detect self-referencing class constants.

Changed

  • Reference guidance updates: fluent wither over new constructor param on consistent-constructor DTOs (PHPStan), PHP_BINARY empty 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-43 desc uses single-quoted YAML so the literal backslash in \Throwable does 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 via uv 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-readable agent_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; apply mode 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 than php --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.sh rewritten as a thin Bash wrapper that dispatches to either verify_php_project.py (default) or introspect.py (introspect subcommand), with a python3 fallback when uv is 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 — when readonly applies vs. property hooks vs. classic mutation.
  • skills/php-modernization/references/doctrine-modernization-edges.md — Doctrine ORM 2.x/3.x, mapped superclasses, embeddables; explicitly forbids readonly on 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): readonly keyword presence on Doctrine entity classes — fails when readonly class collides with #[Entity].
  • PM-40 (LLM review): broad readonly review 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-in scripts block (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 (plus unknown).

Other

  • sonar-project.properties — excludes fixtures/ from SonarCloud analysis.
  • .gitignore entries for Python bytecode caches.

Changed

  • SKILL.md restructured 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.md itself is now a short contract.
  • Verifier output is now treated as a public schema. Checkpoint IDs are stable; renumbering is forbidden.
  • verify-php-project.sh is 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-36 are permanently retired in this skill. They MUST NOT be reused. Equivalent checks now live in typo3-conformance and typo3-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)