tenantcloud/php-cs-fixer-rule-sets
This package is a shared PHP-CS-Fixer rule set, so its job is to replace the hand-maintained rules array in your .php-cs-fixer.dist.php with a versioned, reusable one. That makes it most useful as a dev dependency in Laravel/PHP projects where you want formatting to be consistent across repos without copying config around.
Minimal setup:
composer require --dev tenantcloud/php-cs-fixer-rule-sets
Confirm what PHP-CS-Fixer version constraint it pulls in — shared rule sets are tightly coupled to fixer majors, so check composer show tenantcloud/php-cs-fixer-rule-sets and composer show friendsofphp/php-cs-fixer before assuming anything.
Then look first at vendor/tenantcloud/php-cs-fixer-rule-sets/src/ and the README. The entry point is normally a rule-set class under the TenantCloud\PhpCsFixerRuleSets namespace that exposes a getRules(): array (either implementing PhpCsFixer\RuleSet\RuleSetInterface or a static factory). Confirm the exact FQCN there rather than guessing, then wire it into a config:
<?php
// .php-cs-fixer.dist.php
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
use TenantCloud\PhpCsFixerRuleSets\RuleSet; // verify FQCN in vendor/
$finder = Finder::create()
->in(__DIR__ . '/app')
->in(__DIR__ . '/tests')
->exclude(['vendor', 'storage', 'bootstrap/cache'])
->name('*.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return (new Config())
->setFinder($finder)
->setRiskyAllowed(true)
->setRules(RuleSet::getRules()); // or (new RuleSet())->getRules()
First use case: run it in dry-run/diff mode on a single directory before touching anything.
vendor/bin/php-cs-fixer fix app --dry-run --diff --using-cache=no
Read the diff. A company rule set will impose opinions (import ordering, alignment, trailing commas, declare(strict_types=1) insertion, etc.) that may not match your existing code. The diff is the real documentation — it tells you exactly what the package does to your codebase faster than reading the rule names.
Use it as the base, override locally. Don't fork the rule set to disagree with one rule. Merge and override in your config:
$rules = RuleSet::getRules();
$rules = array_merge($rules, [
'declare_strict_types' => false, // legacy code can't take it
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
]);
return (new Config())->setRules($rules)->setFinder($finder);
Later array keys win, so this is the clean extension point.
Two configs for legacy vs. new code. The most practical pattern in older Laravel apps: keep the full TenantCloud set for app/, tests/, and new modules, and a much narrower set for database/migrations or anything vendored-in. Either run two fixer invocations with different config files, or scope with separate Finder instances per config and run both in CI.
Composer scripts so nobody remembers the flags.
"scripts": {
"lint:fix": "php-cs-fixer fix",
"lint:check": "php-cs-fixer fix --dry-run --diff --using-cache=no"
}
lint:check is the CI gate; lint:fix is the developer convenience. Keeping --using-cache=no in CI avoids stale-cache false greens.
CI as a diff gate, not a blocker on the whole repo. Run --dry-run --diff and fail the job, but print the diff in the log so the fix is a copy-paste. Optionally emit --format=gitlab / --format=github if your CI understands it.
Editor integration. Since the rules live in .php-cs-fixer.dist.php, point PHPStorm's “PHP CS Fixer” file watcher or the php-cs-fixer extension at that file so formatting happens on save. This is where the package earns its keep day-to-day: developers stop thinking about formatting entirely.
Git hook via CaptainHook or a pre-commit script running php-cs-fixer fix --dirty (fixer 3.x --dirty only formats changed files). That keeps commits clean without a full-repo sweep on every save.
Pin the fixer version in composer.json alongside the rule set, e.g. "friendsofphp/php-cs-fixer": "^3.64". The rule set's rules are only valid for the major it targets; an unpinned fixer update is the most common way this setup breaks silently in CI.
The package is effectively unvetted. Zero stars and zero dependents means no community pressure-testing: rule combinations may be internally redundant, deprecated, or removed in the fixer version you install. Before adopting, run php-cs-fixer fix --dry-run -vvv once and check for Rule ... is not defined / deprecation warnings. Treat the installed fixer version as part of the package's API.
Rule-set classes don't magically apply risky rules. setRiskyAllowed(true) is required for anything in the risky tier (declare_strict_types, strict_comparison, void_return, etc.). If the diff looks suspiciously small, that's usually why.
array_merge vs. +. Use array_merge for rule overrides — with +, your local key is ignored if the base set already defines it. This is the single most common “why isn't my override applying?” bug.
Finder is where formatting leaks in. Default Finder::create()->in(__DIR__) will walk vendor/, node_modules, and storage/. Always exclude explicitly, and prefer listing directories (app, tests, routes, config) over the repo root.
Cache invalidation. .php-cs-fixer.cache is keyed partly on config; if you change the rule set but CI still passes, delete the cache or run --using-cache=no. Add .php-cs-fixer.cache to .gitignore either way.
Formatting a large legacy repo in one commit is a review disaster. Do it incrementally — per-directory PRs, or add the dry-run check to CI only for newly touched files using --dirty locally and a path-scoped fix in CI. A 10k-line whitespace diff will get rubber-stamped and hide real changes.
Conflicting rules fail loudly on some fixer versions, silently on others. If you see a fixer oscillation (rule A rewrites, rule B rewrites back), that's usually two rules in the set fighting; isolate with php-cs-fixer fix path/To/File.php -vvv to see which fixers ran. Disable one locally and report upstream.
--diff output is the fastest way to audit what you're adopting. Run it against a representative module before rolling out repo-wide; if the rule set assumes strict types, final classes, or alphabetic imports that your team doesn't use, decide that up front rather than after the mega-commit.
Extending the package. Because it's a thin wrapper, the cleanest “extension point” is composition: keep TenantCloud's rules as the base array and layer your own in the project config. Forking the package for one rule is a maintenance trap given its release cadence.
Check the release cadence before depending on it in a monorepo. Last release 2025-08-07 is recent, but with no dependents there's no signal about how promptly it tracks PHP-CS-Fixer majors. If your org upgrades fixer ahead of the package, you'll be pinned on an older fixer — budget for that, or plan to vendor the rule array into your own config if the package stalls.
How can I help you explore Laravel packages today?