neophp / codechecker-package
Configurable static code rule checker for NeoPHP projects
Requires
- php: >=8.5
README
A configurable static code rule checker for NeoPHP. Define rules once, in a config file, and scan an entire project with a single command — forbidden namespace usages, forbidden code patterns, or required ones, all defined declaratively rather than hard-coded into a script.
Structure
codechecker-package/
├── composer.json
├── README.md
├── src/
│ ├── NeoCodeCheckerPackage.php
│ ├── Commands/
│ │ ├── CodeCheckerScanCommand.php
│ │ └── CodeCheckerAddRuleCommand.php
│ └── Service/
│ ├── RuleLoader.php
│ └── RuleChecker.php
└── config/
└── codechecker-rules.config.php
Installation
php bin/neo package:require neophp/codechecker-package --project=MyProject
Register it in the project's Config/app.config.php:
return [ // ... 'packages' => [ \Vendor\NeoPHP\CodeCheckerPackage\NeoCodeCheckerPackage::class, ], ];
On first boot, config/codechecker-rules.config.php is copied to
Config/Packages/CodeChecker/codechecker-rules.config.php in your
project — pre-populated with a few baseline rules (no var_dump(), no
dd(), no die() in controllers, entities must carry #[Entity]).
Edit this file freely; it is never overwritten once it exists.
Usage
Scan the project
php bin/neo codechecker:scan --project=MyProject
Scanning MyProject…
✘ App/Controllers/ReportsController.php:34
Rule: no-var-dump
No var_dump() calls should remain in the codebase
1 violation(s) found.
Exits with a non-zero code if any violation is found — usable as a CI
step, the same way phpstan analyse is typically wired into a pipeline.
Add a rule interactively
Instead of hand-editing the config array, add a rule step by step:
php bin/neo codechecker:add-rule --project=MyProject
Rule name: no-raw-sql-in-repositories
Description: Repositories must never build raw SQL query strings
Applies to (namespace pattern, * for all): App\Repository\*
Rule type
[1] forbidden_use — a specific `use X;` must NOT appear
[2] forbidden_call — a specific string must NOT appear
[3] required_use — a specific `use X;` MUST appear
[4] required_call — a specific string MUST appear
Type (1-4): 2
Value (class-string to require/forbid, or string pattern): ->query(
✔ Rule 'no-raw-sql-in-repositories' added to codechecker-rules.config.php
The existing rules in the file are preserved — the new rule is appended to the array.
Rule format
Each rule is an associative array with 5 keys:
[
'name' => 'unique-rule-name',
'description' => 'Shown in scan output when this rule is violated',
'applies_to' => 'App\\Controllers\\*', // namespace glob pattern, or '*' for every file
'type' => 'forbidden_call', // see table below
'value' => 'var_dump(', // what to search for
],
| Type | Checks |
|---|---|
forbidden_use |
A specific use X; statement must not appear in the file |
forbidden_call |
A specific string must not appear anywhere in the file |
required_use |
A specific use X; statement must appear in the file |
required_call |
A specific string must appear anywhere in the file |
forbidden_use/required_use and forbidden_call/required_call are
checked identically under the hood — both search for value as a plain
substring of the file's content. _use variants exist as a naming
convention for readability (checking a use statement specifically) but
technically behave the same as _call. Write the full string you're
checking for either way (e.g. 'use Neo\\Core\\Database\\DatabaseManager;'
for a forbidden_use rule, not just the class name).
applies_to supports a single trailing wildcard (App\Controllers\*
matches any class under that namespace) or * to apply to every scanned
file, regardless of namespace.
Known limitations
- Substring matching, not real static analysis. A rule matches on plain text presence in the file — it does not parse the AST, so it cannot distinguish a real usage from, say, a match inside a comment or a string literal. This makes rules simple to write and fast to run, at the cost of occasional false positives — review scan results, don't treat them as infallible.
applies_tomatches on namespace, not file path. A file's namespace must be resolvable (a valid PHP class with anamespacedeclaration) for the rule to apply — non-class files are skipped entirely.- The config file is rewritten from scratch by
codechecker:add-ruleusing a custom array exporter (to keep[]short array syntax rather than PHP's defaultarray(...)fromvar_export()). Any hand-written comments incodechecker-rules.config.phpwill be lost the next time you run this command — edit the file directly instead if you want to keep comments, and only useadd-rulefor quick, comment-free additions.
License
MIT