lin3s/php-cs-fixer-config
Provides a reusable PHP-CS-Fixer configuration from Lin3s for enforcing consistent coding standards across PHP projects and packages. Add it as a development dependency and extend the preset to apply shared formatting and style rules with minimal setup, keeping codebases uniform and easier to mainta...
Check version compatibility before anything else. The last release was November 2017, which puts it in the PHP CS Fixer 2.x era. As of v1.0.2 the only change of note is a ruleset tweak (see Gotchas), so treat the package as stable-but-stale. Inspect the constraint it pulls in:
composer require --dev lin3s/php-cs-fixer-config
composer show lin3s/php-cs-fixer-config
# look at its composer.json "require" for friendsofphp/php-cs-fixer
If it pins ^2.x, install that explicitly in your project too, or you will get a config class that no longer satisfies PhpCsFixer\ConfigInterface in 3.x.
Find the entry point. Look at vendor/lin3s/php-cs-fixer-config/src/ to locate the config class (typically a Config implementation — verify the exact namespace rather than assuming). This is your one integration point.
Wire it up. Create .php_cs.dist in the project root (PHP CS Fixer 2.x config filename):
<?php
$config = new \LIN3S\CS\Config\Lin3sConfig();
$config->getFinder()
->in(__DIR__ . '/src')
->exclude(['vendor', 'var', 'tests/fixtures']);
return $config;
First use case — dry run on a real project. Never run fix first on an existing codebase:
vendor/bin/php-cs-fixer fix --dry-run --diff --using-cache=no
Review the diff, then commit a single "apply house style" commit, then run fix normally from that point on.
Pin the exact version and record the effective ruleset. Because rules live in PHP code, the only way to know what changed between v1.0.1 and v1.0.2 is to dump it:
composer show lin3s/php-cs-fixer-config # confirm 1.0.2
vendor/bin/php-cs-fixer fix --dry-run --rules=@LIN3S -v > rules-after.txt
Diff that against what you had before the bump and commit it alongside the dependency change.
Add Composer scripts so the team has one canonical entry point:
{
"scripts": {
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix"
}
}
Committed vs local config. Commit .php_cs.dist (the shared LIN3S ruleset + project paths). Let each developer add a gitignored .php_cs for local overrides — PHP CS Fixer prefers it over the .dist automatically.
Extending the ruleset per project. The config object is mutable, so merge rather than fork the package:
$config = new \LIN3S\CS\Config\Lin3sConfig();
$baseRules = $config->getRules();
$config->setRules(array_merge($baseRules, [
'declare_strict_types' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
]));
Merge order matters now more than ever: v1.0.2 removed self_accessor from the base rules. If your codebase relies on self:: being rewritten to static:: (or you want it back for consistency), re-add it explicitly after array_merge — a array_merge($baseRules, ...) call that doesn't mention it will silently inherit the removal.
Ruleset pinning across upgrades. Since a patch release can change formatting behavior, lock the version in composer.json ("lin3s/php-cs-fixer-config": "1.0.2" rather than ^1.0) until you've diffed the dumped rules. A rules change combined with a cached result is the classic "CI suddenly fails on untouched files" scenario.
CI as the source of truth. Add a job that only checks, never fixes, and fail the build on non-zero exit:
- run: composer install --no-interaction
- run: vendor/bin/php-cs-fixer fix --dry-run --diff --using-cache=no
--using-cache=no is important in CI — a stale cached result is the most common cause of "it passes locally but fails in CI."
Incremental adoption. On legacy code, don't fix the world. Scope the finder to new modules, or use --path-mode=intersection to check only files in the PR:
vendor/bin/php-cs-fixer fix --dry-run --diff --path-mode=intersection $(git diff --name-only origin/main... | grep '\.php$')
Pre-commit hook. Run the fixer against staged files and re-git add them, so diffs never reach review unformatted. Keep the hook fast by passing only changed paths.
Editor integration. Point PhpStorm's File Watcher (or the VS Code PHP CS Fixer extension) at vendor/bin/php-cs-fixer fix --quiet on save. Pair this with an explicit ruleset so the editor and CI agree — they will not agree if the editor falls back to its own defaults.
Dump the effective rules. Because the ruleset lives inside a class, the rules aren't visible in your repo. Materialize them for code review and documentation:
vendor/bin/php-cs-fixer fix --dry-run --rules=@LIN3S -v
v1.0.2 dropped self_accessor. That fixer rewrote self to static inside classes to enable late static binding. Consequences: (a) files already formatted under 1.0.1 keep their static:: calls — the fixer is not reversible, so this is a one-way change; (b) if you had 'self_accessor' => true implicitly via the base rules and your team expects that churn, you must now declare it yourself. Conversely, if the rewrite was unwanted, 1.0.2 is a quiet win — but your git history already contains the noise. Decide explicitly and document it in a comment next to your setRules() call.
A patch release changed formatting output. Treat any 1.x bump of this package as a potential CI-breaking change. Bump, dump the rules, diff, delete the cache, then re-run — in that order.
The package is archived-in-all-but-name. Zero stars, zero dependents, last release 2017 (v1.0.2 is a cosmetic bump). Treat it as a vendored ruleset, not a live dependency. The most durable move is to copy the rules array into your own .php-cs-fixer.php and drop the dependency — otherwise a PHP or Fixer upgrade will strand you.
PHP 8 install failures. A 2017 composer.json often carries a stale platform/php constraint. You may need --ignore-platform-reqs to install locally, which then risks a config class that crashes at runtime. Test the dry run immediately after install.
@PSR2 → @PSR12. Rules renamed and removed between 2.x and 3.x (@PSR2 deprecated, several fixers split or renamed). Expect "unknown rule" errors if the package and the fixer binary are on different majors. Pin both in composer.json.
Config filename moved. .php_cs → .php-cs-fixer.php in 3.x. If your binary is 3.x and the docs say .php_cs, the fixer silently ignores your config and runs with defaults — a nasty "why is nothing being fixed" bug.
getFinder() side effects. If the config class lazily constructs the Finder, calling getFinder() and mutating it is fine, but setFinder() (replacing it) is safer when you need full control. If your in() paths disappear, log $config->getFinder() and inspect.
Cache drift. .php_cs.cache (or .php-cs-fixer.cache in 3.x) should be gitignored, and deleted when you change rules. Rule changes aren't always part of the cache key — rm -f .php_cs.cache before re-running after a ruleset edit, and definitely after the 1.0.2 bump.
Generated and vendored code. Always exclude(['vendor', 'var', 'node_modules']) and any generated migrations/proxies. Fixing generated files produces diffs that get regenerated away and creates churn in review.
Diagnose individual rules rather than guessing why a line changed:
vendor/bin/php-cs-fixer describe self_accessor
vendor/bin/php-cs-fixer fix --dry-run --diff -vvv
describe still works even for a fixer the ruleset no longer enables — useful for confirming what 1.0.2 took away before you re-add it.
Autoloading the config class. The config file is required by the vendor/bin/php-cs-fixer binary, so Composer's autoloader is available — but only if the package was installed with --dev and composer dump-autoload has run since. A "class not found" in CI is almost always a composer install --no-dev somewhere up the pipeline.
Single-responsibility split. Use this for formatting only; do not let it double as static analysis. Pair with PHPStan/Psalm for correctness — the fixer will happily reformat code that is still broken.
How can I help you explore Laravel packages today?