How would you use PHP CS Fixer to reformat a whole PHP codebase to PER Coding Style in one commit without disrupting the team?
answer
- pin the tool and the set
- @PER-CS3x0, no risky rules
- check --diff first, then fix
- one commit containing nothing else
- .git-blame-ignore-revs for that commit
basics
~20 sPin PHP CS Fixer and a versioned set such as @PER-CS3x0, keep risky rules out, preview with check --diff, apply fix in a commit that contains nothing else, list that commit in .git-blame-ignore-revs, and start enforcing with check.
solid answer
~40 sFirst make the result reproducible: require `friendsofphp/php-cs-fixer` as a dev dependency so the lock file pins it, and use the versioned `@PER-CS3x0` rather than the moving `@PER-CS` alias. Keep the commit **behaviour-neutral**: no risky rules and no migration sets, which belong in later, separately reviewed commits. Run `check --diff` to see the scope, agree a moment when few branches are open, run `fix`, run the tests, and commit the result alone with a clear message. Add that commit's hash to `.git-blame-ignore-revs` so `git blame` skips it. Open branches rebase by running the same `fix` on their side first, which turns most conflicts into no-ops. From then on, `check` keeps the codebase formatted.
code
bash · 6 linescomposer require --dev friendsofphp/php-cs-fixer
vendor/bin/php-cs-fixer check --diff | less # preview the scope
vendor/bin/php-cs-fixer fix # apply @PER-CS3x0 from the dist config
git commit -am 'style: apply PER Coding Style 3.0 with php-cs-fixer'
git rev-parse HEAD >> .git-blame-ignore-revs
git config blame.ignoreRevsFile .git-blame-ignore-revsgo deeper
Recall that the reformat is one fix run with a committed config, done in its own commit, and verified afterwards with check.
Explain why the tool and the rule set must be pinned, and why risky and migration rules stay out of a formatting commit.
Show the full plan: preview with check --diff, isolated commit, tests, .git-blame-ignore-revs, and a rebase recipe for open branches.
Decide timing and communication across teams, and when the next style revision is worth another migration.
## The goal **PER Coding Style** is PHP-FIG's evolving coding-style specification, the successor to PSR-12. Moving an existing codebase to it with PHP CS Fixer is mechanically easy, since a single `fix` run does it. The hard part is doing it **once, predictably and reviewably**, without breaking open pull requests or `git blame` history. ## Step 1: make the result reproducible - **Pin the tool.** `composer require --dev friendsofphp/php-cs-fixer` records the exact version in `composer.lock`. Two developers on different versions can produce different output, because rule sets are not covered by the backward-compatibility promise. - **Pin the style.** `@PER-CS` is an alias for the newest revision, currently `@PER-CS3x0`. For a one-off migration, write `@PER-CS3x0` so an upgrade later cannot quietly reformat everything again. - **Commit the config.** `.php-cs-fixer.dist.php` with the Finder paths and the set, so every run selects the same files. ## Step 2: keep the commit behaviour-neutral A formatting commit is only safe to merge without line-by-line review if it **cannot change behaviour**: - no risky rules (`setRiskyAllowed(false)`, the default), so there is no `strict_comparison`, `declare_strict_types` or `native_function_invocation`; - no migration sets such as `@PHP8x5Migration` in the same commit, because those change syntax and belong in their own reviewed change; - no hand edits mixed in. The fixer lints every file it changes, and the test suite should still pass unchanged. If a test fails, something other than formatting slipped in. ## Step 3: preview, then apply 1. `vendor/bin/php-cs-fixer check --diff` shows how many files change and what the changes look like. Look for surprises, such as a vendored directory that the Finder should exclude. 2. Pick a moment when few branches are open, and announce it. 3. `vendor/bin/php-cs-fixer fix`, then run the test suite. 4. Commit with a message that says what happened, for example `style: apply PER Coding Style 3.0 with php-cs-fixer`, and push it alone. ## Step 4: protect history and open branches - **`git blame`.** Add the commit hash to a `.git-blame-ignore-revs` file and set `git config blame.ignoreRevsFile .git-blame-ignore-revs`, so blame attributes lines to their real authors and not to the formatting commit. - **Open branches.** Before rebasing onto the new base, the branch owner runs the same pinned `fix` on their branch and commits it. Their code is then formatted the same way as the new base, and most would-be conflicts disappear because both sides now contain identical lines. ## Step 5: keep it that way After the migration, the codebase stays formatted only if every new change is checked: `vendor/bin/php-cs-fixer check --diff` must pass. Where that check runs (editor, hook or pipeline) is a separate integration decision; the point here is that the commit is only the start. ## What to leave for later | Change | Why it waits | |---|---| | `@PER-CS:risky` or other risky rules | may change behaviour, so needs review and tests | | `@PHP8x5Migration` | rewrites syntax, and must match the minimum supported PHP | | moving to a newer PER revision | a deliberate future decision, so it gets its own migration commit | ## Common mistakes - **Unpinned tool.** Someone runs a newer PHP CS Fixer a week later and the "done" codebase changes again. - **Finder too wide.** Generated code, vendored libraries or fixtures that deliberately contain odd formatting get rewritten; exclude them with `exclude()` or `notPath()` first. - **Mixed commit.** A reformat that also renames a method or bumps a dependency cannot be skipped in blame or safely reverted. - **No follow-up enforcement.** Without `check` on new changes, the codebase drifts back within weeks and the next migration is needed again. ## Why one commit Reformatting file by file spreads noise across hundreds of feature commits and keeps two styles alive for months. A single, isolated commit is easy to skip in blame, easy to revert, and ends the mixed-style period at once.
- Why use @PER-CS3x0 instead of @PER-CS for the migration?`@PER-CS` follows the newest PER revision and moves when PHP CS Fixer adds one. Pinning `@PER-CS3x0` means a later tool upgrade cannot silently reformat the codebase again; moving to a new revision becomes a deliberate, separate change.
- How should a developer with a long-running branch handle the reformat?Run the same pinned `php-cs-fixer fix` on the branch first and commit that, then rebase or merge. Both sides then contain identically formatted lines, so most conflicts caused purely by formatting vanish, and the remaining ones are real code conflicts.
saying these in an interview costs you the question
- Mix risky rules into the formatting commit to save time
- Let each developer reformat files as they touch them
- Use the moving @PER-CS alias so the migration stays current
- Formatting commits need no test run
- Different PHP CS Fixer versions always produce identical output