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

Phpcs Rules Laravel Package

pekral/phpcs-rules

pekral/phpcs-rules provides custom PHP_CodeSniffer rules and coding-standard configuration for PHP projects, helping teams enforce consistent style, catch common issues, and integrate automated checks into development and CI workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

pekral/phpcs-rules is a shared coding-standard package: you add it as a dev dependency and ref its ruleset from your project's phpcs.xml, so every project in your fleet flags the same things. It pulls in slevomat/coding-standard (the release notes' sniff names are Slevomat's) plus whatever else the ruleset enables.

1. Check your PHP version first. 0.5.0 requires php: ^8.5. Composer will simply refuse to install it on 8.4:

composer require --dev pekral/phpcs-rules:^0.5   # PHP >= 8.5 only
composer require --dev pekral/phpcs-rules:^0.4   # stay here on PHP 8.4

Pin the constraint deliberately (^0.4 / ^0.5) rather than *, because the ruleset itself changes behaviour between minors — see Gotchas.

2. Wire it into a phpcs.xml at the project root. Look at vendor/pekral/phpcs-rules/ first — open the shipped ruleset.xml and note its <ruleset name="..."> value, then reference either that name or the file path:

<?xml version="1.0"?>
<ruleset name="Project">
    <description>Project standard, layered on pekral/phpcs-rules.</description>

    <file>app</file>
    <file>config</file>
    <file>database</file>
    <file>routes</file>
    <file>tests</file>

    <rule ref="vendor/pekral/phpcs-rules/ruleset.xml"/>
</ruleset>

3. First run / first use case — triage an existing Laravel app:

vendor/bin/phpcs -i                    # confirm standards are registered
vendor/bin/phpcs --report=source       # which sniffs are noisy, ranked
vendor/bin/phpcs --report=summary      # per-file counts
vendor/bin/phpcbf                      # auto-fix the mechanical stuff

--report=source is the single most useful first command: it tells you whether you're looking at 40 fixable whitespace nits or a handful of opinionated sniffs worth discussing with the team before you reformat the whole repo. Commit the phpcbf output as a standalone "style only" commit so review stays sane.

Where to look first: vendor/pekral/phpcs-rules/ruleset.xml (what's actually enabled), the package composer.json (the php constraint and the slevomat/coding-standard version it pins), and the changelog for ruleset-level changes — those are behavioural even when the version bump is minor.

Implementation Patterns

Composer scripts as the single entry point. Don't let people memorise flags:

{
  "scripts": {
    "lint": "phpcs",
    "lint:fix": "phpcbf",
    "check": ["@lint", "@phpstan", "@test"]
  }
}

phpcs with no arguments picks up phpcs.xml automatically, so composer lint is the whole interface. In CI, composer check mirrors what a developer runs locally.

Layered ruleset, not a fork. Treat the package as the base layer and add project-specific deltas after the ref. Per-directory overrides beat global exclusions:

<rule ref="vendor/pekral/phpcs-rules/ruleset.xml"/>

<!-- generated / third-party -->
<exclude-pattern>*/bootstrap/cache/*</exclude-pattern>
<exclude-pattern>*/_ide_helper*.php</exclude-pattern>
<exclude-pattern>*/storage/*</exclude-pattern>

<!-- relax one rule for tests only -->
<rule ref="SlevomatCodingStandard.Functions.StaticClosure">
    <exclude-pattern>tests/*</exclude-pattern>
</rule>

<!-- relax one rule for migrations only -->
<rule ref="Generic.Files.LineLength">
    <exclude-pattern>database/migrations/*</exclude-pattern>
</rule>

Get sniff codes with vendor/bin/phpcs -s (shows codes on failures) or vendor/bin/phpcs -e (lists everything in the standard). Never guess a sniff name.

Incremental adoption on a legacy codebase. PHPCS has no native baseline. Two workable patterns:

  • Path ratchet — add directories to <file> one at a time (app/Http/Controllers, then app/Models, …) and let the rest stay unlinted.
  • Generated exclude list — run phpcs --report=json, extract the source keys, and emit <exclude name="..."> entries; commit the generated file and shrink it over time.

Pre-commit / on-save. Run PHPCS only on staged files, and run phpcbf on save for the safe sniffs only — full phpcbf can rewrite more than you expect:

vendor/bin/phpcbf --sniffs=Generic.WhiteSpace.DisallowTabIndent,Generic.PHP.UpperCaseConstant,SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses <files>

In your editor set the standard to phpcs.xml and the executable to vendor/bin/phpcs (never a global install — version drift causes phantom diffs).

CI job. A single job on PHP 8.5 with --parallel and the cache, plus human-readable annotation output:

- uses: shivammathur/setup-php@v2
  with: { php-version: '8.5', coverage: none }
- run: composer install --prefer-dist --no-interaction
- run: vendor/bin/phpcs --parallel=4 --cache --report=checkstyle --report-file=phpcs.xml.out
- if: always()
  run: vendor/bin/cs2pr < phpcs.xml.out

If your application still supports 8.4 at runtime but your dev toolchain is 8.5, keep tests on a matrix and run phpcs in the 8.5 leg only — or set config.platform.php to 8.5.0 so Composer resolves the dev dependency while your runtime constraint stays ^8.4. That works because PHPCS parses code rather than executing it, but treat it as an escape hatch: the package's own source must still parse on the interpreter that loads it.

Keep the fleet aligned. If the same package also ships PHPStan/Rector config, ref those from your phpstan.neon/rector.php rather than hand-maintaining duplicates. One upgrade then moves lint, static analysis and refactoring rules together — which is the whole point of a shared ruleset.

Upgrade ritual. Because ruleset edits ship as minor versions:

composer update pekral/phpcs-rules --with-dependencies
vendor/bin/phpcs --report=summary > /tmp/after.txt   # compare against before.txt
vendor/bin/phpcbf

Do the update in its own PR, isolated from feature work.

Gotchas and Tips

Ruleset changes are silent breaking changes. 0.5.0 re-enables SlevomatCodingStandard.Functions.StaticClosure outside tests. Nothing in the version number screams at you, but code that was clean on 0.4.8 will fail after the bump. Always diff lint counts across a ruleset upgrade before merging anything else.

Static closures can't touch $this. That's the real cost of re-enabled StaticClosure: a closure inside a class that reads $this (or self::, or a property) must now be refactored to take its dependencies via use (...)/parameters, or the sniff has to be excluded for that file or directory:

<rule ref="SlevomatCodingStandard.Functions.StaticClosure">
    <exclude-pattern>app/Support/Legacy/*</exclude-pattern>
</rule>

Don't sprinkle // phpcs:ignore — it doesn't stick to closures reliably and hides future violations. Prefer a per-file // phpcs:disable / // phpcs:enable pair around the specific block.

"Outside tests" is path-based. The exemption depends on how the ruleset identifies test paths. If your specs live somewhere unconventional (spec/, Modules/*/Tests, Pest files beside app/), the sniff may still fire there. Verify with vendor/bin/phpcs tests/SomeTest.php -s and add your own exclude-pattern if needed.

ReadonlyClass is now excluded. That means classes no longer get flagged for missing readonly. If your team liked enforcing it, re-add it explicitly:

<rule ref="SlevomatCodingStandard.Classes.ReadonlyClass"/>

Conversely, if you were fighting it, you can now drop readonly from classes where immutability never fit — the standard won't complain either way.

PHP 8.5 is a hard floor, not a suggestion. On 8.4 you get a Composer platform error, not a warning. Pin ^0.4 explicitly — a bare pekral/phpcs-rules constraint with platform.php: 8.5.0 in config will happily resolve 0.5.x and then the tool won't run on your 8.4 CI image.

The upstream CI change has a blast radius. 0.5.0 collapsed the 8.4/8.5 matrix into a single 8.5 worker. If you're on the 0.4.x line you're on a branch that is now tested against 8.5 only in the sense that it isn't — nothing is validated for that combination going forward. Plan the PHP upgrade rather than sitting on 0.4 long-term.

Warnings fail the build. PHPCS exits non-zero on warnings by default. If you only want errors to gate CI, --runtime-set ignore_warnings_on_exit 1 — but do that consciously, or you'll ship a ruleset whose warnings nobody ever reads.

phpcbf is not safe to run blind. Some fixers reorder use statements and rewrite constant case across files; running it over a dirty working tree makes review impossible. Run it on a clean tree, commit alone, and scope it with --sniffs on save-hooks.

Slevomat version conflicts. If you also require slevomat/coding-standard directly at a different constraint, Composer will fight the package's pin. Let the ruleset own the dependency unless you have a specific reason; if you must override, align to the version the package declares and expect to re-check on every ruleset bump.

Standards not registering. vendor/bin/phpcs -i should list the ruleset. If it doesn't, the dealerdirect/phpcodesniffer-composer-installer plugin is probably blocked by allow-plugins — allow it, or fall back to referencing the ruleset.xml path directly (which is the more robust option anyway).

Speed on big codebases. --parallel=$(nproc) and --cache (with the cache directory keyed on composer.lock in CI) turn a multi-minute lint into seconds. Watch memory with high parallelism; --parallel=4 is usually the sweet spot.

Debugging a confusing failure. vendor/bin/phpcs -s <file> prints the sniff code, -vv explains why a file was skipped, and --report=source shows which single sniff is responsible for most of your violation count. Nine times out of ten, one sniff plus one exclude-pattern is the whole fix.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core