When writing plugins for WordPress, it’s important to keep the code compatible with supported versions of PHP. Even if we test for a minimum version and bail early on an old PHP version, our tests need to run on the lowest common denominator. PHPCS and the PHPCompatibility rules can help us ensure that.
One of the best, and worst, things about WordPress is its dedication to backwards compatibility. Best, because it can run on many different environments, even at the latest versions. Worst, because when we write plugins for WordPress, we get that backwards compatibility shackled to us as well. On its Requirements page, WordPress recommends a minimum of PHP version 7.2 at time of writing, but it also says:
If you are in a legacy environment where you only have older PHP or MySQL versions, WordPress also works with PHP 5.2.4+
Wow! 2018 is almost over, PHP 5.6 is coming off life support soon, but WordPress still runs on PHP 5.2.4 — which means at least some parts of any plugin written for WordPress today still need to run successfully on PHP 5.2.4 as well.
What many plugin writers are doing (myself included) is testing for a minimum PHP version that we’re happy to support, and exiting early with a notification to the site admin if we can’t run in their environment. What that minimum version is depends on the plugin writer, and maybe even on the plugin — for example, some of my plugins require version 7+, some require version 5.6+, some version 5.4+, and some still run fine on 5.2.4.
OK, so now the tricky bit: making sure that the code will actually run on all of those supported versions. The best way is, of course, to test all of the code in all of the versions. This is what continuous integration is really good at doing. Smashing Magazine covered this well with its article, An Introduction To Automated Testing Of WordPress Plugins With PHPUnit. Or we can cheat, and check our code with static analysis to see if there’s anything there that shouldn’t be there!
PHP_CodeSniffer can scan PHP code for coding standard violations — a really handy tool for keeping things neat, and even catching some bugs before we get stuck into testing. But the best news is that there is a custom rule set for checking PHP cross-version compatibility. There are simple instructions there for setting up the code sniffer to run across our code.
The easiest way to run the code sniffer is to create a custom configuration file called phpcs.xml, and detail what we want PHPCS to do. Here’s a simple example that runs the tests for minimum PHP 5.4 support; it will produce warnings for any code found that won’t run in PHP versions from 5.4 to current versions (7.2 at the moment).
<?xml version="1.0"?> <ruleset name="PHP_CodeSniffer"> <description>Some basic coding standards sniffs for this project.</description> <file>example-plugin.php</file> <file>includes</file> <file>views</file> <arg name="basepath" value="." /> <arg name="colors" /> <arg name="extensions" value="php" /> <arg name="tab-width" value="4" /> <config name="testVersion" value="5.4-"/> <rule ref="PHPCompatibility" /> <!-- and any other code sniffs we want to run --> </ruleset>
With that config file in place, calling phpcs from the command line will analyse the main plugin file, and any code inside two folders (includes and views).
But what about the code that needs to be compatible with PHP 5.2? Two tricks help us achieve that. First, we can create a second configuration file just for scanning for PHP 5.2 incompatibility; and second, we need to run another rule — PHP 5.4 made it possible to use short echo tags whether or not the server allowed short open tags, but that won’t work in PHP 5.3 or earlier.
So here’s a separate file that we can call phpcs-5.2.xml just for those PHP 5.2+ compatibility checks.
<?xml version="1.0"?> <ruleset name="PHP_CodeSniffer"> <description>PHP sniffs for PHP 5.2 compatibility.</description> <file>example-plugin.php</file> <file>views/requires-php.php</file> <file>includes/functions-global.php</file> <arg name="basepath" value="." /> <arg name="colors" /> <arg name="extensions" value="php" /> <arg name="tab-width" value="4" /> <config name="testVersion" value="5.2-"/> <rule ref="PHPCompatibility" /> <rule ref="Generic.PHP.DisallowShortOpenTag" /> </ruleset>
Great, now we can use static analysis to check for rule violations. But that’s not exactly the same as checking for code that runs without error. In fact, we can have a basic PHP error in our code that breaks PHP, and our code sniffs won’t tell us. What we can do easily enough, however, is run PHP from the command line to lint all of our files, ensuring at least that we haven’t left off a semicolon or something. On a Unix / Linux style system, this will do the trick:
php -l example-plugin.php; find includes views -name '*.php' -exec php -l '{}' \;
That command checks the main plugin file, then finds any PHP files in the includes and views folders and checks them too.
For completeness, here’s a sample composer.json file that can be used to install all of the necessary files, and run the tests.
{ "require-dev": { "phpcompatibility/php-compatibility": "*", "squizlabs/php_codesniffer": "*", "dealerdirect/phpcodesniffer-composer-installer": "*" }, "scripts": { "lint": "php -l example-plugin.php; find includes views -name '*.php' -exec php -l '{}' \\;", "sniff": "phpcs -ps && phpcs -ps --standard=phpcs-5.2.xml" }, "prefer-stable" : true }
From the command line, we can run composer lint
and then composer sniff
to check for silly mistakes and PHP compatibility problems, and then get down to the business of testing.