Creating Your Own Standard for PHP_CodeSniffer

In “The PSR Coding Standards” we discussed how we have three options when picking a coding standard.

  1. We can create a coding standard from scratch.
  2. We can borrow a coding standard that already exists and use it as is.
  3. We can borrow a coding standard that exists and then add or remove rules that don’t fit our team’s environment.

In this article, we’re going to discuss how we can implement the third option by creating our own standard for phpcs using the PSR12 standard as a starting point. If you missed either our article about “The PSR Coding Standards” or “PHP_CodeSniffer” you might want to read them first.

Creating Our Own Standard File

To get this started we’re going to create an XML file at the root of our project directory named after the standard we’re going to create. We’re going to be making the “PHP Developers TV” coding standard. It’s based on the PSR12 standard but includes some helpful sniffs that will make it easier to write our code and catch errors sooner.

To start we’re going to create the “PHPdevsTV.xml” file and enter the following information.

<?xml version="1.0"?>
<ruleset name="PHP Developers TV">
    <description>The PHP Developers TV coding standard.  PSR12 + Types</description>
    <rule ref="PSR12" />
</ruleset>

To start with our new standard uses all of the sniffs included in the PSR12 standard.

We can use it by either specifying the standard as the default or we can pass it using the --standard command-line switch.

./vendor/bin/phpcs –config-set default_standard PHPdevsTV.xml ./vendor/bin/phpcs –standard=PHPdevsTV.xml [file]

Now to the fun part.

Adding Rules

Our XML file allows us to specify additional rules inside the file which can either reference another standard or sniffs.

There are two ways that we can add additional sniffs to our coding standard.

The first is that we can write our own. We don’t recommend this because it’s a lot of work. We looked into it and it’s its own article because there are a bunch of steps and a lot of background knowledge required. We’re not opposed to creating such a thing so let us know in the comments if you would like to see one.

The other is to find libraries of already developed rules or find some inside the existing rules shipped with phpcs to use.

Unfortunately, there isn’t a good source for finding a list of rules included in the phpcs core so it’s best to either find a library with a lot of them and build off of that or google “phpcs” plus whatever rule is needed. We should point out that rules are enforced by sniffs in PHP_CodeSniffer so that might be helpful for your searches.

To start we’re going to add a rule that requires us to have declare(strict_types=1) at the start of our file. For more information check out our article “Always Use Type Declarations For Function Parameters and Return Values in PHP” to see why this is important.

Thankfully, the CodeSniffer project comes with this built-in so we just need to add the rule to our XML file.

<?xml version="1.0"?>
<ruleset name="PHP Developers TV">
    <description>The PHP Developers TV coding standard.  PSR12 + Types</description>
    <rule ref="PSR12" />
    <rule ref="Generic.PHP.RequireStrictTypes" />
</ruleset>

Now when we run our standard against our setups we’ll see where we need to add declare(strict_types=1).

$ ./vendor/bin/phpcs --standard=PHPdevsTV.xml app/User.php

FILE: /var/www/app/User.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 1 | ERROR | Missing required strict_types declaration
----------------------------------------------------------------------

Time: 261ms; Memory: 8MB

Now it’s time to add in a third-party set of sniffs. We like the Slevomat Coding Standard library because it includes a LOT of sniffs. Maybe more than anyone could ever use.

To use them we need to install the library as a composer package.

composer require --dev "slevomat/coding-standard"

Then we can add the rules from the library we find the most helpful.

<?xml version="1.0"?>
<ruleset name="PHP Developers TV">
    <description>The PHP Developers TV coding standard.  PSR12 + Types</description>
    <rule ref="PSR12" />
    <rule ref="Generic.PHP.RequireStrictTypes" />
    <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses" />
    <rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint" />
    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint" />
    <rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint" />
</ruleset>

As an aside, these are the sniffs we think should be included in every project. The AlphabeticallySortedUses sniff makes sure all of the use statements are in alphabetical order which prevents merge conflicts and the others all force us to include types in various places which cuts down on the potential for bugs.

Removing Rules

There may come a situation where a team doesn’t like all the rules included in a standard or the team is attempting to implement a new standard but can’t follow all of the rules for some historical reason. In that case, we can just ignore the rule.

For example, we might be using underscores in our class names which is something the PSR12 standard doesn’t support (it’s CamelCase only). Our classes look something like the following.

<?php

declare(strict_types=1);

namespace App;

class Model_User
{
    //
}

When we run phpcs we’ll get an error.

$ ./vendor/bin/phpcs --standard=PHPdevsTV.xml app/User.php

FILE: /var/www/app/User.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 7 | ERROR | Class name "Model_User" is not in PascalCase format
----------------------------------------------------------------------

Time: 148ms; Memory: 8MB

It’s hard to know exactly what sniff is causing this error so to find it we can run phpcs with the -s command-line argument.

$./vendor/bin/phpcs --standard=PHPdevsTV.xml -s  app/User.php

FILE: /var/www/app/User.php
-------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
-------------------------------------------------------------------------------------------------------------
 7 | ERROR | Class name "Model_User" is not in PascalCase format
   |       | (Squiz.Classes.ValidClassName.NotCamelCaps)
-------------------------------------------------------------------------------------------------------------

Time: 299ms; Memory: 8MB

Now we can ignore that sniff in our standard.

<?xml version="1.0"?>
<ruleset name="PHP Developers TV">
    <description>The PHP Developers TV coding standard.  PSR12 + Types</description>
    <rule ref="PSR12">
        <exclude name="Squiz.Classes.ValidClassName.NotCamelCaps"/>
    </rule>
    <rule ref="Generic.PHP.RequireStrictTypes" />
    <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses" />
    <rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint" />
    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint" />
    <rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint" />
</ruleset>

Now when we run our standard against our code we’ll get no errors.

$ ./vendor/bin/phpcs --standard=PHPdevsTV.xml app/User.php
$ 

Ideally, we wouldn’t be doing this but sometimes it can’t be helped.

What You Need To Know

  • The PHP_CodeSniffer Project allows us to create our own standards
  • We can use existing or third-party sniffs
  • It’s possible to exclude sniffs