Sharing configurations for PHP-CS-Fixer across projects
You are working on PHP applications and packages and use
friendsofphp/php-cs-fixer to enforce coding standards.
What are your options for sharing your configurations for
friendsofphp/php-cs-fixer to enforce a consistent coding standard across these projects in your organization?
The configuration file, typically with the name
.php-cs-fixer.dist.php, contains PHP and needs to return a configuration object.
declare(strict_types=1); use PhpCsFixer\Config; $config = new Config(); // ... return $config;
The configuration object must configure a finder that supplies
friendsofphp/php-cs-fixer with a list of files to inspect.
declare(strict_types=1); use PhpCsFixer\Config; use PhpCsFixer\Finder; $finder = Finder::create() ->exclude([ '.build/', '.github/', ]) ->in(__DIR__); $config = new Config(); $config->setFinder($finder) return $config;
If you do not configure any rulesets or rules,
friendsofphp/php-cs-fixer currently uses
@PSR12 as default ruleset.
If you configure one or more of the available rulesets and rules,
friendsofphp/php-cs-fixer will create, configure, and apply the corresponding fixers to the PHP files.
declare(strict_types=1); use PhpCsFixer\Config; use PhpCsFixer\Finder; $finder = Finder::create() ->exclude([ '.build/', '.github/', ]) ->in(__DIR__); $config = new Config(); $config ->setFinder($finder) ->setRules([ // one or more of 38 rulesets '@PSR12' => true, // one or more of 250 rules for fixers 'blank_line_before_statement' => [ 'statements' => [ 'break', 'case', 'continue', // ... ], // ... 'yoda_style' => true, ]); return $config;
Since the location of PHP files will differ from one project to another, you will want to configure the finder per project and are only concerned with sharing the configuration of rulesets and rules across projects.
Sharing configurations for PHP-CS-Fixer using a GitHub template repository
A GitHub repository template, such as
ergebnis/php-package-template enables you to set up a new project quickly - which could be an ideal location for maintaining a configuration of rulesets and rules for
But, you may realize that the PHP applications and packages you build have very different requirements and setups, so maybe you need more than one GitHub repository template.
So which of these GitHub template repositories should now be the source of truth for your configurations of rulesets and rules for
Also, you may realize that you need to support a wide range of PHP versions, requiring different configurations for
friendsofphp/php-cs-fixer. So how can you make sure that you use the correct configuration?
And, even if you had only a single GitHub repository template where you maintain a configuration of rulesets and rules for
friendsofphp/php-cs-fixer, how would you propagate the configuration of rulesets and rules to all of your other projects?
Copying the configurations from there and pasting them into a PHP project whenever you pick up work could be an option.
But this option is prone to errors. And how do you know whether the configuration in the central location has changed since you last worked on a specific project?
If we only had a delivery mechanism for distributing configurations!
Sharing configurations for PHP-CS-Fixer using a PHP package
As Paul Redmond rightly points out in his recent article about sharing configurations of rules for
squizlabs/php_codesniffer (an alternative to
friendsofphp/php-cs-fixer), the ideal solution for sharing and propagating configurations is a PHP package.
A PHP package can not only provide files, such as the XML configuration files for PHPCS in Paul's article, or PHP files or classes returning or creating arrays of rulesets and rules configurations for
friendsofphp/php-cs-fixer, but also declare dependencies.
friendsofphp/php-cs-fixer evolves, contributors and maintainers add, deprecate, and remove fixers and configuration options. You certainly want to ensure that your configuration of rulesets and rules works well with the version of
friendsofphp/php-cs-fixer you use in your projects. You can achieve that by declaring the dependency on
friendsofphp/php-cs-fixer in the PHP package instead of the consuming application or package.
If you set up the PHP package cleverly, you can use these automated updates to discover added, deprecated, and removed fixers and configuration options with automated tests. Automatic discovery of new fixers and configuration options allows you to use the full potential of fixers that
friendsofphp/php-cs-fixer provides and can free up time for more pressing problems than manually fixing coding standard violations.
Last but not least, by using Dependabot, Renovate Bot or similar services, you can propagate the configurations of rulesets and rules from your PHP package, which can now become the single source of truth of configurations for
friendsofphp/php-cs-fixer, to all of your other PHP applications and packages.
The simplest solution would be to create one or more PHP files in your package that return the configuration of rulesets and rules.
declare(strict_types=1); return [ // one or more of 38 rulesets '@PSR12' => true, // one or more of 250 rules for fixers 'blank_line_before_statement' => [ 'statements' => [ 'break', 'case', 'continue', // ... ], // ... 'yoda_style' => true, ];
Then you can require the PHP file downstream in your configuration for PHP-CS-Fixer.
declare(strict_types=1); use PhpCsFixer\Config; use PhpCsFixer\Finder; $rules = require __DIR__ . '/vendor/ergebnis/php-cs-fixer-config/config/php82.php'; $finder = Finder::create() ->exclude([ '.build/', '.github/', ]) ->in(__DIR__); $config = new Config(); $config ->setFinder($finder) ->setRules($rules); return $config;
But why not extract a factory and concrete classes providing configurations of rulesets and rules that create the configurations for you?
For example, a factory could ensure that you are running
friendsofphp/php-cs-fixer on the appropriate version of PHP. A factory could also further prepare the configuration object. Concrete classes for PHP-version-specific rulesets could allow you to override rules for project-specific configurations. A factory and classes also have the advantage that they are autoloadable, and you do not have to deal with exact file locations.
declare(strict_types=1); use Ergebnis\PhpCsFixer; $config = PhpCsFixer\Config\Factory::fromRuleSet(new PhpCsFixer\Config\RuleSet\Php82('', [ 'date_time_immutable' => false, 'mb_str_functions' => false, ]); $config->getFinder() ->exclude([ '.build/', '.github/', 'var/', ]) ->in(__DIR__); return $config;
Concrete examples from the field
As far as I can tell, this was the first PHP package that shared configurations of rulesets and rules for
friendsofphp/php-cs-fixer, inspiring many others you can find on Packagist today.
Since 2015, I have successfully applied and refined this approach in
ergebnis/php-cs-fixer-config, which I use in all my projects.
For your convenience, I have extracted a GitHub template repository at
ergebnis/php-cs-fixer-config-template as a perfect starting point to create, share, and distribute configurations of rulesets and rules for
Take a look - you will not regret it!
Projects on GitHub
📓 Provides a configuration factory and multiple rule sets for
Find out more at
📓 Provides a GitHub template repository for a configuration factory and a custom rule set for
Find out more at
📒 Provides a GitHub template repository for a PHP library, using GitHub actions.
Find out more at