php-collective/code-sniffer
A PHP_CodeSniffer ruleset that follows PSR-2 and PSR-12 with many extra fixers on top, plus a stricter PhpCollectiveStrict standard. Install via Composer, add it to phpcs.xml, and run phpcs/phpcbf or composer scripts to check and auto-fix code style.
Architecture fit
phpcs/phpcbf) rules over PSR-2/PSR-12 and bundles ~251 sniffs across Generic, PSR1/2/12, Slevomat, and 88 PhpCollective-specific sniffs, with a stricter PhpCollectiveStrict variant.ruleset.xml reference in the project root, vendor/bin/ppcs invocations, and Composer require-dev. It has zero runtime coupling to application code, so architectural fit is essentially "fits any Composer-based PHP 8.1+ codebase."PhpCollective.*, PhpCollectiveStrict.*) confirm a house style rather than an ecosystem-neutral standard.Commenting and PHP namespaces were repaired. Fit assessment is unchanged.Integration feasibility
composer require --dev php-collective/code-sniffer, add a phpcs.xml referencing vendor/php-collective/code-sniffer/PhpCollective/ruleset.xml, add cs-check/cs-fix Composer scripts, wire into CI. README also documents --config-set installed_paths/default_standard and PhpStorm external-tool setup, which lowers developer-adoption friction.PhpCollective and stricter PhpCollectiveStrict), enabling a phased rollout rather than a single big-bang gate.slevomat/coding-standard or squizlabs/php_codesniffer directly, version conflicts are plausible and must be checked before adoption.PhpCollective.Commenting.DocBlockThrows now detects qualified relative exception names (throw new Sub\NestedException()) that it previously skipped, and resolves unimported names against the enclosing namespace (including files with multiple namespace blocks). An in-place version bump can therefore surface new violations on unchanged code, which means upgrades must be treated as ruleset changes, not routine dependency bumps.Technical risk
phpcbf bugs that produced broken or wrong output: PreferCastOverFunction emitted a parse error on first-class callables (strval(...) → (string)(...)); DocBlockParamAllowDefaultValue appended a spurious |array to params already typed non-empty-array/non-empty-list/list; DocBlockThrows mis-resolved same-namespace exceptions as global (@throws \LocalException instead of @throws \App\LocalException). Two of the three were silent semantic corruption that would pass a "build is green" check and only surface in review or at runtime. This directly validates the earlier recommendation against unattended phpcbf in pre-commit hooks, and raises the bar: fixer output needs diff review, not just a passing exit code.^0.6 constraint is not a safe build-stability posture.phpcbf diff and the review effort to accept or suppress rules the team disagrees with.Key questions
DocBlockThrows namespace-resolution change)? Release notes should flag "new findings on upgrade" explicitly if patch bumps can turn builds red.cakephp/cakephp-codesniffer, and what is the divergence/commitment to track upstream?PhpCollectiveStrict gate on type hints in a way that conflicts with existing Larastan/PHPStan level-8 expectations?Stack fit
vendor/, storage/, bootstrap/cache/, generated code, and migrations initially, or the first run will be dominated by noise.Migration path
phpcs read-only against app/ and tests/, capture violation counts by sniff and a sample of phpcbf diffs. This produces the real cost estimate.phpcbf over a representative sample (including first-class callables, generics-style array docblocks, and exceptions thrown across namespaces — the three areas 0.6.9 just repaired) and confirm the output parses and the semantics are unchanged. Cheap, and it is the only way to size the review burden of step 3.PhpCollective ruleset, suppress sniffs the team rejects with explicit <exclude name="..."> entries and comments explaining why. Record decisions in the repo so they are reviewable.phpcbf on one bounded module, review the diff, merge. Do not mass-fix the whole codebase in one commit.phpcs against a diff), then repo-wide once the violation count is near zero.PhpCollectiveStrict on new code only, via a separate ruleset scoped to a directory, before considering it for the whole repo.Compatibility
squizlabs/php_codesniffer, slevomat/coding-standard, or phpstan/phpstan before committing; if there is one, either drop the direct dev dependency and rely on the bundled version or pin both."php-collective/code-sniffer": "0.6.9") rather than a floating constraint. 0.6.9 demonstrates both halves of the risk: it fixes output corruption (a reason to take it) and it changes what gets reported (a reason not to take it implicitly on an unrelated PR).Sequencing
phpcbf cleanup with reviewed diffs.Maintenance
Support
docs/sniffs.md, GitHub issues, and the upstream PHP_CodeSniffer wiki.cakephp/cakephp-codesniffer or a hand-rolled PHPCS + Slevomat ruleset if the package goes dormant. The exit cost is genuinely small, which is the main reason to accept the adoption risk.Scaling
phpcs.xml template rather than asking each repo to copy XML — otherwise convention drift is guaranteed. If you do that, centralize the version pin and the bump checklist too, so one team owns fixer-correctness validation for everyone.Failure modes
| Failure | Symptom | Mitigation |
|---|---|---|
| Fixer emits invalid or wrong code | Code parses in CI's lint but breaks at runtime; or a docblock/type annotation is silently wrong (cf. 0.6.9: strval(...), |array appended to non-empty-list, @throws \LocalException) |
Review phpcbf diffs; run php -l / test suite after bulk fixes; keep auto-fix off pre-commit hooks |
| Patch bump changes findings | Green build turns red on an unrelated PR; new @throws findings on untouched files |
Pin exact version; re-run baseline on every bump; read release notes for behavior-change flags |
| Pint/PHPCS conflict | Fixers overwrite each other; diffs oscillate | Single owner per concern; disable overlapping sniffs |
Unattended phpcbf |
Semantic changes in unrelated files, noisy PRs | Fix only staged/changed files; review fixer diffs in CI |
| Rule overload | Team adds blanket exclusions; gate becomes meaningless | Start with non-strict standard; justify each exclusion |
| Upstream abandonment | No releases; new PHP version unsupported | Small exit cost; keep a fallback ruleset documented; reassess if cadence stalls |
| Performance | CI lint step grows to minutes | Parallel, diff-scoped, cached runs; exclude generated code |
Ramp-up
phpcs/phpcbf are widely understood, and README provides Composer scripts and PhpStorm external-tool config, so a team can be productive in under a day.TPM recommendation
Treat as Trial, not Adopt, until the spike produces violation counts and a reviewed sample diff — the 0.6.9 release does not change that stance, it sharpens the conditions attached to it. On the positive side, the release is evidence of an actively maintained project that triages correctness bugs and writes genuinely useful release notes, which is better governance signal than the earlier snapshot showed. On the negative side, it confirms the assessment's central operational concern: phpcbf here can silently corrupt valid code, and the docblock/cast sniffs are exactly the layer this package would own under a Pint split. Two consequences: (1) pin the exact version and formalize a bump ritual that re-runs the baseline, because even a patch release can add findings; (2) never run phpcbf unattended — review the diff, and lint/test afterward. Take 0.6.9 over earlier 0.6.x for the three fixer corrections, keep it as the semantic-convention layer rather than a Pint replacement, gate it on changed files first, and revisit the Adopt decision once there is a full quarter of clean bump cycles and a measured violation baseline.
How can I help you explore Laravel packages today?