Must have tools to improve the quality of your PHP Code
In this article, I want to showcase you a number of essential proper testing tools that will aid to improve the quality of your PHP code.
Prerequisites
I assume that you are familiar with PHP 7.1 syntax, you use Composer and follow PSR-4 for autoloading and PSR-1 & PSR-2 for coding standards.
Build Tool
I am going to use a few different testing tools, so it’s good to have something that would run them all with just one script. PHing provides us with an excellent solution to this problem. PHing is similar to Apache Ant and allows to easily automate tasks using XML configuration.
You install it by running
composer require --dev phing/phing
Then you create some basic build.xml file in your project’s root directory.
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="run">
</project>
In next steps we will add some targets to run by PHing.
Static Code Analysis
I recommend the first thing you can do to improve your code quality at almost no cost is setting up static code analysers. They will check your code for mistakes by just reading it without really running it. It’s like having a code-review done in a few seconds by a robot.
PHPCS (PHP Code Sniffer)
phpcs – PHP Code Sniffer: Code is much more maintainable when it is written using proper styling. Everybody knows that (if you don’t, you should at least start reading “Clean Code” by Robert C. Martin). PHP CodeSniffer is a very good tool to output the coding standards violations you have in your codebase. This tool automates this task.
You can install it by running
composer require --dev squizlabs/php_codesniffer
Then add a target for running it in build.xml. Your build.xml should now look like this:
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="run">
<target name="phpcs" description="Check code style with PHP_CodeSniffer">
<exec executable="bin/phpcs" passthru="true" checkreturn="true">
<arg line="--standard=PSR1,PSR2 -extensions=php src"/>
</exec>
</target>
<target name="run" depends="phpcs"/>
</project>
Now you can run ./bin/phing and phpcs will automatically check if you have any mistakes against PSR-1 and PSR-2 coding standards.
Many frameworks, like Symfony, define their own rules for code style and thanks to the community we can have those checked automatically as well. Eg. if you are using Symfony framework, check https://github.com/leaphub/phpcs-symfony2-standard for information on how to check Symfony’s standards with PHPCS. Two command line scripts can be used: phpcs to output the actual coding standards flaws and phpcbf which can fix some errors for you.
Example output of a wrongly formatted file:
As you can see phpcbf can fix automatically two errors for you by typing:
$ phpcbf src/Model/SuperModel.php
PHPCPD (Copy/Paste Detector)
PHPCPD – PHP Copy/Paste Detector. Duplicated code is bad, everybody knows it. Sometimes we create such code by mistake and we never notice it. Sometimes we do because we are lazy. It’s better to be equipped with a tool which will prompt about this at build time.
You install it by running
composer require --dev sebastian/phpcpd
Then you just add target to your build.xml:
<target name="phpcpd" description="Generate pmd-cpd.xml using PHPCPD">
<exec executable="bin/phpcpd" passthru="true">
<arg line="src"/>
</exec>
</target>
...
<target name="run" depends="phpcs,phpcpd"/>
PHPMD (PHP Mess Detector)
PMD will display the possible bugs and misuses of the language in your application.
Here how to do the magic:
$ phpmd vendor/ text cleancode
PHPMD will scan the directory and sub-directories of your project and output in plain text the errors found. You can as well create an html or xml output by replacing the text option in the command line above.
In this example we use the cleancode ruleset but you can obviously change it or create your own.
Example output of a duplicated code check run on vendors directory:
If you want to output the errors in a file? Easy:
Example output of a duplicated code check run on vendors directory:
$ phpmd src/ html cleancode --reportfile ~/phpmd.html
If you choose xml as output you will have more information regarding the rule set as following:
You can see for example the priority of the rules violated. You can then refine your result by using the --minimumpriority option for example.
In short: PHPMD is a great tool I really encourage you to use. It will detect a lot of potential problems in your code and will save you hours of debugging.
PHPStan (PHP Static Analysis Tool)
PHPStan is another tool to have in your toolbox. It aims? Output errors like a compiled language would display during compilation. It’s a good complement to PHPMD. If you are starting your project from scratch you should take a look at Phan – it’s a really powerful code analyser which will make your code beautiful. Check it out at https://github.com/phan/phan
You install it by running
composer require --dev phan/phan
Then create a config file .phan/config.php with content:
<?php
return [
'target_php_version' => '7.1',
'directory_list' => [
'src',
'vendor/symfony/console',
],
"exclude_analysis_directory_list" => [
'vendor/'
],
];
Also create phan target in your build.xml:
<target name="phan" description="Check code with phan">
<exec executable="bin/phan" passthru="true" checkreturn="true"/>
</target>
...
<target name="run" depends="phpcs,phpcpd,phan"/>
You can run it as follow:
$ phpstan analyse src/ --level=7
You can precise the strictness of PHPStan with the level option. The minimum is level 0, the maximum level 7.
To give you an idea here an example of output:
Phan is magical – it reads your whole code and performs multiple checks on it, including comparing phpdoc declarations with real usage of variables, methods, classes, etc. You can check the whole list of features on https://github.com/phan/phan#features
PhpCodeFixer
Deprecated functions are bad. They can create very weird bugs difficult to debug. This tool can help you detect them in your shiny application.
You can install it by running:
composer global require friendsofphp/php-cs-fixer
you can create a config file as explained here https://github.com/FriendsOfPHP/PHP-CS-Fixer
For example:
<?php
return PhpCsFixer\Config::create()
->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
]);
If you've created a config file, you have to configure its path in the plugin's settings.
In Menu -> Preferences -> Package Settings -> PHP CS Fixer -> Settings - user
{
"config": "/path/to/.phpcsfixer"
}
When using multiple projects with different configurations, it's possible to configure the path relative to the Sublime project folder:
{
"config": "${folder}/.php_cs.dist"
}
It's also possible to specify multiple config paths. In that case, the first readable file is used:
{
"config": [
"${file_path}/.php_cs",
"${file_path}/.php_cs.dist",
"${folder}/.php_cs",
"${folder}/.php_cs.dist",
"/path/to/.phpcsfixer"
]
}
You can precise the version of PHP you work with and your main codebase directory as follow:
$ phpcf --target 7.1 src
And here the usual possible output:
PHPUnit and the CRAP metric
Probably most common way of testing software is writing unit tests. They serve the purpose of testing particular units of the code, basing on the assumption that everything else works as expected.
In order to be able to write proper unit tests our code should follow some basic design rules. We should especially focus on SOLID principles.
- By implementing Single responsibility principle (our code should only take care about single part of functionality) we will make sure that during tests writing we will only focus on a small part of the project at once
- By using Liskov substitution principle and Dependency inversion principle our code won’t care if we inject mock dependencies, as long as they implement proper interfaces
In unit tests we really want to replace all dependant services with mock objects, so we only test one class at a time. But what are mocks? They are objects implementing the same interface as some other object, but behaving in a controlled way. For example, let’s say we are creating a price comparison service and we utilize another service to fetch current exchange rates. During testing our comparator we could use a mock object configured to return specific rates for specific currencies, so our test would neither be dependant nor call the real service.
You can it install by running:
composer require --dev phpunit/phpunit
Then, if you are using PHing configured in first part of the article, you add build target to build.xml:
<target name="test">
<phpunit printsummary="true" haltonfailure="true"><batchtest><fileset dir="./tests"><include name="*Test.php"/></fileset></batchtest></phpunit></target>
...
<target name="run" depends="phpcs,phpcpd,phan,phpunit"/>
PHPUnit can as well display a very interesting information: the CRAP metric.
CRAP uses the cyclomatic complexity with the code coverage of your code to display what might be the code difficult to change in your application.
More the CRAP index is high, more you code will be considered as “crappy”.
Indeed if your code has a great complexity but a low code coverage, you can expect it to cause unfortunate bugs each time you change it. You won’t even notice till your boss yells at you. Expect Dave, your colleague developer, trying to push you even more down for him to shine in the shadow of your shame.
To display the CRAP metrics, you need to produce a code coverage report:
$ phpunit phpunit --coverage-html ./tempFolder
This will create HTML files in the tempFolder directory. You can open the index.html in there and click on the dashboard link to finally contemplate the CRAP indicator.
dePHPend
Did you ever work on a project full of unnecessary dependencies, wondering how to understand this nightmare? Do you want to verify if your wonderful project is not mutating into a complex Big Ball of Mud?
dePHPend can help you grandly on that matter.
You can install it by running:
composer global require dephpend/dephpend:dev-master
You can use it as follow:
$ dephpend metrics src/
This output will then appear magically:
As you can see, dePHPend will output the number of Afferent Coupling, the number of Efferent Coupling and display an instability indicator based on them.
In clear:
- No class depend on the class App\Kernel
- The class App\Kernel depends on five other classes
The instability score is high here: this class couple other classes together but is never used!
You can as well output plain text or UML for example.
Configuring PHPStorm
Here are some handly links to Jetbrain’s documentation: