Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Code Sniffer Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a PHP_CodeSniffer standard (squizlabs/php_codesniffer), not a PHP-CS-Fixer preset — so it is a sibling/alternative to Laravel Pint, not a plugin for it. Pint fixes formatting; this package additionally enforces docblocks, native type hints, declare(strict_types=1), namespace/use ordering, and a long list of opinionated "don't do this" rules.

Install and wire it up in three steps:

composer require --dev php-collective/code-sniffer

Project-root phpcs.xml — start narrow, do not point it at the whole repo on day one:

<?xml version="1.0"?>
<ruleset name="laravel-app">
    <file>app/</file>
    <file>tests/</file>

    <arg name="extensions" value="php"/>
    <arg name="colors"/>
    <arg value="ps"/> <!-- progress + show sniff codes -->

    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/storage/*</exclude-pattern>
    <exclude-pattern>*/bootstrap/cache/*</exclude-pattern>
    <exclude-pattern>*/public/*</exclude-pattern>
    <exclude-pattern>*/_ide_helper*.php</exclude-pattern>

    <rule ref="vendor/php-collective/code-sniffer/PhpCollective/ruleset.xml"/>
</ruleset>

Then:

vendor/bin/phpcs -i                 # sanity: is the "PhpCollective" standard registered?
vendor/bin/phpcs app/Http --report=summary
vendor/bin/phpcs -s app/Http/Controllers/UserController.php   # -s prints the exact sniff to exclude

Where to look first

  • docs/sniffs.md in the installed package — the full sniff inventory, grouped by upstream standard (Generic, PSR1/2/12, SlevomatCodingStandard, PhpCollective, PhpCollectiveStrict). Read this before writing a single exclusion; most "weird" rules are named here.
  • PhpCollective/ruleset.xml and PhpCollectiveStrict/ruleset.xml — the actual source of truth. PhpCollectiveStrict is PhpCollective + 3 aggressive TypeHints.* sniffs. Diff the two files to see exactly what "strict" adds.
  • The README's PhpStorm external-tool block, if you want one-keystroke sniff/fix.

First use case: run it read-only (phpcs, never phpcbf) against a single leaf directory that already has decent types — typically app/Http/Controllers or a app/Services folder. Triage the output into three buckets: (1) auto-fixable, (2) genuine bugs the sniff caught, (3) conventions your team disagrees with → exclude those sniffs explicitly. Do this before letting the fixer anywhere near the codebase.

Implementation Patterns

Two-tier: lint gate + selective autofix. The pragmatic Laravel setup is to keep Pint as the only auto-formatter and use this package as a check-only gate for the semantic sniffs Pint doesn't cover (strict types, forbidden functions, docblock completeness, !== vs !=, exit, use-statement ordering). Two fixers rewriting the same lines creates an infinite "fix → reformat → fix" churn loop. Pick one fixer.

Composer scripts with the useful flags:

"scripts": {
    "cs-check": "phpcs -nps --parallel=8 --cache=.phpcs-cache",
    "cs-fix":   "phpcbf -nps --parallel=8 --no-pager",
    "cs-diff":  "phpcs --report=diff app/"
}

-n hides warnings (warnings are usually the noisy, half-configured sniffs), -p progress, -s sniff names. --cache is essential on a mid-size Laravel app; add .phpcs-cache to .gitignore.

Layered rulesets per directory. tests/ and database/migrations/ almost always need different rules than app/. Scope exclusions inside the rule rather than globally:

<rule ref="vendor/php-collective/code-sniffer/PhpCollective/ruleset.xml">
    <!-- anonymous-class migrations and factory closures fight these -->
    <exclude name="PhpCollective.Commenting.DocBlockParam"/>
    <exclude name="PhpCollective.PHP.DeclareStrictTypes"/>
</rule>

<rule ref="vendor/php-collective/code-sniffer/PhpCollective/ruleset.xml">
    <exclude-pattern>tests/*</exclude-pattern>
    <exclude-pattern>database/migrations/*</exclude-pattern>
</rule>

Ratchet, don't big-bang. PHPCS has no native baseline. Practical options, cheapest first:

  1. <exclude-pattern> per-legacy-directory, then delete one line per sprint.
  2. // phpcs:ignore Sniff.Name -- legacy, tracked in PROJ-123 on the specific line (must be on the violating line, or the line above for multi-line constructs).
  3. Generate a checkstyle report in CI and fail only on new violations via a diff against the base branch.

CI: GitHub Actions with staabm/phpcs-cs2pr turns the report into inline PR annotations:

- run: vendor/bin/phpcs --report=checkstyle --report-file=phpcs.xml.out || true
- run: phpcs-cs2pr < phpcs.xml.out

GitLab wants --report=checkstyle as a codequality artifact. In pre-commit hooks, PHPCS has no --staged mode — pass the staged filenames explicitly from git diff --name-only --cached --diff-filter=ACM -- '*.php'.

Upgrade to PhpCollectiveStrict only after PhpCollective is clean. The three added TypeHints.* sniffs demand native parameter/property/return types on every method, including legacy controllers and Eloquent relations — a genuine refactor, not a formatting pass. Treat it as a separate project.

Extension point: custom project sniffs go in e.g. tools/cs/Project/Sniffs/Forbidden/DdSniff.php with a local ruleset.xml alongside it; register installed_paths (or add dealerdirect/phpcodesniffer-composer-installer) so PHPCS finds it. This is the right place for Laravel-specific bans (dd, dump, env() outside config, DB::raw).

Gotchas and Tips

  • Treat phpcbf as a code-changing operation, not a formatter. 0.6.9 is a straight bug-fix release for three fixers that silently produced broken or wrong code in 0.6.8: PreferCastOverFunction turned first-class callables into parse errors (array_map(strval(...), $names) → array_map((string)(...), $names)), DocBlockParamAllowDefaultValue appended a bogus |array to narrowed array types (non-empty-array<...>, non-empty-list<...>, bare list), and DocBlockThrows wrote @throws \LocalException instead of @throws \App\LocalException for same-namespace classes. If you ran the fixer before 0.6.9, grep your diff for those three shapes — the first one leaves the file non-parseable, the other two leave subtly wrong docblocks that PHPStan/baseline diffing may not catch.
  • 0.6.9 changes findings, not just behaviour. DocBlockThrows now resolves qualified relative names — throw new Sub\NestedException() was previously skipped and is now reported. Expect new violations on untouched code after the bump even though you changed nothing. Re-run phpcs before assuming a config regression.
  • --config-set is a trap. phpcs --config-set installed_paths ... writes into vendor/squizlabs/php_codesniffer/CodeSniffer.conf, which is not committed and is wiped by composer install. In CI it will silently fall back to PEAR and report the wrong errors — or "standard not found". Always pass the standard via phpcs.xml or --standard= explicitly. If you want the shortcut, add dealerdirect/phpcodesniffer-composer-installer instead.
  • Version conflicts with an existing PHPCS. If another dev dependency pulls squizlabs/php_codesniffer ^3 and this package eventually wants ^4 (or vice versa), composer require fails or silently pins an old standard. Check composer why squizlabs/php_codesniffer before debugging phantom behaviour.
  • Pin the version and review upgrades as PRs. Sniff sets change between minors: a new sniff = new CI failures on untouched code. Lock "php-collective/code-sniffer": "x.y.*" and let Renovate/Dependabot open an isolated bump PR so you can see exactly which sniffs turned red. Note that patches matter too — 0.6.9 adds findings via the DocBlockThrows behaviour change — so if your repo is large, treat a patch bump as worth its own PR rather than letting it hitch a ride on an unrelated change.
  • Pint interaction. Both tools want to own quote style, concatenation spacing, array formatting and trailing commas. Running pint then phpcbf will ping-pong. Choose one writer. If you keep Pint, run this package with fixers disabled via phpcs only.
  • Trailing-comma sniffs are contradictory. The bundled standards include both RequireTrailingCommaInCall and DisallowTrailingCommaInCall families, plus PhpCollective.PHP.DisallowTrailingCommaInSingleLine. If you cherry-pick sniffs by hand you can configure an impossible standard; if you just ref the whole ruleset, don't add your own contradictory one on top.
  • Docblock vs. PHPStan generics. PhpCollective.Commenting.DisallowArrayTypeHintSyntax and DocBlockParamArray push Type[]; if your codebase is PHPStan level 8 with array<string, Foo> generics, these will fight. Exclude the array-syntax sniffs and keep generics — static analysis wins over docblock aesthetics. If you do keep narrowed forms (non-empty-array<...>, non-empty-list<...>, list<...>), be aware they were only recognised as valid array coverage from 0.6.9 onward — older fixer runs may have appended a redundant |array.
  • PhpCollective.PHP.DeclareStrictTypes touches every file. Preview with phpcbf --report=diff app/ before writing; on a large app, do app/Models first in its own commit so the diff is reviewable.
  • Testing sniffs assume plain PHPUnit. PhpCollective.Testing.* (AssertPrimitives, ExpectException, Mock) match PHPUnit-style assertions. Pest's expect()->toThrow() and global test() helpers can trip PhpCollective.Namespaces.FunctionNamespace. Exclude the Testing and Namespaces groups for tests/ if you're on Pest.
  • PhpCollective.PHP.Exit flags exit/die — including places where it's legitimate (console commands, queue workers, abort() alternatives). Exclude it deliberately or you'll get used to ignoring the whole report.
  • Configure the "banned function" sniffs. Generic.PHP.ForbiddenFunctions, PhpCollective.PHP.DisallowFunctions and Internal.DisallowFunctions ship with their own lists; dd/dump/var_dump are probably not all in there by default. Set them explicitly via <property name="forbiddenFunctions" type="array">.
  • First-class callable syntax (foo(...)) is everywhere in modern Laravel (array_map($this->normalise(...), $rows), Str::of(...)->pipe(...)). It is now safe from PreferCastOverFunction, but any other custom sniff you add that pattern-matches "function call followed by parentheses" is a good candidate for the same bug class — check it against a callable before trusting it.
  • Generic.Files.LineLength is a warning-level sniff with a default limit around 120 that rarely matches a team's actual wrapping policy. Either set lineLimit/absoluteLineLimit yourself or drop it and let Pint handle width — otherwise -n hides it and nobody notices.
  • Exit codes matter in CI: 0 clean, 1 violations, 2 fixer failed, 3 processing/config error. Failing the build on 3 with the same message as 1 sends people hunting for style problems when the real issue is a bad phpcs.xml.
  • Preview every fixer run. phpcbf --report=diff first, then write. Some fixers are semantically safe (intval() → (int), Yoda removal) but a few rewrite expressions (isset($x) ? $x : $y → ??) and — as 0.6.9 demonstrates — some can emit invalid PHP. Run on a clean worktree and read git diff — never phpcbf as part of a commit hook without review.
  • phpcbf may need two passes. Fixing one violation can create another (e.g. a docblock line now exceeds the length limit). Always follow phpcbf with phpcs in the same script and fail if it's still dirty.
  • Don't feed it generated code. _ide_helper.php, bootstrap/cache/*.php, compiled Blade views in storage/framework/views, and vendor/ will produce thousands of errors and dominate runtime; the exclude-pattern entries above are not optional.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator