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

spryker/code-sniffer

Spryker Code Sniffer is a PHPCS ruleset for PHP 8.3+ that follows PSR-2 and PSR-12 with extra Spryker convention sniffs. It ships Spryker, SprykerStrict, and opt-in SprykerConventions standards, and works via phpcs/phpcbf.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a PHP_CodeSniffer standard, not a Laravel package. There is no service provider, no config/code-sniffer.php, no Artisan command. It ships two rulesets (Spryker and the stricter SprykerStrict) plus ~80 custom sniffs on top of PSR-2/PSR-12 and slevomat/coding-standard.

composer require --dev spryker/code-sniffer

That pulls in squizlabs/php_codesniffer and slevomat/coding-standard as transitive deps. You call the binaries directly — the console code:sniff:style shortcut mentioned in the README belongs to Spryker's Development module and does not exist in a Laravel app.

First run — sniff only, show sniff codes (-s), summary report:

vendor/bin/phpcs --standard=vendor/spryker/code-sniffer/Spryker/ruleset.xml app/ -s --report=summary

Where to look first, in this order:

  1. vendor/spryker/code-sniffer/Spryker/ruleset.xml — the actual truth. Read it before trusting the README.
  2. docs/sniffs.md — generated list of included sniffs, one line each.
  3. docs/README.md — the parts that matter for a non-Spryker codebase: silencing, phpVersion, .license, test_files skipping.
  4. SprykerStrict/ruleset.xml — diff it against Spryker/ to see what "strict" adds.

First realistic use case: do not try to adopt the whole standard on an existing Laravel codebase. A typical Laravel app will emit hundreds of violations from the file-doc-block, method-annotation and namespace sniffs that were written for Spryker's module layout. Instead, use it as a second-opinion audit on a single new module (app/Modules/Billing) or as a curated rule source you cherry-pick into your own phpcs.xml. Keep Laravel Pint for formatting; use this for the structural/semantic sniffs Pint can't express.

Implementation Patterns

1. Own ruleset that inherits and prunes

Never point CI at the vendor ruleset directly — you'll be at the mercy of upstream changes on composer update, and you can't exclude the Spryker-specific noise.

<?xml version="1.0"?>
<ruleset name="AppStandard">
    <description>Laravel app standard, based on Spryker + Slevomat.</description>

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

    <rule ref="vendor/spryker/code-sniffer/Spryker/ruleset.xml">
        <!-- Spryker DI/facade docblock sniffs: meaningless outside Spryker modules -->
        <exclude name="Spryker.MethodAnnotation.ConfigMethodAnnotation"/>
        <exclude name="Spryker.MethodAnnotation.EntityManagerMethodAnnotation"/>
        <exclude name="Spryker.MethodAnnotation.FacadeMethodAnnotation"/>
        <exclude name="Spryker.MethodAnnotation.FactoryMethodAnnotation"/>
        <exclude name="Spryker.MethodAnnotation.QueryContainerMethodAnnotation"/>
        <exclude name="Spryker.MethodAnnotation.RepositoryMethodAnnotation"/>
        <exclude name="Spryker.Namespaces.SprykerNamespace"/>
        <exclude name="Spryker.Internal.SprykerDisallowFunctions"/>
    </rule>

    <exclude-pattern>*/bootstrap/cache/*</exclude-pattern>
    <exclude-pattern>*/storage/*</exclude-pattern>
    <exclude-pattern>*/public/build/*</exclude-pattern>
    <exclude-pattern>*/tests/fixtures/*</exclude-pattern>
</ruleset>

Then vendor/bin/phpcs with no --standard picks up phpcs.xml from the CWD.

2. Composer scripts, split sniff vs fix

"scripts": {
    "cs": "phpcs -q --report=summary --parallel=4",
    "cs:full": "phpcs -s --report=full",
    "cs:fix": "phpcbf --parallel=4 || true",
    "cs:changed": "phpcs -s --file-list=<(git diff --name-only --diff-filter=ACM origin/main...HEAD -- '*.php')"
}

cs:fix is deliberately || true — phpcbf exits non-zero when unfixable violations remain, which will otherwise break composer run chains. --parallel makes the difference on a large app/.

3. CI: gate on new code, report on legacy

Two jobs. The blocking one only checks files touched in the MR; the advisory one produces a source report so you can see which sniffs dominate. Use --report=source before deciding what to silence — it aggregates by sniff, which is far more actionable than a 4,000-line wall of errors.

# blocking
git diff --name-only --diff-filter=ACM "$BASE"...HEAD -- '*.php' \
  | xargs -r vendor/bin/phpcs -q --standard=phpcs.xml -s

The upstream migration guidance (lock to ~ patch releases, never bump the sniffer in a feature branch, land standard updates on a dedicated master-cs branch and merge forward) applies verbatim to a Laravel team with long-lived release branches. It's the single most useful thing in the docs.

4. PhpStorm external tools

Identical to the README, just swap the standard for your own ruleset:

  • Program: $ProjectFileDir$/vendor/bin/phpcs, Parameters: --standard=$ProjectFileDir$/phpcs.xml -p $FilePath$, Working dir: $ProjectFileDir$
  • Program: $ProjectFileDir$/vendor/bin/phpcbf, Parameters: --standard=$ProjectFileDir$/phpcs.xml -v $FilePath$

Bind to Ctrl+, / Ctrl+.. Prefer the single-file invocation over file watchers — file watchers running phpcbf on save will fight Pint on every keystroke.

5. Coexistence with Pint

Both tools rewrite the same bytes. Pick an order and stick to it:

  • Pint is the formatter of record (spacing, imports, ordering) — run it in the pre-commit hook.
  • phpcs runs in CI only, as an audit. If you must run phpcbf, exclude every sniff in the SlevomatCodingStandard.Whitespaces / SlevomatCodingStandard.Arrays families, or you get a reformat ping-pong.

6. Incremental adoption

Silence a whole sniff, or one code within a sniff, without touching the source:

<!-- full silence -->
<rule ref="SlevomatCodingStandard.ControlStructures.ControlStructureSpacing">
    <severity>0</severity>
</rule>

<!-- silence a single code (x.y.z.code) -->
<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName">
    <severity>0</severity>
</rule>

The three-part form silences an entire sniff; the four/five-part form silences one violation code. Getting this wrong silently does nothing — always confirm the exact code with -s.

7. Custom sniffs for house rules

If you write your own (e.g. "no env() outside config/"), drop them in ./Sniffs/<Category>/ next to your ruleset, add <rule ref="./Sniffs"/>, and mirror the structure under ./tests/ — the base library auto-discovers both. Scaffold with:

php vendor/spryker/code-sniffer/tests/generate.php App.MyCategory.NoEnvOutsideConfig

bin/tokenize.php (in the package root) dumps the token stream for a file, which is the fastest way to figure out why a sniff isn't matching.

Gotchas and Tips

Most of the standard silently skips your code. Spryker.Namespaces.SprykerNamespace and all the MethodAnnotation sniffs only fire for namespaces in Pyz,SprykerEco,SprykerMiddleware,SprykerSdk,Spryker. On App\... code they pass vacuously. A green build does not mean "the Spryker standard passed" — it means "the subset that applies to Laravel passed." Exclude them explicitly so your report is honest, or reconfigure:

<rule ref="Spryker.Namespaces.SprykerNamespace">
    <properties>
        <property name="namespace" value="App"/>
        <property name="isRoot" value="true"/>
    </properties>
</rule>

rootDir is the escape hatch when src/ isn't your root — for Laravel that means app.

The shipped ruleset pre-excludes PHP 8 features. DisallowNamedArguments, DisallowTrailingCommaInDeclaration, DisallowConstructorPropertyPromotion and DisallowNullSafeObjectOperator are excluded upstream to avoid PHP 7.4 breakage. Because they're excluded via <exclude> inside the referenced ruleset, a plain <rule ref=".../Spryker/ruleset.xml"/> won't re-enable them. On a PHP 8.2+ Laravel app you'll want to re-add them by name:

<rule ref="SlevomatCodingStandard.Functions.DisallowNamedArguments"/>

Same story for Spryker.PHP.DeclareStrictTypesAfterFileDoc — strictTypesMandatory defaults off. Turning it on changes runtime semantics (no more implicit casts), so don't flip it in the same PR as anything else.

.license is a trap. Creating an empty .license at the repo root means "no license doc block is expected"; putting content there means every PHP file must match it byte-for-byte including the trailing newline. Add this file on a branch where you're prepared to accept either zero diff or a repo-wide diff — there is no middle ground, and the failure mode is thousands of identical file-comment errors that look like a broken sniffer.

config-set writes to the shared PHPCS config, not your project. vendor/bin/phpcs --config-set installed_paths ... mutates a config that lives with the binary — fine in CI (fresh composer install), confusing locally if you also have a global phpcs. Prefer explicit --standard=phpcs.xml and skip config-set entirely.

phpcbf needs multiple passes. Fixers conflict; a single run leaves residual violations and a non-zero exit. Loop until clean, and preview with --report=diff first:

phpcbf --report=diff app/ | head -200
while vendor/bin/phpcbf -q app/ >/dev/null 2>&1; do :; done

Stale cache after version bumps. If you pass --cache, delete the cache file whenever you change phpcs.xml or bump the sniffer — the cache key doesn't account for the standard's content, and you'll chase phantom "fixed" violations. --parallel and --cache are also mutually temperamental on old PHPCS versions.

phpVersion for the disallow-functions sniff is a separate knob from the platform requirement:

<rule ref="Spryker.Internal.SprykerDisallowFunctions">
    <properties><property name="phpVersion" value="8.2"/></properties>
</rule>

Set it to off if you already depend on polyfills or shims that the sniff doesn't know about — otherwise it flags functions you've legitimately backfilled.

test_files is a magic folder name. Anything under a directory called test_files is auto-skipped. If you have fixture PHP that should be linted, don't name the directory test_files; conversely, renaming an existing fixtures dir to it is a free way to kill a whole category of noise.

Debugging sequence that actually works: -s to get codes → --report=source to rank by volume → silence the top three → re-run. Do not start with --report=full on a legacy app.

Adoption risk. 38 stars, and dependents effectively zero outside Spryker's own suite; the package is alive (releases into 2026, PHPStan level 8, CI badge) but its maintenance signal is "the vendor needs it," not "the community needs it." Pin it ("spryker/code-sniffer": "~1.2" or an exact version) and keep your real policy in your phpcs.xml. That way an upstream <exclude> or a new sniff can never turn a green branch red overnight.

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.
frankkessler/guzzle-oauth2-middleware
delatbabel/site-config
delatbabel/applog
eusonlito/laravel-gettext
henzeb/housepedia-intelligence-laravel
aliqsm/laravel-authkit
itsjustvita/laravel-wp-migrator
uslanozan/laravel-cache-contract
adilazhari/laravel-trace
ulsyairil/ai-local-guardrails
dzwallet/laravel-sdk
glc36/maxloop-kernel
ikhbarnuridev/filament-mega-menu
mydnic/volet-chatbot
enadstack/laravel-localization
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder