What does PHP CS Fixer do to your PHP code, and how do its fix and check commands differ?
answer
- every rule is a fixer
- rewrites files in place
- check equals fix --dry-run
- --diff prints a unified diff
- exit bit 8 means files need fixing
basics
~20 sPHP CS Fixer rewrites PHP source so it matches a configured set of rules; fix changes the files, while check (fix --dry-run) changes nothing and only reports which files would change, exiting with bit 8 set.
solid answer
~40 sPHP CS Fixer is a **fixer-first** tool: every rule, such as `array_syntax` or `no_unused_imports`, is an automatic transformation of the token stream, grouped into rule sets like `@PER-CS`. `vendor/bin/php-cs-fixer fix` applies the configured rules and writes the files back; it exits 0 even when it changed files, because making those changes was the job. `vendor/bin/php-cs-fixer check`, the shorthand for `fix --dry-run` added in 3.34, runs the same rules without writing and lists the files that would change. Adding `--diff` prints the exact changes as a unified diff. In check or dry-run mode the exit code gains bit 8 when some file needs fixing and bit 4 when a file has invalid syntax, so a pipeline can fail on a non-zero result.
code
bash · 4 linesvendor/bin/php-cs-fixer check --diff # show what would change, write nothing
echo $? # 8 when at least one file needs fixing
vendor/bin/php-cs-fixer fix # apply the changes
echo $? # 0, even though files were modifiedgo deeper
Recall that PHP CS Fixer rewrites code to match rules, that fix writes files and check only reports, and that --diff shows the changes.
Explain rules versus rule sets, why fix exits 0 after changing files, and how the check exit code is built from bit flags.
Show you know the limits: no report-only rules, no type analysis, risky rules off by default, and how to read combined exit codes in automation.
Frame automatic formatting as removing a whole category of review comments, and decide which transformations a team is willing to automate.
## What the tool is **PHP CS Fixer** (Composer package `friendsofphp/php-cs-fixer`, binary `vendor/bin/php-cs-fixer`) is a code **formatter and modernizer** for PHP. It reads PHP files, splits them into tokens, applies a list of **rules**, and writes the transformed code back. Two words are easy to mix up: - **rule** (also called a *fixer*): one transformation, such as `array_syntax` (turn `array()` into `[]`), `no_unused_imports` or `single_quote`. Many rules take options, for example `['syntax' => 'short']`. - **rule set**: a named bundle of rules whose name starts with `@`, such as `@PSR12`, `@PER-CS` or `@PhpCsFixer`. The key design point is that **every rule is a fixer**. PHP CS Fixer does not have a class of report-only checks; if a rule notices something, it can also rewrite it. That makes it fast to adopt, and it also means it has nothing to say about problems that need a human decision, such as a badly named method. ## fix: change the files ```bash vendor/bin/php-cs-fixer fix # use .php-cs-fixer(.dist).php vendor/bin/php-cs-fixer fix src/ -v # one path, list the rules applied per file ``` `fix` applies every configured rule to every file the configuration selects and writes the results. After fixing, it lints each changed file, and if the result would not parse it reports an error rather than keeping broken code. `fix` exits with **0** when it ran successfully, **even if it modified files**. Changing files is its purpose, not a failure. ## check: report, never write `check` was introduced in 3.34 as a shorthand for `fix --dry-run`, and accepts the same options. It runs the same rules in memory and prints the files that **would** change, without touching them. - `--diff` adds a unified diff for each file, which shows exactly what the fixer wants to change. - `--format` chooses the output (`txt`, `json`, `junit`, `checkstyle`, `gitlab`, `xml`; in 3.x the default is `txt`). - `--stop-on-violation` stops after the first file that needs fixing. ## Exit codes are bit flags | Bit | Meaning | When it can appear | |---|---|---| | 0 | OK | always | | 1 | general error, or PHP minimum not met | always | | 4 | some files have invalid syntax | `check` / `fix --dry-run` only | | 8 | some files need fixing | `check` / `fix --dry-run` only | | 16 | configuration error of the application | always | | 32 | configuration error of a rule | always | | 64 | exception raised within the application | always | Because they are bit flags, `12` means "some files need fixing **and** some have invalid syntax". A CI job simply fails on any non-zero value from `check`. ## Everyday workflow 1. Locally, run `fix` before committing, or wire it into the editor. 2. Review the resulting diff like any other change, especially the first time a rule set is adopted. 3. In automation, run `check --diff`, so a failing job shows the exact change the author needs to make. ## Understanding what a run did - `-v` lists, per file, the rules that changed it, which is the quickest way to find the rule behind a surprising edit. - `vendor/bin/php-cs-fixer describe single_quote` prints a rule's description, options, before-and-after examples and whether it is risky. - `vendor/bin/php-cs-fixer describe @PER-CS --expand` lists every rule a set contains, including nested sets. - `vendor/bin/php-cs-fixer list-files` prints only the paths that need fixing, which is handy for scripting. Reading a rule's own description before disabling it usually shows whether an option would fit better than switching it off. ## What it does not do - It does not analyse types or logic; a formatter that knows nothing about what `$user` holds cannot find a null dereference. - It does not report problems it cannot fix, because it has no report-only rules. - By default it does not run **risky** rules, those that may change behaviour, unless the configuration or `--allow-risky=yes` allows them.
- Why does php-cs-fixer fix exit with 0 after it modified files?Because rewriting files is the command's purpose. Only `check` or `fix --dry-run` treat a needed change as a finding and set bit 8. That is why automation that must fail on unformatted code runs `check`, not `fix`.
- What does the exit code 12 from php-cs-fixer check mean?The exit code is built from bit flags: 8 means some files need fixing and 4 means some files have invalid syntax, so 12 means both happened in the same run. The syntax errors must be fixed by hand, because the fixer skips files it cannot parse.
saying these in an interview costs you the question
- php-cs-fixer only reports problems and never edits files
- check is a separate engine with different rules from fix
- fix exits non-zero whenever it changed a file
- PHP CS Fixer can flag badly named methods it cannot fix
- Risky rules run by default