In PHP CS Fixer, how do you configure .php-cs-fixer.dist.php with a Finder and rule sets such as @PER-CS or @PHP8x5Migration?
answer
- the file returns a Config object
- Finder chooses the files
- @ prefix marks a rule set
- rule => false switches one off
- local .php-cs-fixer.php takes precedence
basics
~20 sThe committed .php-cs-fixer.dist.php is a PHP file that returns a PhpCsFixer\Config whose Finder selects the files and whose setRules() array enables rule sets like @PER-CS or @PHP8x5Migration plus individual rules, which can be tuned or switched off.
solid answer
~40 s`.php-cs-fixer.dist.php` is plain PHP that returns a `PhpCsFixer\Config`. `setFinder()` takes a `PhpCsFixer\Finder`, a Symfony Finder that by default keeps only `*.php` files, skips `vendor` and, in 3.x, dot-files; add `->in(__DIR__.'/src')`, `->exclude()` for directories and `->notPath()` for files. `setRules()` takes an array where `'@PER-CS' => true` enables a set, `'strict_param' => true` adds a rule, `'array_syntax' => ['syntax' => 'short']` configures one and `'yoda_style' => false` removes one. `@PER-CS` tracks the latest PER Coding Style (currently `@PER-CS3x0`), `@PhpCsFixer` extends `@PER-CS` and `@Symfony`, and `@PHP8x5Migration` modernises code for PHP 8.5. An uncommitted `.php-cs-fixer.php` takes precedence over the `.dist` file. With no config at all, 3.x applies `@PSR12`.
code
php · 17 lines<?php
declare(strict_types=1);
$finder = (new PhpCsFixer\Finder())
->in([__DIR__ . '/src', __DIR__ . '/tests'])
->exclude('Fixtures')
->notPath('Legacy/bootstrap.php');
return (new PhpCsFixer\Config())
->setRules([
'@PER-CS' => true,
'@PHP8x5Migration' => true,
'no_unused_imports' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder);go deeper
Recall that the committed config is .php-cs-fixer.dist.php, that it returns a Config with a Finder, and that @ names a rule set.
Explain Finder defaults, exclude versus notPath, how rules override sets in setRules(), and how the local config takes precedence.
Show you pin versioned sets, match the migration set to the project's minimum PHP, and inspect the effective config with describe.
Decide whether a shared base configuration belongs in a package reused across repositories, and who approves changes to it.
## The config file is PHP PHP CS Fixer is configured with a **PHP file that returns a config object**, not with XML or YAML. When you run the tool without `--config`, it looks in the target directory (and the working directory) for: 1. `.php-cs-fixer.php`, a local, usually git-ignored override; 2. `.php-cs-fixer.dist.php`, the committed project configuration. The first one found wins. The documented pattern is for the local file to `require` the `.dist` file and adjust only personal preferences such as output format, so the team's rules and paths stay shared. If no config exists, PHP CS Fixer 3.x applies `@PSR12` to the path you give it (version 4 plans to switch this default). ## Choosing files with Finder `PhpCsFixer\Finder` extends Symfony's Finder component with sensible defaults: - only files matching `*.php`; - the `vendor` directory excluded; - VCS directories ignored, and in 3.x dot-files and dot-directories as well. You then narrow or widen it: | Method | Purpose | |---|---| | `->in([__DIR__.'/src', __DIR__.'/tests'])` | directories to scan | | `->exclude('fixtures')` | skip **directories**, relative to `in()` | | `->notPath('legacy/bootstrap.php')` | skip specific **files**, relative to `in()` | | `->append([__DIR__.'/bin/console'])` | add files the name filter would miss | A path given on the command line **overrides** the Finder by default (`--path-mode=override`); `--path-mode=intersection` keeps only files that are both on the command line and in the Finder. ## Choosing rules with setRules() `setRules()` takes an associative array. Keys starting with `@` are **rule sets**; other keys are individual **rules**: - `'@PER-CS' => true` enables a whole set; - `'no_unused_imports' => true` adds a single rule; - `'array_syntax' => ['syntax' => 'short']` enables a rule with options; - `'yoda_style' => false` turns off a rule that an enabled set would include. Later entries refine earlier ones, so the usual shape is "one or two sets, then a few overrides". ## The rule sets that matter | Set | What it is | |---|---| | `@PSR12` | the PSR-12 style; the 3.x default when there is no config | | `@PER-CS` | PER Coding Style, an alias for the newest revision, currently `@PER-CS3x0` | | `@PER-CS3x0` | PER Coding Style 3.0, pinned | | `@Symfony` | extends `@PER-CS` with the Symfony project's conventions | | `@PhpCsFixer` | the maintainers' opinionated set; extends `@PER-CS` and `@Symfony` | | `@PHP8x5Migration` | modernisations for PHP 8.5; builds on `@PHP8x4Migration` and adds `switch_case_semicolon_to_colon` | | `@auto` | newest PER-CS plus the PHP migration set matching the minimum PHP in `composer.json` | Each set that contains behaviour-changing rules has a `:risky` companion, such as `@PER-CS:risky` or `@PHP8x5Migration:risky`, which only runs when risky rules are allowed. Naming changed during 3.x: the migration sets now use `x` notation (`@PHP8x5Migration`), and the dotted and older names (`@PHP85Migration`, `@PER-CS3.0`) are deprecated and slated for removal in 4.0. Pick the migration set that matches the **lowest PHP version the project supports**, not the version on your laptop. For example `@PHP8x4Migration` includes `new_expression_parentheses`, which rewrites `(new Foo())->bar()` to `new Foo()->bar()`, syntax that only parses on PHP 8.4 or newer. `@auto` and `@autoPHPMigration` read that minimum from `composer.json` for you. ## Other useful settings - `setRiskyAllowed(true)` permits risky rules. - `setCacheFile()` and `setUsingCache()` control the cache (`.php-cs-fixer.cache` by default). - `setParallelConfig()` tunes the parallel runner. - `setIndent()` and `setLineEnding()` set the whitespace style. Two more tools are worth knowing: - `--rules` on the command line **replaces** the configured rules for one run, for example `--rules=@PER-CS,-single_quote` (a leading dash removes a rule), which is useful for trying a rule before committing to it. - A file-level `// @php-cs-fixer-ignore rule_name` comment skips one rule for one file. The feature is marked **experimental** in 3.95, and the tool raises an error when an ignored rule is not part of the configured rules at all, so an ignore cannot silently outlive a rule set change. To see what a configuration expands to, run `vendor/bin/php-cs-fixer describe @PER-CS --expand`, or `describe @` for the configuration currently in use.
- Why pin @PER-CS3x0 instead of @PER-CS?`@PER-CS` is an alias for the latest PER Coding Style revision, and rule sets are not covered by the backward-compatibility promise. A tool upgrade can therefore start reformatting files when a new revision lands. Pinning `@PER-CS3x0` keeps the style fixed until the team decides to move.
- A developer's .php-cs-fixer.php makes their run ignore the team rules. What went wrong?`.php-cs-fixer.php` is found before `.php-cs-fixer.dist.php`, and only the first file is used. If it returns a fresh `Config` instead of requiring the `.dist` file and adjusting it, the shared rules and Finder are lost. The local file should `require` the project config and change only personal settings.
saying these in an interview costs you the question
- The config is a YAML or XML file
- Finder exclude() works for single files too
- @PER-CS is frozen at one PER revision forever
- A local .php-cs-fixer.php is merged with the .dist file automatically
- Choose the migration set for the PHP version on your laptop