One PHP CS Fixer binary, eight rule sets, and a ceiling at PHP 8.5
A tool to automatically fix PHP Coding Standards issues
At a glance
- What is it?
- PHP CS Fixer both detects and rewrites PHP coding standards violations, but the decisions that shape a rewrite sit above the binary: which rule set you name, which Composer package you end up with, and whether your PHP version is inside the supported range.
- Who is it for?
- Adopt it when a team will settle on a written rule set in a config file and pin one version everywhere, and prefer check as the CI gate over fix. Leave it alone on an unsupported PHP version until you have read the migration rule sets, since @autoPHPMigration rewrites code as well as layout.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- PHP-CS-Fixer/PHP-CS-Fixer13,578 ★ · +24
| Repository | 2026-09-22 | 2026-11-14 |
|---|---|---|
| PHP-CS-Fixer/PHP-CS-Fixer | 13.6k | 13.6k |
24 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Which rule set you name decides which lines get rewritten
Style is an argument you settle before the tool ever opens a file. Each set has its own page under doc/ruleSets/, and the range is wide. @PER-CS tracks the PHP-FIG coding style, @Symfony follows the conventions the Symfony community writes down, and @PhpCsFixer is the maintainers' own opinionated one. Two more, @auto and @auto:risky, exist as base rules that aim to be good. Then the direction changes. @autoPHPMigration and @autoPHPMigration:risky move a codebase toward newer PHP, and @autoPHPUnitMigration:risky moves tests toward a newer PHPUnit.
Those migration sets are the ones to think about hardest. A style set adjusts how code looks on the page. A migration set changes code, so the same run that reindents a class can also rewrite what that class does. That is the difference between a formatting commit and a code review. If your team wants the migration behaviour, take it with tests already in place and as its own commit, and treat @autoPHPMigration:risky as a change you can roll back rather than a setting you flip.
Composer install, the shim fallback, and the first three commands
Composer is the recommended install, and it goes in as a development dependency so the binary stays inside the project:
composer require --dev friendsofphp/php-cs-fixer
## or when facing conflicts in dependencies:
composer require --dev php-cs-fixer/shimThat second line carries more weight than it looks. When your dependency tree refuses to resolve, the answer offered is a different package name rather than a different version constraint. Composer.json therefore ends up naming php-cs-fixer/shim in one repository and friendsofphp/php-cs-fixer in the next, and nothing in the README sets out what differs between them. If you take the shim you are making that call from your own conflict output.
The binary then arrives at vendor/bin, and the first documented run is one command to write a base config followed by the two you will actually use:
./vendor/bin/php-cs-fixer init
./vendor/bin/php-cs-fixer fix
./vendor/bin/php-cs-fixer checkDocker and behind-CI installs are pointed at doc/installation.rst, and styles the tool does not ship can be added through doc/custom_rules.rst.
fix rewrites files, check only says changes are needed
The two verbs differ in what they touch. fix writes to your files. check reports that changes are needed and leaves the tree as it found it. That split is the reason to keep both: check as the gate that turns a pull request red on style, fix for the developer who wants the work done.
What the README does not document is a mode that prints a patch and writes nothing. If you want to see the rewrite before it lands on a branch, the only documented route is to let fix run and read the outcome through git. On a codebase that has never had this rule set applied, that first run touches a large share of the tracked files, and the review becomes a formatting review. Keeping that sweep in a commit of its own, made with fix and before any feature work, is what makes the remaining diff readable.
PHP 8.5 is the supported ceiling and the way past it is one typed option
Supported versions run from PHP 7.4 through PHP 8.5. The stated reason for a ceiling is effort rather than policy: each new PHP version requires a huge effort to support the new syntax, which is why the latest PHP version might not be supported yet. The way past it is a single option, `--allow-unsupported-php-version=yes`, and the project's own description of using it is at your own risk.
That is a real limit on adoption rather than a footnote. A runner image on a PHP release past 8.5 will not start the fixer without that option, so a CI matrix that adds a version ahead of the project has to add the option too. Once it is set, the tool is rewriting code in a version its maintainers have said they have not finished supporting, and there is no per-version list telling you which rules apply. Pin your runner inside the supported range and move it when the supported line moves, instead of carrying the flag permanently.
PhpStorm is the only editor that ships an integration
Editor support is where the project stops short. PhpStorm is listed under native support. Everything else is a plugin somebody else maintains: NetBeans, Sublime Text, Vim and VS Code, each with its link pointing outside this repository.
The cost lands on you, not on the project. Format-on-save, or the save hook that shells out to the binary, is executed by a plugin you do not control, so a change in how a rule set or config option resolves reaches your editor on that plugin's schedule rather than the tool's. The README says nothing about a version agreement between plugin and binary, and nothing about what happens when the config file in your project uses an option the plugin has not parsed. On editors other than PhpStorm you are reading the plugin's changelog to find out what the fixer now does.
Two environment variables in .env.example are marked for removal in 4.0
.env.example is a map of the tool's internal switches, and the file shows its own deprecations:
PHP_CS_FIXER_FUTURE_MODE= # `0` / `1`, default is `0` - run PHP CS Fixer in future mode
PHP_CS_FIXER_NON_MONOLITHIC= # `0` / `1`, default is `0` - run PHP CS Fixer for non-monolithic mode (not officially supported)
PHP_CS_FIXER_IGNORE_MISMATCHED_RULES_EXCEPTIONS= # `0` / `1`, default is `0` - do not stop execution on mismatched rules exceptionsFurther down, the block headed @deprecated carries two entries that matter for CI. PHP_CS_FIXER_IGNORE_ENV is annotated use Config::setUnsupportedPhpVersionAllowed instead, and PHP_CS_FIXER_PARALLEL is annotated no effect; use Config::setParallelConfig instead. Both sit under the note that they are due to be removed in 4.0.
The consequence is concrete for a build script. A pipeline that sets PHP_CS_FIXER_PARALLEL expecting parallel runs is setting a variable the file itself calls inert; the file points to Config::setParallelConfig instead. Anything still on PHP_CS_FIXER_IGNORE_ENV has a major version bump waiting for it. PHP_CS_FIXER_NON_MONOLITHIC buys you a layout the file marks not officially supported. One more line is worth knowing before you compare a laptop to a build agent: PHP_CS_FIXER_ALLOW_XDEBUG defaults to `0`, while compose.yaml sets it to `1` in the container environment, so a machine with Xdebug loaded and the project's own container are not the same configuration.
The dist image copies src and a symlink, so there is no shell inside
The Dockerfile is split into stages, and the one meant to be run is deliberately thin:
RUN mkdir /code
WORKDIR /code
COPY src /fixer/src
COPY php-cs-fixer /fixer/php-cs-fixer
# Only take the dependencies (not composer itself) into the container
COPY --from=vendor /fixer/vendor /fixer/vendor
RUN ln -s /fixer/php-cs-fixer /usr/local/bin/php-cs-fixer
ENTRYPOINT ["/usr/local/bin/php-cs-fixer"]The dist stage builds from base rather than base-dev, so Composer is not copied into it. That is enough for a CI job that executes the tool and not for anything expecting to resolve a Composer dependency from inside. compose.yaml builds the dev stage once per supported PHP version, 7.4 through 8.5, each with its own ALPINE_VERSION and PHP_XDEBUG_VERSION, and adds sphinx-lint, yaml-lint and markdown-lint services for the project's documentation and its YAML files.
Three Adalbertus patch builds and a push dated 2026-10-02
v3.95.27, v3.95.26 and v3.95.25 all carry the codename Adalbertus and landed on 2026-09-22, 2026-09-19 and 2026-09-08. The default branch is master and the last push was 2026-11-01, so nothing here is dormant. The shape of the line is worth noticing anyway: three patch builds in a fortnight.
For adoption that means pin an exact version through composer.lock and in your editor plugin instead of following master, and expect the config file, not the code, to be what has to move. The project runs the fixer on itself: .php-cs-fixer.dist.php is joined by .php-cs-fixer.php-highest.php and .php-cs-fixer.php-lowest.php alongside it, phpstan.dist.neon keeps static analysis separate from style, and .git-blame-ignore-revs sits at the root as well. When PHP CS Fixer is the alternative you are weighing, PHPCS is worth a look too: it keeps the fixer in a separate phpcbf binary that consumes the standard PHP_CodeSniffer already selected, while PHP CS Fixer carries detection and rewriting in one executable with its own rule sets.
Editorial conclusion
Adopt it when a team will settle on a written rule set in a config file and pin one version everywhere, and prefer check as the CI gate over fix. Leave it alone on an unsupported PHP version until you have read the migration rule sets, since @autoPHPMigration rewrites code as well as layout. Verify two things before you start: that your runner sits inside PHP 7.4 to 8.5, and that your scripts do not lean on PHP_CS_FIXER_PARALLEL, which .env.example marks as having no effect.
Frequently asked questions
how to use php cs fixer
Run ./vendor/bin/php-cs-fixer init to write a base config, then ./vendor/bin/php-cs-fixer fix to rewrite files or ./vendor/bin/php-cs-fixer check to only report that changes are needed. The rules come from a rule set such as @PER-CS, @Symfony or @PhpCsFixer.
how to install php cs fixer
Composer is the recommended route: composer require --dev friendsofphp/php-cs-fixer, or php-cs-fixer/shim when facing conflicts in dependencies. Other methods, including Docker and running behind CI, are covered in doc/installation.rst.
install php cs fixer globally
The README's recommended install is a development dependency inside a project, addressed as ./vendor/bin/php-cs-fixer, and it points to doc/installation.rst for other installation methods. The dist Docker image symlinks php-cs-fixer to /usr/local/bin/php-cs-fixer.
install php cs fixer phpstorm
PhpStorm has native support in the editor integration. NetBeans, Sublime Text, Vim and VS Code are served by community plugins maintained outside the project, so their update cadence is their own.
php cs fixer vs phpstan
The repository configures the two separately for its own code: .php-cs-fixer.dist.php and phpstan.dist.neon sit side by side. The README sets out no comparison between them.
php cs fixer vs phpcbf
PHPCS keeps its fixer in a separate phpcbf binary that consumes the standard you already selected for PHP_CodeSniffer, while PHP CS Fixer carries detection and rewriting in one executable with its own rule sets under doc/ruleSets/. The README does not name phpcbf.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/php-cs-fixer-php-cs-fixer)Star history chart for your README
[](https://hysenlabs.com/projects/php-cs-fixer-php-cs-fixer)Was this useful?