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 Phar Laravel Package

jin2chen/phpcs-phar

A Composer-installable PHAR distribution of PHP_CodeSniffer (PHPCS), making it easy to run coding-standard checks in local development and CI without managing a separate PHPCS installation.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Composer wrapper around the phpcs/phpcbf PHAR — it does not change how PHP_CodeSniffer behaves, so the upstream docs and wiki (linked from the squizlabs repo) are your reference. Everything you learn about PHPCS applies verbatim.

1. Install (dev only):

composer require --dev jin2chen/phpcs-phar

2. Verify what you got — do this first, because the PHAR's PHPCS version is fixed at build time (~3.6/3.7, package last released 2023-02-22):

vendor/bin/phpcs --version
vendor/bin/phpcs -i          # lists bundled standards: PEAR, PSR1, PSR2, PSR12, Squiz, Zend, Generic, MySource
vendor/bin/phpcs --help | head -40

3. First use case — lint the app against bundled PSR-12 with zero config:

vendor/bin/phpcs --standard=PSR12 --extensions=php app/ routes/ config/

4. Immediately create a phpcs.xml.dist so you stop passing flags:

<?xml version="1.0"?>
<ruleset name="Laravel">
    <description>Project coding standard</description>

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

    <arg name="extensions" value="php"/>
    <arg name="colors"/>
    <arg name="parallel" value="8"/>
    <arg name="cache" value=".phpcs.cache"/>
    <arg name="basepath" value="."/>

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

    <rule ref="PSR12"/>
</ruleset>

5. Wire into composer.json scripts so it's a one-word command for the whole team:

"scripts": {
    "lint": "phpcs",
    "lint:fix": "phpcbf",
    "lint:report": "phpcs --report=summary -s"
}

Run composer lint. That's the whole onboarding. From there, the natural progression is: -s to see sniff codes → targeted // phpcs:ignore suppressions → external standards via installed_paths → CI gate.

Implementation Patterns

CI gate with GitHub Actions annotations. Use checkstyle output + cs2pr, which turns phpcs findings into inline PR comments:

- uses: shivammathur/setup-php@v2
  with: { php-version: '8.2', tools: composer, coverage: none }
- run: composer install --prefer-dist --no-interaction
- run: vendor/bin/phpcs -q --report=checkstyle --no-colors | cs2pr

Keep --no-colors in CI so logs stay parseable; keep colors locally.

Cache-aware pipelines. --cache writes .phpcs.cache. Cache it in CI keyed by the ruleset hash, or you'll spend more time restoring a stale cache than linting:

- uses: actions/cache@v4
  with:
    path: .phpcs.cache
    key: phpcs-${{ hashFiles('phpcs.xml.dist') }}

Locally, when results look wrong or missing, the first reflex is rm .phpcs.cache (or --no-cache) — a ruleset edit without a cache invalidation causes confusing "why is this still reported / not reported".

Pre-commit on changed files only. Full-repo PHPCS is slow on a large Laravel app; scope it to staged PHP files:

"lint:staged": "git diff --name-only --cached --diff-filter=ACMR -- '*.php' | xargs -r vendor/bin/phpcs -s"

Add || true guarding is not recommended for a gate — but note the exit codes (below) before writing wrapper scripts.

Coexistence with Laravel Pint. Pint wraps PHP-CS-Fixer; this package wraps PHP_CodeSniffer. They are different engines with overlapping rulesets and will fight if both auto-format in the same loop. Pick one of two clean splits:

  • Pint formats, PHPCS gates. Pint (composer pint) owns stylistic fixes; PHPCS runs only sniffs Pint cannot express — Generic.PHP.ForbiddenFunctions, SlevomatCodingStandard.Namespaces.*, PHPCompatibility, custom architecture sniffs — with PSR12 removed from your ruleset to avoid duplicate/conflicting messages.
  • PHPCS owns everything. Use phpcbf as the fixer and drop Pint. Then set your editor to run phpcbf on save, not both.

Never run phpcbf and pint in the same pre-commit hook; you get churn diffs as each undoes the other's spacing choices.

Add external standards without polluting the app. The PHAR only ships the built-in standards. Standard practice (Slevomat, PHPCompatibility, WordPress) is:

composer require --dev slevomat/coding-standard
<config name="installed_paths" value="vendor/slevomat/coding-standard"/>
<rule ref="SlevomatCodingStandard"/>

installed_paths set inside a ruleset is resolved relative to that ruleset file, so the path above is correct only because phpcs.xml.dist sits next to vendor/. This is the single most common "standard not installed" failure.

Progressive adoption / suppressing instead of rewrites. For a legacy Laravel app, don't start with PSR12 across app/. Start with one directory (app/Http/Controllers), then widen. For individual lines:

// phpcs:ignore SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint
public function __invoke($request) { ... }

// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found
function handler($event, $payload) { ... }
// phpcs:enable

Use -s on every run when authoring suppressions — you need the exact sniff code, and PHPCS does not tell you it by default.

Editor integration. Point PHP Intelephense / vscode-phpcs / PhpStorm's Code Sniffer configuration at vendor/bin/phpcs (the Composer bin proxy, not the .phar inside vendor/jin2chen/...). Set "standard" to the absolute path of phpcs.xml.dist so editors and CI agree. Because both binaries are shipped as PHAR-based proxies, no include_path or global PHPCS install is needed — this is exactly the isolation benefit: your app's composer.lock never gains squizlabs/php_codesniffer's dependency tree.

Use it as a diff review tool, not just a gate. vendor/bin/phpcs --report=source -s groups findings by sniff, which is the fastest way to decide which rules to disable project-wide versus fix. --report=summary gives per-file counts suitable for a "tech debt burndown" metric before you enable a rule for everyone.

Gotchas and Tips

Version staleness is the headline risk. The package was last released 2023-02-22, so the embedded PHPCS is roughly 3.7.x. Tokenizer support for newer PHP syntax (readonly classes, DNF types, 8.4 asymmetric visibility, hooks) is missing. Symptoms are not clear errors — they are phantom findings (indentation, spacing, "unexpected token") on files that are perfectly valid. Before blaming your code, run phpcs --version and check the token in question; add a targeted phpcs:ignore with a comment, or exclude the file.

Exit codes will break naive shell scripts. PHPCS returns 0 (clean), 1 (errors found), 2 (fixer failure), 3 (processing error — e.g. a parse error or missing standard). phpcbf is different again: 0 (nothing to fix) and 1 (fixes applied) are both effectively "success". So phpcbf || exit 1 is wrong — it fails precisely when it did its job. Check for > 1 instead.

Composer bin name collisions. If anything in your tree already pulls in squizlabs/php_codesniffer (a dev tool, a transitive dev dependency), both packages want vendor/bin/phpcs. Composer silently skips one and prints a "Skipped installation of bin" warning that is easy to miss — you then run a different PHPCS than you think. Grep composer install output for it, and composer why squizlabs/php_codesniffer if the version reported by phpcs --version doesn't match the package.

require-dev means "absent in production-shaped installs". Any CI job or Docker stage that runs composer install --no-dev has no vendor/bin/phpcs at all. Run lint in a dedicated job / build stage, or keep it in the dev stage image.

No built-in baseline. PHPCS has no --generate-baseline like PHPStan. You either suppress inline, exclude patterns, or adopt a community baseline tool. Plan for it — it's the main reason teams abandon a strict ruleset on legacy code.

phpcbf is not safe everywhere. It rewrites <?php blocks inside templates and can mangle heredocs/nowdocs and unusual string interpolation. Exclude Blade and mixed-HTML views entirely (<exclude-pattern>*resources/views/*</exclude-pattern>, *mail/*) — PHPCS cannot meaningfully lint Blade anyway. Always run git diff after a phpcbf pass and commit fixes separately from functional changes.

--parallel + custom sniffs. Parallel workers are separate processes; sniffs that write to files, mutate globals, or depend on run order can behave inconsistently. If results differ between --parallel=8 and --parallel=1, that's your culprit — disable parallel in the ruleset for that project.

Warning/error severity is a footgun. Many sniffs emit warnings, which still cause exit code 1 but don't fail builds if you've configured --warning-severity=0. Set severity deliberately in the ruleset rather than relying on default behavior, and remember --warning-severity=0 --error-severity=1 is the usual "only fail on errors" combination.

--runtime-set and <config> are different mechanisms. CLI --runtime-set installed_paths ... overrides the ruleset; useful in CI where paths differ. Forgetting which one applies leads to "works locally, external standard missing in Docker".

PHAR environments. Reading a PHAR does not require phar.readonly=0, but the phar extension must be present. Some hardened production/minimal Docker images and certain CI sandboxes disable it or restrict phar:// stream wrappers. Also note some security scanners flag vendored PHARs — expect an allowlist entry, and document why the dependency exists.

Path and file-discovery surprises. --basepath=. keeps report paths relative (nicer in CI annotations). exclude-pattern uses glob-like matching relative to the ruleset, so vendor/* will not exclude a nested packages/foo/vendor/* — use */vendor/*. Same for storage/framework/views.

Debugging checklist, in order: phpcs -i (is the standard there?) → --version (is it the PHAR or a stray system install?) → --no-cache (stale cache?) → --parallel=1 (process weirdness?) → -s (which sniff?) → run the sniff alone with --sniffs=Vendor.Standard.Sniff (isolate false positives) → confirm php -l passes (PHPCS reports parse errors as processing failures, not findings).

Extension point reminder. You can't add dependencies to the PHAR. If you need a sniff that requires a runtime library, it must be installed via Composer and referenced with installed_paths — and it will autoload with your Composer autoloader, not the PHAR's. Keep such sniffs' classes free of assumptions about PHP_CODESNIFFER_CBF/PHP_CODESNIFFER_VERBOSITY being defined by the PHAR's entry point.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo