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

Php Cs Fixer Custom Fixers Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is not a Laravel package — it is an extension pack for PHP CS Fixer. Nothing happens at runtime, so it belongs in require-dev and only pays off if you actually run a fixer binary in your workflow.

composer require --dev friendsofphp/php-cs-fixer kubawerlos/php-cs-fixer-custom-fixers

Create .php-cs-fixer.dist.php in the project root:

<?php

use PhpCsFixer\Config;
use PhpCsFixer\Finder;
use PhpCsFixerCustomFixers\Fixer;
use PhpCsFixerCustomFixers\Fixers;

return (new Config())
    ->registerCustomFixers(new Fixers())   // <- without this line, every rule below is "unknown"
    ->setRiskyAllowed(true)
    ->setRules([
        '@PSR12' => true,
        // safe, high-value first picks
        Fixer\NoDuplicatedImportsFixer::name()          => true,
        Fixer\NoUselessCommentFixer::name()             => true,
        Fixer\NoSuperfluousConcatenationFixer::name()   => true,
        Fixer\NoTrailingCommaInSinglelineFixer::name()  => true,
        Fixer\NoUselessParenthesisFixer::name()         => true,
        Fixer\CommentSurroundedBySpacesFixer::name()    => true,
        Fixer\DeclareAfterOpeningTagFixer::name()       => true,
    ])
    ->setFinder(
        Finder::create()
            ->in(__DIR__)
            ->exclude(['bootstrap/cache', 'storage', 'vendor', 'node_modules'])
            ->name('*.php')
            ->notName('*.blade.php')
    );

Add a script so the team never types the binary name:

"scripts": {
    "lint": "php-cs-fixer fix --dry-run --diff",
    "lint:fix": "php-cs-fixer fix"
}

Where to look first: run vendor/bin/php-cs-fixer describe Kubawerlos/no_useless_comment (any fixer name) to get the exact diff semantics and options, then skim vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ — each fixer is a single readable class with a getDefinition()/getConfigurationDefinition() that documents its options better than the README.

First use case that pays for itself immediately: NoDuplicatedImportsFixer + NoUselessCommentFixer + NoSuperfluousConcatenationFixer on an existing Laravel app. Run composer lint -- --diff once and you will typically get a few hundred mechanically safe, reviewable changes on day one.

Important Laravel caveat up front: Laravel Pint cannot register custom fixers. Pint has no equivalent of registerCustomFixers(), so Kubawerlos/... keys in pint.json will not work. If your team is on Pint, run Pint for the base preset and PHP CS Fixer (with this package) as a second pass — or keep the rule sets aligned and only let PHP CS Fixer handle the extras.

Implementation Patterns

Reference rules via ::name(), never string literals. The returned value (the Kubawerlos/... form you'll see in configs and cache output) is stable, but ::name() gives you IDE navigation, refactor safety, and a compile-time-ish failure if a fixer is renamed.

Start curated, not "all fixers". This package ships ~40 fixers of wildly different aggressiveness, and several are risky. A pattern that works well in a Laravel codebase:

$safe = [
    Fixer\CommentSurroundedBySpacesFixer::name(),
    Fixer\ConstructorEmptyBracesFixer::name(),
    Fixer\EmptyFunctionBodyFixer::name(),
    Fixer\MultilineCommentOpeningClosingAloneFixer::name(),
    Fixer\NoDuplicatedArrayKeyFixer::name(),
    Fixer\NoDuplicatedImportsFixer::name(),
    Fixer\NoImportFromGlobalNamespaceFixer::name(),
    Fixer\NoLeadingSlashInGlobalNamespaceFixer::name(),
    Fixer\NoSuperfluousConcatenationFixer::name(),
    Fixer\NoTrailingCommaInSinglelineFixer::name(),
    Fixer\NoUselessCommentFixer::name(),
    Fixer\NoUselessDirnameCallFixer::name(),
    Fixer\NoUselessParenthesisFixer::name(),
    Fixer\FunctionParameterSeparationFixer::name(),
];

then opt into risky ones per-directory rather than globally (see below).

Split configuration by path. PHP CS Fixer takes one rule set per config file, but you can run it twice with different configs — this is the cleanest way to apply PhpUnit* fixers only to tests/:

// .php-cs-fixer.tests.php
return (new Config())
    ->registerCustomFixers(new Fixers())
    ->setRiskyAllowed(true)
    ->setRules([
        Fixer\PhpUnitAssertArgumentsOrderFixer::name()   => true,
        Fixer\PhpUnitDedicatedAssertFixer::name()        => true,
    ])
    ->setFinder(Finder::create()->in(__DIR__.'/tests'));
"lint:fix": [
    "php-cs-fixer fix",
    "php-cs-fixer fix --config=.php-cs-fixer.tests.php"
]

CI gate pattern. Use the cache locally, kill it in CI so a stale cache never hides a regression, and fail the build on any diff:

vendor/bin/php-cs-fixer fix --dry-run --diff --using-cache=no -v

For pre-commit hooks (CaptainHook, Husky, lint-staged, GrumPHP), restrict work to staged files:

vendor/bin/php-cs-fixer fix --path-mode=intersection --diff --using-cache=yes $(git diff --cached --name-only --diff-filter=ACM -- '*.php')

Gradual rollout on a legacy app. Fix one rule at a time and land it as its own PR:

vendor/bin/php-cs-fixer fix --rules=Kubawerlos/no_superfluous_concatenation --dry-run --diff app/

Because PHP CS Fixer applies each fixer once per file in a single pass, enabling a new fixer can expose a second-order opportunity for another fixer. Practical pattern: after composer lint:fix, run composer lint again and commit any residual diff — or just accept that CI's first run may need a follow-up pass.

Pair with static analysis, not against it. Laravel projects lean on PHPStan/Larastan docblocks (@property, @method, generics on Collection). Keep those paths out of PhpdocNoSuperfluousParamFixer and NoUselessCommentFixer scopes; a two-config split (app/ vs app/Models/) is usually cheaper than arguing about it.

Discovering fixers programmatically. Fixers is an iterator of fixer instances, so you can build a rules array and filter by risk instead of hand-maintaining lists:

$rules = [];
foreach (new Fixers() as $fixer) {
    if (!$fixer->isRisky()) {
        $rules[$fixer->getName()] = true;
    }
}

This is also the pattern to use when you want a "report only" inventory of everything the package would change, before committing to a subset.

Gotchas and Tips

  • Pint incompatibility is the #1 trap. If you drop Kubawerlos/... keys into pint.json expecting them to work, they silently don't (or error as unknown rules). Pint ships its own fixer registration; custom fixers need a real .php-cs-fixer.dist.php. Also beware wrappers like Duster that delegate to Pint — same limitation.
  • Risky fixers need ->setRiskyAllowed(true), otherwise the config throws The rules contain risky fixers. On the CLI that's --allow-risky=yes. Note this flips risk on globally for that config file — split configs if you only want risky fixers in tests/.
  • Shim bootstrap. If PHP CS Fixer is installed via php-cs-fixer/shim (common when a tool bundles it), the PhpCsFixerCustomFixers\* classes aren't autoloaded by the shim's isolated autoloader. Add:
    require __DIR__.'/vendor/kubawerlos/php-cs-fixer-custom-fixers/bootstrap.php';
    
    Symptom if you forget: Class "PhpCsFixerCustomFixers\Fixers" not found while PHP CS Fixer itself runs fine.
  • Cache staleness. .php-cs-fixer.cache is written to the project root — gitignore it. After upgrading this package or PHP CS Fixer, if a fixer appears to do nothing, delete the cache and rerun before debugging anything else. This bites people constantly.
  • Deprecated fixers are being absorbed into core. DataProviderNameFixer, DataProviderReturnTypeFixer, DataProviderStaticFixer, InternalClassCasingFixer, MultilinePromotedPropertiesFixer, NumericLiteralSeparatorFixer, PhpDocPropertySorterFixer are all marked deprecated in favour of core rules (php_unit_data_provider_*, class_reference_name_casing, multiline_promoted_properties, numeric_literal_separator, …). Prefer the core rule when available — the custom one can disappear in a major. Don't let both versions of the same rule run; you'll get churn or a conflict.
  • Mutually exclusive pair: CommentedOutFunctionFixer (comments out var_dump, dd, dump, …) and NoCommentedOutCodeFixer (deletes commented-out code) fight each other. Pick one. On Laravel, dd/dump are usually caught by a static-analysis rule or a pre-commit grep instead; NoCommentedOutCodeFixer is the safer default.
  • Fixers that fight core rules:
    • DeclareAfterOpeningTagFixer vs blank_line_after_opening_tag / declare_strict_types (the core rule inserts on a new line; this one moves it up). Enable declare_strict_types and this one, but drop blank_line_after_opening_tag, and verify with --diff.
    • NoLeadingSlashInGlobalNamespaceFixer vs global_namespace_import / fully_qualified_strict_types. Choose one philosophy; mixing them produces noisy, oscillating diffs.
    • NoImportFromGlobalNamespaceFixer will rewrite use DateTime; to \DateTime — check it doesn't collide with your ordered_imports / no_unused_imports expectations.
  • Risky fixers that bite Laravel specifically:
    • IssetToArrayKeyExistsFixer — isset($data['x']) is false when the value is null; array_key_exists is true. If you have nullable payload keys, this changes behaviour. It also emits a warning on undefined arrays where isset was silent.
    • NoNullableBooleanTypeFixer — Eloquent/DB booleans are frequently null (nullable tinyint columns). Do not enable on models or anything hydrating from the DB.
    • NoReferenceInFunctionDefinitionFixer — breaks by-ref wrappers around preg_match, array_walk, sort, and any macro doing by-ref work.
    • ForeachUseValueFixer — unsafe if the loop body mutates the array or relies on ordering.
    • NoUselessStrlenFixer — assumes strlen isn't overridden and that the operand is a string; mb_strlen semantics differ from === '' only marginally, but custom string wrappers will break it.
    • PhpUnitAssertArgumentsOrderFixer / PhpUnitDedicatedAssertFixer — assume vanilla PHPUnit assertions. Custom test-case base classes and wrapper assertions can be misread.
  • Pest is largely invisible to the PHPUnit fixers. Pest tests use closures and ->with() providers, not @dataProvider docblocks or static provider methods. Expect zero coverage there; don't enable the data-provider fixers thinking they'll tidy your Pest suite.
  • NoUselessCommentFixer can strip comments that tooling depends on — Laravel Idea, IDE Helper _ide_helper.php regeneration, and PHPStan all consume docblocks. Exclude app/Models or the whole *.php set that your static analyser reads, then introduce it deliberately.
  • ClassConstantUsageFixer produces false positives on short, common strings ('bar', 'active', 'pending') appearing in unrelated classes. Keep it on app/ only, and never on migrations/seeders.
  • NoSuperfluousConcatenationFixer has two escape hatches worth knowing: allow_preventing_trailing_spaces and keep_concatenation_for_different_quotes. Enable both if your codebase deliberately splits long translation strings or concatenates single- and double-quoted literals for readability/line length.
  • Debugging a fixer that "doesn't apply": run php-cs-fixer fix path/File.php -vvv --diff --dry-run. -vvv prints which fixers were applied and which were skipped. If it's not in the list at all, the problem is registration (registerCustomFixers), a config typo, or the Finder excluding the file — not the fixer.
  • Version lockstep. The package tracks PHP CS Fixer majors; a Composer ^3.x PHP CS Fixer with an incompatible custom-fixers release is where "unknown rule" errors come from. Upgrade both together and rerun with --using-cache=no.
  • Extension point: Fixers is just an iterator, so you can compose your own registration object and mix in house fixers implementing PhpCsFixer\Fixer\FixerInterface — useful when you want one registerCustomFixers() call covering both this package and internal rules for things like env() outside config/.
  • Performance: on a large Laravel repo, the full fixer set on a cold cache is slow. Local dev should keep the cache on; only CI should pay for --using-cache=no. Narrow the Finder (->exclude(['storage', 'bootstrap/cache'])) rather than trimming fixers.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core