In my last article for Torque, I wrote about why I think that for PHP code following the coding standards set by the PHP community make more sense than those set for WordPress core. These standards are based on serving the needs of an existing legacy code base, limited to language features that are 10 years old or later. When you start a new WordPress plugin, site, or app, there is no reason for you to make the same decision, when you’re circumstances are totally different.
As a follow up to that article, I’d like to show you, technically how you can implement and adhere to a set standard, such as psr-2 or even the WordPress coding standards. You can do this using a tool called PHP Code Sniffer, in this article I’ll show you how to use this and other tools to test that your code follows these rules and fix most deviations automatically. Also, we’ll lint the code too.
Why Do Coding Standards Matter?
Normally when people write about coding standards they say it’s important because it makes the code more readable and reduces friction of having multiple developers on a project. That’s all true, I agree. But that’s not why I’m super into having a code style enforced and automatically fixed in my code.
I think not worrying too hard about code style allows me to think about tying letters on the screen less and focus more on the actual code. Writing code is not easy and increased focus helps. I think thinking less about things that I don’t need to think about is a really important. For example, I could think about how an object representation of an HTTP request could be designed. There are a lot of totally valid ways to solve the problem. Instead, I’m going to follow the pattern established by the PSR-7 standard like everyone else does. It’s not just following standards for standards-sake, it pays off when a choice of framework for running your code doesn’t affect the code you’re writing at all.
And I’m not arguing for rewriting WordPress so it’s built to be interoperable with PHP frameworks. The WordPress REST API and WP-CLI allow us to paper over a lot of those issues. Instead, we’re talking about your own custom site code or your plugins.
Sniffers
PHP Code Sniffer looks for code that smells bad. The term “smells bad” refers to code smells. Code smells are patterns in code that when present likely mean a common problem is happening in the code. It doesn’t mean it’s not working, but that it’s not optimal. For example, a class that is very long, smells bad. It doesn’t mean it’s bad, but thinking about why it’s so long might lead to improving code based on the common solutions to the problems this smell suggests.
Code Sniffer lets us “sniff out” bad smells before they get in the code base. Sniffing violations of code standards is only one type of smell you could detect with this tool. Let’s look at enforcing coding standards.
For this article, I’ll be using Composer to manage installing and running the sniffer and the other tools. While you could install them using PEAR and run them from the CLI directly, using Composer is easier. Also, it ensures you’re running the version of the sniffer and other tools such as PHPUnit called for by the project, not a system version.
Here is the command to add code sniffer to your project using Composer:
composer require squizlabs/php_codesniffer --dev
Notice that I used the –dev flag. That adds it to Composer’s “require-dev” section. That way you can build your code for production or release without this dependency.
Now in the Composer file’s scripts section add a script called “sniffs”. For each directory to sniff, we want to run the command “phpcs –standard=PSR2 <dir>”, so if we want to sniff directories called src and test, we would use this for our scripts:
{ "scripts" : { "sniffs" : "phpcs --standard=PSR2 src/ && phpcs --standard=PSR2 tests/" } }
Now whenever we run the is script — use “composer sniffs”, PHP code sniffer will report any violations of the psr-2 standard. That’s cool, and you can fix them manually, but let’s see how much we can get fixed automatically.
By the way, there is a WordPress coding standards sniffer for PHP Code Sniffer. You can get it here if you want to go that way.
Fixers
Obsessively following a standard for how to arrange code on a page is not a great investment of time. Instead, I set my IDE to follow the standard, and use PHP Coding Standards Fixer to automatically fix most deviations.
In PhpStorm, you can go to the “Code Style” section of the settings. In the PHP settings, there is an option called “Set From” on the right. You will see PSR-2, and WordPress as options. I recommend choosing one. Then the automatic formatting of the code will match the standard and the clean up code command will as well.
compsoer require friendsofphp/php-cs-fixer --dev
This cool little tool fixes most problems with your code. If it can’t it will explain the problem to you in a way that I find makes it easy to fix.
Let’s add one more script to the composer file, this one for lints:
{ "scripts" : { "sniffs" : "phpcs --standard=PSR2 src/ && phpcs --standard=PSR2 tests/", "fixes" : "php-cs-fixer fix src/ && php-cs-fixer fix tests/" } }
Notice that I’m putting all of the code for each task in one script. Later, we’ll add one script to call the others in the right order.
Linters
While a sniffer looks for code that is likely a long-term issue of maintainability, linters look for errors. It’s important to note that neither runs your code. They all just analyze the text files your code is in. A linter doesn’t substitute for tests. But I like to run one in between my fixer and my tests so that if I missed something bad, or the fixer caused an error to happen, it will get flagged without running the tests, which would have failed anyway.
There are a few PHP linters out there. I like Parallel Lint because it’s faster to process multiple files at once, then one at a time. The name describes the advantage. Good show. Let’s install that:
composer require jakub-onderka/php-parallel-lint
The command for the linter is “parallel-lint <dir>” so let’s update composer’s scripts for a lints command for src and tests:
{ "scripts" : { "sniffs" : "phpcs --standard=PSR2 src/ && phpcs --standard=PSR2 tests/", "fixes" : "php-cs-fixer fix src/ && php-cs-fixer fix tests/", "lints" : "parallel-lint ./src --blame --exclude vendor && parallel-lint ./tests --blame --exclude vendor" } }
Pulling It All Together
Now that we have those three scripts, we can run them all, followed by phpunit tests with one command. I think it’s important to run the lints and fixes before the tests. That way we’re also testings the automated fixes. It’s fairly doubtful that this process would cause errors, but it’s easy to avoid by running things in the right order.
More importantly, if the code smells bad, it’s not ready to be tested. So, my order I like to run things, narratively is fix code style, lint the code for obvious errors, check for remaining code style violations and only once the code is arranged properly — remember none of the code actually runs during lints and fixed — test the code.
Here’s the final script section with a new script called “tests”. This script just calls the other scripts, but in the right order.
{ "scripts" : { "tests" : "composer fixes && composer sniffs && composer lints && phpunit --coverage-clover coverage/clover.xml", "sniffs" : "phpcs --standard=PSR2 src/ && phpcs --standard=PSR2 tests/", "fixes" : "php-cs-fixer fix src/ && php-cs-fixer fix tests/", "lints" : "parallel-lint ./src --blame --exclude vendor && parallel-lint ./tests --blame --exclude vendor" } }
I like this modularity, as it means I can run each process separately, but most of the time, I just use the “composer tests” command.
If you’re curious, here’s a complete composer.json for a WordPress plugin that uses the tools I’ve described in this article. I encourage you to work these tools into your code, I know they’ve helped me a lot. I’m excited to find new sniffs or even make my own sniffs. This will help me and my team write better and more consistent code. I wish better more consistent code, as well as other positive feelings and emotions for you as well.
1 Comment