Delivering high quality, understandable and future proof code

As a developer I strive to deliver high quality code, that is understandable for other developers and is as future proof as it can be. For me this is an ongoing learning process in the way code is written and tools that can be used to assist me. Many different sources can be found with opinionated instructions how to write "perfect" code. It basically comes down to using a well configured integrated development environment (IDE), not being shy in providing supportive comments that explains the code flow and structure the implemented functionality in logical methods and classes. But as a lazy programmer, I'm also interested in extending my development stack with tools that result in more qualitative code. When I can run these programs with one push of a button (better yet, run them automatically), it makes it easy to create quality reports and apply improvement changes immediately.

I've incorporated three different tools in my development stack, which are applied regularly and look at the code from different perspectives. One improves readability of code by ensuring that Drupal's coding conventions are adhered, thereby keeping code uniform throughout the complete project (core, contributed and custom code) and between all developers (also the ones in the future). The second tool uses static code analysis to locate flaws and make suggestions to improve the code. Finally, the third tool applies unit testing to ensure the code shows the intended behavior.

Required software packages

Assuming you're using Composer to manage Drupal's dependencies, the command line instructions below can be used to install all three code quality tools (for our development environment):

composer require --dev drupal/core-dev
composer require --dev mglaman/drupal-check

Note that the drupal/core-dev provides both the drupal/coder and phpunit/phpunit packages. However, depending on your setup and/or preferences you might want to install these tools globally. In that case, I can highly recommend to take a look at consolidation/cgr.

Enforcing Drupal's coding standards

The drupal/coder project provides the Drupal coding standards definitions, which are used as the configuration input for the scripts offered by squizlabs/PHP_CodeSniffer to detect whether code style violations are made. Besides detecting violations, this tool is also able to automatically provide fixes. The commands to invoke the scripts and check the code of our custom modules is:

./vendor/bin/phpcs --runtime-set installed_paths vendor/drupal/coder/coder_sniffer --standard=Drupal --extensions='php,module,inc,install,test,profile,theme,css,info,txt,md,yml' web/modules/custom
./vendor/bin/phpcbf --runtime-set installed_paths vendor/drupal/coder/coder_sniffer --standard=Drupal --extensions='php,module,inc,install,test,profile,theme,css,info,txt,md,yml' web/modules/custom
./vendor/bin/phpcs --runtime-set installed_paths vendor/drupal/coder/coder_sniffer --standard=DrupalPractice --extensions='php,module,inc,install,test,profile,theme,css,info,txt,md,yml' web/modules/custom

The first command verifies whether the source code meets the Drupal coding standards, reports any violations and indicates which one can be fixed automatically. Run the second command to apply these automatic fixes. To avoid common mistakes and be informed about best practices, such as using Dependency Injection to load services, run the third command.

Using static code analysis to improve code

This tool is based/build on PHPStan and spots errors without running the code. It analysis your code and reports errors such as nonexistent classes and usage of deprecated code. To analyse your custom modules, simply run:

./vendor/bin/drupal-check web/modules/custom

Applying automated tests using PHPUnit

In order to guarantee code keeps behaving as intended by the developer, unit tests should be supplied. When code is changed, due to refactoring, extending functionality or simply because libraries are updated, running unit tests can give you a (relative) quick feedback whether errors occur. Another advantage of writing unit tests, is that they provide in some way documentation about the implemented code. In this blog post we focus only on how to run the unit tests easily, the topic of how write proper unit tests and all possible feature that exists is out of scope.

Drupal (core) ships with a PHPUnit configuration file, i.e., web/core/phpunit.xml.dist, which can be used to set the correct options for running tests of your custom modules. Although this configuration file introduces some overhead, the main advantage is that it's kept up-to-date by Drupal (which controls the PHPUnit version). In order to correctly execute all different types of tests (e.g., based on BrowserTestBase), some parameters have to be set. The most easiest way to do this is by setting the following environment variables, which can be auto-loaded using vlucas/phpdotenv:

SIMPLETEST_BASE_URL=http://localhost
SIMPLETEST_DB=mysql://username:password@localhost/databasename#table_prefix
BROWSERTEST_OUTPUT_DIRECTORY=/path/to/webroot/sites/simpletest/browser_output

The command to start the unit tests then becomes:

./vendor/bin/phpunit --configuration web/core web/modules/custom
Category
Drupal
Composer
Quality Assurance
Security
Coding Standards
Static analysis
PHPStan
Unit testing
PHPUnit