Linting WordPress Code Using Composer and PHP_CodeSniffer

Linting is the process of using software to analyze static code for programming errors and bugs, code smells, and stylistic errors. Code smells are simply characteristics or constructs that may present as errors during program execution. Generically, the term lint (or linter) refers to a class of programs that perform these functions prior to or as part of compiling, however, it originates from a specific program named ‘lint’ used for evaluating C code.

This guide is focused on linting PHP code against the WordPress Coding Standards using PHP_CodeSniffer (phpcs and phpcbf). For those who want to publish plugins or themes as part of the official WordPress.org directory, linting your code against the WordPress Coding Standards significantly reduces the repeated back-and-forth issue correction cycle in the overall approval process.

Although I am using a Windows machine to install and run the development environment, all of the referenced applications are available on most major operating systems and the listed commands are not operating system specific.

Step 1 – Install PHP

PHP is a prerequisite for installing Composer in the next step. While you can download and install PHP as a standalone product, I prefer to install XAMPP which is a free Apache distribution containing PHP, MySQL (MariaDB), and several other products. The primary advantage of using XAMPP is that it creates an all-in-one local development and testing environment to install and execute WordPress and custom code. For the purposes of this guide, the subsequent steps only require access to PHP so either standalone PHP or XAMPP is sufficient. If you do install XAMPP, it does not need to be actively running for the other steps to work properly.

In this example, XAMPP is installed in the c:\xampp\ directory and PHP is located in the c:\xampp\php\ directory.

After installing either option, verify that the PHP directory is included in the %PATH% variable.

XAMPP – Control Panel
XAMPP – Control Panel

Step 2 – Install Composer

Composer is an application-level dependency management application for PHP. In other words, Composer handles the installation and update of your PHP project dependencies and libraries. Composer will be used to install PHP_CodeSniffer, WordPress Coding Standards, and PHPCompatibilityWP in subsequent steps.

To get started, download and install Composer. As part of the installation process, ensure Composer is able to locate the command line PHP (php.exe) program installed during Step 1. Since I used XAMPP, Composer is configured to use php.exe located in the c:\xampp\php\ directory.

After installing Composer, verify that the Composer\vendor\bin directory is included in the %PATH% variable.

Composer – Setup Settings Check PHP Path
Composer – Setup Settings Check PHP Path

For those with Composer already installed, execute the following command to update the Composer application itself as well as any dependencies.

composer self-update && composer global update

These commands result in the following output (or similar). Since I executed these commands on a fresh install of Composer, there is nothing to update. No dependencies have been installed at this point so the composer.json file does not yet exist.

You are already using the latest available Composer version 2.4.3 (stable channel).
Changed current directory to C:/Users/john/AppData/Roaming/Composer
Composer could not find a composer.json file in C:\Users\john\AppData\Roaming\Composer
To initialize a project, please create a composer.json file. See https://getcomposer.org/basic-usage
Composer – Update Command Result
Composer – Update Command Result

When the update commands are executed after installing dependencies, the output will be similar to the following.

You are already using the latest available Composer version 2.4.3 (stable channel).
Changed current directory to C:/Users/john/AppData/Roaming/Composer
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files
No security vulnerability advisories found
Composer – Update With Dependencies Command Result
Composer – Update With Dependencies Command Result

NOTE: Even though we have not installed any dependencies yet, the following commands are provided for reference if you ever need to remove the dependencies installed as part of this guide.

composer global remove dealerdirect/phpcodesniffer-composer-installer
composer global remove wp-coding-standards/wpcs
composer global remove phpcompatibility/phpcompatibility-wp

Step 3 – Install PHP_CodeSniffer

Next, we will install PHP_CodeSniffer through Composer. PHP_CodeSniffer includes two scripts. The phpcs script performs the linting function for PHP, JavaScript, and CSS files using the configured coding standards while phpcbf (PHP Code Beautifier and Fixer) attempts to automatically correct identified coding standard inconsistencies, i.e., it formats the code to look pretty.

composer global require --dev dealerdirect/phpcodesniffer-composer-installer

If prompted “Do you trust “dealerdirect/phpcodesniffer-composer-installer” to execute code and wish to enable it now?“, then respond ‘y‘ to proceed.

Changed current directory to C:/Users/john/AppData/Roaming/Composer
Info from https://repo.packagist.org: #StandWithUkraine
Using version ^0.7.2 for dealerdirect/phpcodesniffer-composer-installer
./composer.json has been created
Running composer update dealerdirect/phpcodesniffer-composer-installer
Loading composer repositories with package information
Updating dependencies
Lock file operations: 2 installs, 0 updates, 0 removals
  - Locking dealerdirect/phpcodesniffer-composer-installer (v0.7.2)
  - Locking squizlabs/php_codesniffer (3.7.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
  - Installing squizlabs/php_codesniffer (3.7.1): Extracting archive
dealerdirect/phpcodesniffer-composer-installer (installed globally) contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins
Do you trust "dealerdirect/phpcodesniffer-composer-installer" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?] y
  - Installing dealerdirect/phpcodesniffer-composer-installer (v0.7.2): Extracting archive
Generating autoload files
No security vulnerability advisories found
Composer – Install PHP_CodeSniffer Command Result
Composer – Install PHP_CodeSniffer Command Result

The phpcs -i command displays the installed coding standards.

phpcs -i
The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz and Zend
PHP_CodeSniffer – Coding Standards
PHP_CodeSniffer – Coding Standards

Step 4 – Install WordPress Coding Standards

Now we can install the WordPress Coding Standards ruleset which is used to validate code developed for WordPress against the official WordPress Coding Standards.

Execute the following command.

composer global require --dev wp-coding-standards/wpcs
Changed current directory to C:/Users/john/AppData/Roaming/Composer
Using version ^2.3 for wp-coding-standards/wpcs
./composer.json has been updated
Running composer update wp-coding-standards/wpcs
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking wp-coding-standards/wpcs (2.3.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing wp-coding-standards/wpcs (2.3.0): Extracting archive
Generating autoload files
PHP CodeSniffer Config installed_paths set to ../../wp-coding-standards/wpcs
No security vulnerability advisories found
Composer – Install WP Coding Standards Command Result
Composer – Install WP Coding Standards Command Result

The phpcs -i command displays the installed coding standards which now include WordPress, WordPress-Core, WordPress-Docs and WordPress-Extra.

phpcs -i
The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend, WordPress, WordPress-Core, WordPress-Docs and WordPress-Extra
PHP_CodeSniffer – WP Coding Standards
PHP_CodeSniffer – WP Coding Standards

Step 5 – Install PHPCompatibilityWP

The final dependency to install is PHPCompatibilityWP which is used to check for PHP cross-version compatibility issues.

composer global require --dev phpcompatibility/phpcompatibility-wp
Changed current directory to C:/Users/john/AppData/Roaming/Composer
Info from https://repo.packagist.org: #StandWithUkraine
Using version ^2.1 for phpcompatibility/phpcompatibility-wp
./composer.json has been updated
Running composer update phpcompatibility/phpcompatibility-wp
Loading composer repositories with package information
Updating dependencies
Lock file operations: 3 installs, 0 updates, 0 removals
  - Locking phpcompatibility/php-compatibility (9.3.5)
  - Locking phpcompatibility/phpcompatibility-paragonie (1.3.1)
  - Locking phpcompatibility/phpcompatibility-wp (2.1.3)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Installing phpcompatibility/php-compatibility (9.3.5): Extracting archive
  - Installing phpcompatibility/phpcompatibility-paragonie (1.3.1): Extracting archive
  - Installing phpcompatibility/phpcompatibility-wp (2.1.3): Extracting archive
3 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
PHP CodeSniffer Config installed_paths set to ../../phpcompatibility/php-compatibility,../../phpcompatibility/phpcompatibility-paragonie,../../phpcompatibility/phpcompatibility-wp,../../wp-coding-standards/wpcs
No security vulnerability advisories found
Composer – Install PHPCompatibilityWP Command Result
Composer – Install PHPCompatibilityWP Command Result

The phpcs -i command displays the installed coding standards which now include PHPCompatibility, PHPCompatibilityParagonieRandomCompat, PHPCompatibilityParagonieSodiumCompat, and PHPCompatibilityWP.

phpcs -i
The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend, PHPCompatibility, PHPCompatibilityParagonieRandomCompat, PHPCompatibilityParagonieSodiumCompat, PHPCompatibilityWP, WordPress, WordPress-Core, WordPress-Docs and WordPress-Extra
PHP_CodeSniffer – PHPCompatabilityWP Coding Standards
PHP_CodeSniffer – PHPCompatabilityWP Coding Standards

Step 6 – PHP_CodeSniffer Custom Ruleset

All of the installation steps are complete and we can finally begin linting code. First, we need to configure PHP_CodeSniffer with the appropriate flags and rules for the project. To do this, we create a file named phpcs.xml in the root folder of a PHP project. A sample phpcs.xml file is provided below which should be customized for your specific project needs. In this example configuration, PHP_CodeSniffer evaluates the project’s PHP files against the WordPress Coding Standards ruleset with a few rule exclusions. Yes, I realize I have mostly excluded Commenting rules so shame on me. The configuration file also instructs the sniffer to evaluate the code for PHP compatibility against PHP version 7.4 or later. Again, adjust the configuration file to meet your needs.

<?xml version="1.0"?>
<ruleset name="DALESANDRO.NET Coding Standards">
  <description>Custom coding standards configuration for WordPress plugin development.</description>

  <!--
    PHPCS flags:
    n:  Do not print warnings.
    s:  Show sniff codes in all reports.
    p:  Show progress of the run.
  -->
  <arg value="nsp"/>

  <!-- Check all files in the current local directory and all subdirectories. -->
  <file>.</file>

  <!-- Check files with PHP extensions only. -->
  <arg name="extensions" value="php"/>

  <!-- Use colors in output. -->
  <arg value="-colors"/>

  <!-- Include WordPress Coding Standards. -->
  <rule ref="WordPress">
    <!-- Exclude the following rules. -->
    <exclude name="Squiz.Commenting.ClassComment.Missing"/>
    <exclude name="Squiz.Commenting.FileComment.Missing"/>
    <exclude name="Squiz.Commenting.FileComment.MissingPackageTag"/>
    <exclude name="Squiz.Commenting.FunctionComment.Missing"/>
    <exclude name="Squiz.Commenting.VariableComment.Missing"/>
    <exclude name="WordPress.WP.DeprecatedFunctions.sanitize_urlFound"/>
    <exclude name="WordPress.WP.EnqueuedResourceParameters.MissingVersion"/>
    <exclude name="WordPress.Security.NonceVerification.Missing"/>
  </rule>

  <rule ref="PHPCompatibilityWP"/>
  <config name="testVersion" value="7.4-"/>
  <config name="minimum_supported_wp_version" value="5.9"/>
</ruleset>

Results

As a test, I have created a project directory named c:\test. In this directory, phpcs.xml is located at the root level with a subdirectory named classes. A PHP file named class-test.php is found within the classes subdirectory.

c:\test\
 ├── phpcs.xml
 └── classes/
     └── class-test.php

phpcs Results

At a command prompt, executing phpcs . from within the parent project directory results in the following sample output. We can see that the code is evaluated against the WordPress Coding Standards based on the various WordPress.DB and WordPress.Security rules flagged as errors. The output includes color as directed in the configuration file.

phpcs .
phpcs – Example Output
phpcs – Example Output

phpcbf Results

As I mentioned earlier in this guide, PHP_CodeSniffer includes a second script named phpcbf. This script is used to automatically fix code formatting and style inconsistencies. I have created another sample file named class-test2.php which is formatted by someone who both dislikes indentation and does not have a functioning space bar.

phpcbf – Code Format (Before Execution)
phpcbf – Code Format (Before Execution)

At a command prompt, executing phpcbf class-test2.php results in the code being formatted according to conventional coding style guidelines.

phpcbf class-test2.php
phpcbf – Code Format (After Execution)
phpcbf – Code Format (After Execution)