Sharing PHPCS Rules Across Projects and Teams
Published on by Paul Redmond
PHPCS is an open-source CLI tool that detects code style violations of a defined coding standard, and also provides automatic fixes for automatically fixable rules. I want to convince you that even if you use Pint or PHP-CS-Fixer directly, you should still consider adding PHPCS to your repertoire.
Defining PHPCS rules across multiple projects in a team setting is tedious, and rule drift is bound to happen between projects. You'll likely want to enforce consistent rules across your projects without much thought or effort.
At the end of this tutorial, you'll know how to create an organization ruleset you can use to quickly lint all your PHP projects.
In Part 1 of this series, you learned how to Use PHP Codesniffer With Laravel projects. This tutorial will move the ruleset we created in a Laravel project to a dedicated Composer package.
Background
You might ask, "why would I use PHPCS when we have Laravel Pint and PHP CS Fixer?" Instead of considering these tools as competitors, consider them complementary tools that each provide unique, valuable code style suggestions and fixes. PHPCS is a linter, and PHP-CS-Fixer is a fixer.
Indeed, these types of tools overlap: PHP-CS-Fixer has linting capabilities using the --dry-run
feature. PHPCS has a phpcbf
CLI tool that automatically fixes PHPCS sniff violations. However, phpcbf
does not fix every single available rule.
For example, you can configure PHPCS to detect and report line length violations; however, PHPCS cannot automatically fix these violations since the tool cannot determine how you would like to break up long lines:
The screenshot illustrates line length configuration that warns when a line exceeds 120 characters and warns for lines that exceed 80 characters but are still within the maximum threshold of 120 characters. This can be useful to keep your code healthy and lint things like line length.
Getting Started
We'll need to create a new PHP composer package to create a custom Ruleset that you can reuse on all your projects. PHPCS needs to know about these rulesets though, so we will use the Composer Installer package which makes it easy to install coding standards via Composer.
Before we get to that, let's create a package and initialize Git and Composer:
mkdir phpcscd phpcsgit initcomposer init
Once you run composer init
, Composer will prompt you to define things like your package name. At least fill out the package name, optionally the description, and finish the prompts. We will require dependencies manually, so don't worry about installing them interactively.
To ease detecting PHPCS standards from composer packages, we now need to install the Composer Installer package:
composer require --dev \ dealerdirect/phpcodesniffer-composer-installer
The composer installer also requires that you define the type
property with phpcodesniffer-standard
. The Composer Installer plugin searches for rulesets using the type
property in all of your project's installed Composer packages.
In the end, your composer.json
file should look something like the following:
{ "name": "bitpressio/phpcs", "type": "phpcodesniffer-standard", "authors": [ { "name": "Paul Redmond", "email": "paulrredmond@gmail.com" } ], "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0" }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } }}
We have everything we need to define our Composer package, and all we need to do to get it working is define our ruleset.
Defining the Ruleset
Our Composer package will contain our ruleset.xml
file that we will install in all of our projects. I named my ruleset Bitpress
, so I need to create a folder in my project that will house the ruleset:
mkdir Bitpresstouch Bitpress/ruleset.xml
We created a Ruleset in Part 1 of this series, so add the following contents to ruleset.xml
, replacing values for whatever you called your ruleset:
<?xml version="1.0"?><!-- @see https://pear.php.net/manual/en/package.php.php-codesniffer.annotated-ruleset.php --><ruleset name="Bitpress PHPCS Rules"> <description>PHPCS ruleset for Bitpress</description> <!-- Use colors in output --> <arg name="colors"/> <!-- Show progress of the run --> <arg value="p"/> <!-- Show sniff codes in all reports --> <arg value="s"/> <!-- Our base rule: set to PSR12 --> <rule ref="PSR12"> <exclude name="PSR12.Traits.UseDeclaration.MultipleImport" /> <exclude name="PSR12.Operators.OperatorSpacing.NoSpaceBefore" /> <exclude name="PSR12.Operators.OperatorSpacing.NoSpaceAfter" /> </rule> <rule ref="Generic.Files.LineLength"> <properties> <property name="lineLimit" value="80"/> <property name="absoluteLineLimit" value="120"/> </properties> </rule> <rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps"> <exclude-pattern>tests/</exclude-pattern> </rule> <exclude-pattern>*/.phpstorm.meta.php</exclude-pattern> <exclude-pattern>*/_ide_helper.php</exclude-pattern> <exclude-pattern>*/*.blade.php</exclude-pattern> <exclude-pattern>*/autoload.php</exclude-pattern> <exclude-pattern>*/vendor/*</exclude-pattern> </ruleset>
Our ruleset will not include any custom sniffs, but we will define our preferred setup based on the built-in PSR12
ruleset.
We have everything we need to start using our custom ruleset! Once you publish your composer package, you can require it as a --dev
dependency.
Setting Up the Ruleset in a Project
Once your package is published as a Composer package, you can install it in a project like so:
composer require --dev bitpressio/phpcs
The Composer Installer dependency will ask for permission to execute code so that it can detect and install found standards in Composer packages. Select y
to enable the plugin:
At this point, we installed our custom Ruleset, and we can verify using the following command:
vendor/bin/phpcs -iThe installed coding standards areMySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend,Bitpress and VariableAnalysis
The --show-info
flag can show us more information:
vendor/bin/phpcs --config-showUsing config file: /Users/paul/code/sandbox/bitpress-phpcs/vendor/squizlabs/php_codesniffer/CodeSniffer.conf installed_paths: ../../bitpressio/phpcs,../../sirbrillig/phpcs-variable-analysis
Finally, we can make our ruleset the default with the following:
vendor/bin/phpcs --config-set default_standard Bitpress
We could have each developer manually set our rulesets as the default; however, using composer.json
we can trigger it automatically with the following:
"scripts": { "post-package-install": "vendor/bin/phpcs --config-set default_standard Bitpress"}
If we rerun our command, our ruleset should be the default now:
vendor/bin/phpcs --config-show...default_standard: Bitpress
We are almost done, but we have a few minor tweaks to make to finish up our installation.
Defining a Project Ruleset Configuration
We have our ruleset installed and set as the default standard. However, if we run phpcs
we will get the following message:
vendor/bin/phpcsERROR: You must supply at least one file or directory to process. Run "phpcs --help" for usage information
Though our ruleset is the default, we still need to provide configuration to instruct PHPCS which files and folders to lint. Let's create a phpcs.xml
in the root of a project:
<?xml version="1.0"?><!-- @see https://pear.php.net/manual/en/package.php.php-codesniffer.annotated-ruleset.php --><ruleset name="My App"> <file>app</file> <file>tests</file> <rule ref="Bitpress"></rule></ruleset>
If you run phpcs
in your project (I tested this out in a new Laravel app), PHPCS will lint files in app/
and tests/
and should output some warnings about line length. Feel free to configure these paths to your liking.
Wrap Up
On our whirlwind tour of PHPCS rulesets, we published a composer package containing a custom Ruleset that we can share across our organization. If you want consistent linting across multiple projects, hopefully this approach might come in handy.