kubawerlos/php-cs-fixer-custom-fixers
A collection of custom fixers for PHP CS Fixer that adds extra coding-standard rules beyond the built-in set. Install via Composer, register the custom fixers, and enable rules for cleaner, more consistent PHP code.
Architecture fit
This package is developer tooling, not application architecture. It registers additional rules into PHP CS Fixer and therefore has zero runtime footprint in a Laravel application — no service provider, no config publishing, no autoload cost in production (require-dev only). It slots into the existing code-quality tier alongside Pint, PHPStan/Larastan, and Rector. The correct framing for prioritisation is "DX / consistency improvement," not "platform capability," so it should be evaluated and funded out of the tooling backlog rather than competing with feature work.
The real value is concentrated in a subset of fixers (e.g. NoCommentedOutCodeFixer, CommentedOutFunctionFixer, ClassConstantUsageFixer, NoUselessCommentFixer, NoSuperfluousConcatenationFixer, NoUselessParenthesisFixer) rather than the full catalogue of ~50. Roughly seven fixers are already marked deprecated in favour of native PHP CS Fixer rules (DataProviderNameFixer, DataProviderReturnTypeFixer, DataProviderStaticFixer, InternalClassCasingFixer, MultilinePromotedPropertiesFixer, NumericLiteralSeparatorFixer, PhpDocPropertySorterFixer), which tells us the package is partly a staging ground that upstreams into PHP CS Fixer. That is a good signal for quality and a bad signal for long-term dependency surface.
Integration feasibility
Mechanically trivial: composer require --dev, then in .php-cs-fixer.dist.php call ->registerCustomFixers(new PhpCsFixerCustomFixers\Fixers()) and enable rules via Fixer::name(). Two Laravel-specific frictions matter:
pint.json does not expose custom-fixer registration — rules can only reference PHP CS Fixer's built-in rule set. In practice this means either running a second, raw php-cs-fixer invocation in CI alongside Pint, or replacing Pint with a hand-rolled PHP CS Fixer config. This must be verified against the current Pint version before committing, because it drives the whole integration shape.php-cs-fixer/shim (common in PHAR-style CI images), the package's bootstrap.php must be required to autoload its classes. Miss this and CI fails with class-not-found errors that look like a broken install.Technical risk
Low-to-moderate, and almost entirely confined to CI and code review:
DeclareAfterOpeningTagFixer (same-line declare(strict_types=1)) and NoImportFromGlobalNamespaceFixer (bans use DateTime;), both of which collide with prevalent Laravel style and with native rules. These should be excluded up front, not discovered in a failed build.NoNullableBooleanTypeFixer, IssetToArrayKeyExistsFixer, ForeachUseValueFixer, NoUselessStrlenFixer, NoReferenceInFunctionDefinitionFixer, PhpUnitAssertArgumentsOrderFixer, CommentedOutFunctionFixer). These can change semantics; they need a dry-run review gate, not silent enablement.*.php, which captures *.blade.php. Without an explicit exclusion the tool will attempt to reformat Blade templates. This is the single most likely way to cause real damage.Key questions
storage/, bootstrap/cache/, _ide_helper.php, migrations, generated code) that need exclusion or baselining?Stack fit
Composer-based Laravel apps and monorepos; CI on GitHub Actions or GitLab CI. Ships as a require-dev entry, ideally not required directly by every app but transitively via an internal acme/coding-standard package that also declares Pint and PHPStan config. That keeps the version bump in one place and prevents 20 repos drifting onto different rule sets.
Migration path
php-cs-fixer with the candidate subset in --dry-run --diff mode against the repo. Triage output into "safe, adopt," "risky, review," and "conflicts with our conventions, drop."DeclareAfterOpeningTagFixer, NoImportFromGlobalNamespaceFixer, and anything in the deprecated list whose native equivalent already exists..git-blame-ignore-revs, then enforce via CI with --dry-run --diff so new violations fail the build.Compatibility
Verify the declared PHP and friendsofphp/php-cs-fixer constraints against our lowest-supported PHP version. Confirm no rule collisions at config load (PHP CS Fixer fails fast, which is helpful here). Confirm Pest vs PHPUnit split per repo — the fixer value proposition differs materially between them.
Sequencing
Define a single canonical formatter order and document it. If running both tools, the pragmatic order is raw php-cs-fixer with the custom subset first, then Pint as the final word on style — this avoids the two tools oscillating on the same constructs. Do not enable CommentedOutFunctionFixer and NoCommentedOutCodeFixer together without testing; the first comments code out, the second wants commented-out code gone. Validate that pairing in the pilot before it reaches CI. Run locally through a composer fix script so developers never memorise the invocation.
Maintenance
Low but non-zero, and it belongs to a named owner. Pin an exact version, let Renovate/Dependabot raise bumps, and review the changelog for deprecations and removals on every upgrade — this package's majors are the most likely source of CI breakage. Budget roughly an hour per release cycle. The internal coding-standard package is what keeps this from multiplying across repos.
Support
Minimal internal support load. The README is generated from the fixer implementations and includes per-fixer before/after diffs, which is genuinely good self-service documentation for developers who hit an unfamiliar change. Escalation path for "the formatter did something weird" is the config file, which is version-controlled and reviewable.
Scaling
Centralise the config; that is the whole scaling story. For large repos or monorepos, use --parallel, the on-disk cache, explicit path scoping, and in PR pipelines --path-mode=intersection so only changed files are checked. Naive full-repo runs on a large Laravel monorepo will add minutes to CI and get disabled by developers within a sprint.
Failure modes
| Mode | Impact | Mitigation |
|---|---|---|
| Rule conflict on config load | CI fails immediately, no formatting runs | Validate config in the pilot; fail fast in a dedicated CI job |
| Risky fixer alters semantics | Behaviour change merged under a "formatting" PR | Risky fixers off by default; enable per-rule after diff review |
| Blade / generated file reformatted | Broken views or diff noise | Explicit Finder exclusions for *.blade.php, storage/, bootstrap/cache/, _ide_helper.php |
| Deprecated rule removed in a major | Pipeline breaks on upgrade day | Pin version; track deprecations; migrate to native rules proactively |
| Two-tool oscillation | Noisy, unreviewable diffs | Fixed tool order; single writer for contested constructs |
| Maintainer abandonment | Tool stays frozen, no new rules | Acceptable — freeze is fine for a formatter; plan to shed deprecated rules |
Ramp-up
One to two days end-to-end for the pilot including review. Developer learning curve is genuinely low because the fixers are self-documenting in diffs, but reviewers need one explicit instruction: formatting-only PRs are reviewed by scope, not line by line, and are landed in isolation. Skip pre-commit hooks initially — they slow the loop and generate bypass behaviour; CI enforcement is sufficient.
Recommendation. Proceed with a time-boxed pilot of a curated, mostly non-risky subset, with Pint retained as the canonical style authority. Treat this as a low-cost, low-risk DX improvement worth roughly a sprint-day of effort, not a strategic investment — and set a two-quarter checkpoint to decide whether native PHP CS Fixer rules have made the dependency redundant.
How can I help you explore Laravel packages today?