• Reading time ~ 5 min
  • 03.03.2023

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 phpcs
cd phpcs
git init
composer 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": "[email protected]"
        }
    ],
    "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 Bitpress
touch 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 -i
The installed coding standards are
MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend,
Bitpress and VariableAnalysis

The --show-info flag can show us more information:

vendor/bin/phpcs --config-show
Using 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/phpcs
ERROR: 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.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

ABOUT

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

About author CrazyBoy49z
WORK EXPERIENCE
Contact
Ukraine, Lutsk
+380979856297