Skip to content

Add PHPStan analysis for PHP blocks in feature files - #354

Open
swissspidy wants to merge 3 commits into
mainfrom
claude/phpstan-integration-pr-340-wxxccy
Open

Add PHPStan analysis for PHP blocks in feature files#354
swissspidy wants to merge 3 commits into
mainfrom
claude/phpstan-integration-pr-340-wxxccy

Conversation

@swissspidy

@swissspidy swissspidy commented Aug 9, 2026

Copy link
Copy Markdown
Member

Feature files embed PHP snippets in docstrings that no static analysis tool
currently looks at. This extracts those snippets into standalone PHP files
padded so that their line numbers match the feature file, runs PHPStan over
them, and reports the findings against the feature file they came from.

PHPStan needs a few things that a code style check does not:

  • The opening tag has to come first. Padding in front of it counts as inline
    HTML, which makes a declare() or namespace statement in a block a fatal
    error, so the tag a block brings along itself is dropped instead.
  • Blocks that are not standalone PHP have to be left out, since a single file
    that fails to parse makes PHPStan abort the whole run. Feature files hold
    deliberately broken snippets as well as Behat placeholders such as
    {POST_ID} that are substituted before the snippet is written to disk.
  • Blocks declaring the same class or function are analysed separately from
    each other, so that a name is not resolved to another block's declaration.

The analysis is opt-in through a phpstan-feature-files.neon[.dist] file in
the package root, layered on top of the defaults in phpstan/feature-files.neon.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01M4oxq4G2FxhwJfHd3w6VLw

Summary by CodeRabbit

  • New Features

    • Added PHPStan analysis for PHP code embedded in Behat feature files.
    • Diagnostics are mapped back to the original feature files.
    • Added commands to extract, analyze, and report on embedded PHP snippets.
    • Automatically handles skipped or invalid snippets and duplicate declarations.
  • Documentation

    • Added setup and configuration guidance for feature-file analysis.
    • Updated Composer test commands to include PHPStan checks.
  • Chores

    • Added the required PHP tokenizer extension for this analysis.

Feature files embed PHP snippets in docstrings that no static analysis tool
currently looks at. This extracts those snippets into standalone PHP files
padded so that their line numbers match the feature file, runs PHPStan over
them, and reports the findings against the feature file they came from.

PHPStan needs a few things that a code style check does not:

* The opening tag has to come first. Padding in front of it counts as inline
  HTML, which makes a `declare()` or `namespace` statement in a block a fatal
  error, so the tag a block brings along itself is dropped instead.
* Blocks that are not standalone PHP have to be left out, since a single file
  that fails to parse makes PHPStan abort the whole run. Feature files hold
  deliberately broken snippets as well as Behat placeholders such as
  `{POST_ID}` that are substituted before the snippet is written to disk.
* Blocks declaring the same class or function are analysed separately from
  each other, so that a name is not resolved to another block's declaration.

The analysis is opt-in through a `phpstan-feature-files.neon[.dist]` file in
the package root, layered on top of the defaults in `phpstan/feature-files.neon`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M4oxq4G2FxhwJfHd3w6VLw
@swissspidy
swissspidy requested a review from a team as a code owner August 9, 2026 12:06
Copilot AI lite review requested due to automatic review settings August 9, 2026 12:06
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e5c65aea-810d-474c-9e1b-b29c91f9f70a

📥 Commits

Reviewing files that changed from the base of the PR and between 515ed32 and e94758a.

📒 Files selected for processing (5)
  • .readme-partials/USING.md
  • README.md
  • bin/run-phpstan-tests
  • tests/tests/TestPhpStanFeatureFiles.php
  • utils/phpstan-feature-files.php
🚧 Files skipped from review as they are similar to previous changes (3)
  • README.md
  • .readme-partials/USING.md
  • bin/run-phpstan-tests

📝 Walkthrough

Walkthrough

The change adds PHPStan analysis for PHP blocks embedded in Behat feature files. It adds extraction and reporting utilities, integrates them into the Composer test workflow, adds configuration, and documents and tests the workflow.

Changes

Feature-file PHPStan analysis

Layer / File(s) Summary
Feature extraction and batching
utils/phpstan-feature-files.php, tests/tests/TestPhpStanFeatureFiles.php
Extracts PHP blocks, preserves source lines, skips invalid blocks, separates duplicate declarations, writes manifests, and tests extraction behavior and safety checks.
Manifest loading and diagnostic reporting
utils/phpstan-feature-files.php, tests/tests/TestPhpStanFeatureFiles.php
Maps PHPStan JSON diagnostics to feature-file locations, reports skipped blocks and failures, and tests reporting cases.
PHPStan test runner integration
bin/run-phpstan-tests, composer.json, phpstan-feature-files.neon.dist, phpstan/feature-files.neon, .readme-partials/USING.md, README.md
Runs standard and feature-file analysis, preserves accumulated exit status, adds tokenizer support, and supplies PHPStan configuration and Composer commands.
Usage documentation
.readme-partials/USING.md, README.md
Documents extraction, diagnostic mapping, configuration, ignored errors, and skipped blocks.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Composer
  participant run_phpstan_tests
  participant phpstan_feature_files
  participant PHPStan
  participant FeatureFiles
  Composer->>run_phpstan_tests: run aggregate test command
  run_phpstan_tests->>phpstan_feature_files: extract PHP blocks
  phpstan_feature_files->>FeatureFiles: read feature files
  phpstan_feature_files-->>run_phpstan_tests: return batches and manifest
  run_phpstan_tests->>PHPStan: analyze PHP batches
  PHPStan-->>run_phpstan_tests: return JSON diagnostics
  run_phpstan_tests->>phpstan_feature_files: report diagnostics
  phpstan_feature_files-->>Composer: return mapped findings and status
Loading

Possibly related PRs

  • wp-cli/wp-cli-tests#340: Both changes add tooling for extracting and processing PHP blocks from Behat feature files.

Suggested labels: scope:testing

Suggested reviewers: brianhenryie

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding PHPStan analysis for PHP blocks in feature files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/phpstan-integration-pr-340-wxxccy

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.2.7)

PHPStan was skipped because the config uses disallowed bootstrapFiles, bootstrapFile, or includes directives.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M4oxq4G2FxhwJfHd3w6VLw

This comment was marked as resolved.

@swissspidy

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Fixes the defects the review surfaced, all of which are reproducible:

* Map the errors PHPStan reports through a canonical path instead of a
  string prefix. The path it reports is not necessarily spelled the way the
  extraction wrote it -- macOS resolves `/var` to `/private/var` and Windows
  has a short and a long form of a directory name -- which made every finding
  come out as `Unexpected file` and failed the unit tests on both platforms.
* Resolve the symlink Composer installs in the vendor binary directory before
  deriving the root of this package from it. Without that the analysis was
  silently skipped for every package using the framework, since it looked for
  the extraction script under `vendor/utils` and never found it.
* Keep a package whose feature files hold no PHP block at all from failing.
  No batch means no results, which left the glob unexpanded and made the
  report treat the pattern itself as a missing file.
* Guard the fields of the PHPStan results before reading them.
* Only pass PHPStan's own output through when it produced no results, so its
  notes about interpreting errors do not bury the report.
* Skip the dot entries while looking for feature files.

The rule that a docstring opening with `<?php` counts as PHP whichever step
precedes it is kept: it covers PHP files that are not named `*.php`, such as
the `.maintenance` file. The documentation now says so instead of claiming
that only a `.php` file step is considered.

Also corrects the documented way to ignore an error, which matched nothing:
the pattern applies to the extracted file, whose name carries the line range
and is relative to the `features` directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M4oxq4G2FxhwJfHd3w6VLw

This comment was marked as resolved.

@swissspidy

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@swissspidy swissspidy added the scope:testing Related to testing label Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope:testing Related to testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants