Skip to content

Add a PHPStan path-coverage check: every PHP-containing dir must be explicitly included or excluded #10

Description

@ballidev

Problem

PHP files in directories that PHPStan never analyses silently escape ALL static-analysis rules. A real example: in a downstream project a $_ENV['X'] ?? '' silent-default smell lived in config/services.php and was never caught — even though ForbidNullCoalescingEmptyStringRule was enabled — because php-qa-ci's PHPStan runner only analyses src and tests, not config. The rule was active; the directory was simply never visited. This is a whole class of blind spot: any dir with .php files that isn't in the analysed set gets zero enforcement, invisibly.

Why this canNOT be a normal PHPStan (AST) rule

A PHPStan rule only fires on nodes inside files PHPStan actually visits. An un-analysed directory is never visited, so a rule placed there can never fire — chicken-and-egg. The enforcement must therefore be a standalone check/tool that runs as part of the php-qa-ci pipeline and inspects the project structure vs the analysis configuration, not the parsed AST.

Proposed solution

Add a php-qa-ci pipeline check (e.g. a tool runnable via qa -t stanPathCoverage, name open to maintainers) that:

  1. Enumerates every directory in the project that contains at least one .php file (excluding vendor/, build/cache output, and generated code by default).
  2. Reads the project's declared PHPStan analysis configuration — the analysed paths plus an explicit exclude list.
  3. FAILS the pipeline if any PHP-containing directory is neither explicitly included in the analysed paths nor explicitly excluded — forcing a conscious include/exclude decision for every such directory.

Intended default policy (overridable per project): vendor/ → exclude; src/, tests/, config/ → include. The key property is that adding a new top-level PHP dir (or a config/ that was silently unanalysed) trips the check until someone explicitly classifies it.

Acceptance criteria

  • A new check integrated into the php-qa-ci pipeline (and into the relevant meta-groups, e.g. allStatic).
  • Project-level configuration to declare include/exclude classifications (with sensible defaults).
  • The check fails with a clear, actionable message naming each unclassified PHP directory.
  • Tests covering: a fully-classified project (passes); a project with an unclassified PHP dir like config/ (fails); explicitly-excluded dirs (passes).
  • Docs describing configuration and rationale.

Context / origin

Surfaced during downstream work (checkout "plan 00097" — Zoho dual-account credentials) when a $_ENV[...] ?? '' smell in a vendor config/services.php slipped past an enabled ForbidNullCoalescingEmptyStringRule purely because config/ was outside the analysed paths. The fix for that one instance was applied directly; this issue is the "defence before fix" ratchet to prevent the entire class recurring across all php-qa-ci consumers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions