Let’s be honest. PHP has a very bad press when it comes to code quality. By reading this series of articles, you will learn how to improve the quality of your PHP code.

We could blame this on many reasons, but surely it’s not just because the PHP ecosystem lacks proper testing tools. In this article, I want to show you an easy set-up for the basic quality testing of your project. I will not go into details of any particular tool, but rather focus on setting the environment for testing.

There is a demonstration code for this article available on GitHub: https://github.com/mkosiedowski/php-testing-demo – please refer to it if you have any problems with examples from this article.

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. In my examples vendor’s binaries are installed to ./bin directory.

Build tool

We are 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

php composer.phar require --dev phing/phing 1 php composer.phar 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> 1 2 3 <? 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

The first thing you can do to improve your code quality at almost no cost is setting up static code analyzers. 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. Cool, huh?

Code style

Your 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), but still many teams have problems keeping the standards they’ve agreed on. It’s a miracle that we can automate this task with phpcs – PHP Code Sniffer.

You can install it by running

php composer.phar require --dev squizlabs/php_codesniffer 1 php composer.phar 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> 1 2 3 4 5 6 7 8 9 <? 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.

Example output of a wrongly formatted file:

MyProject > phpcs: FILE: /home/maciej/workspace/php-testing/src/Domain/Price.php ------------------------------------------------------------------------- FOUND 1 ERROR AFFECTING 1 LINE ------------------------------------------------------------------------- 28 | ERROR | Method name "Price::get_value" is not in camel caps format ------------------------------------------------------------------------- Time: 67ms; Memory: 6Mb 1 2 3 4 5 6 7 8 9 10 11 MyProject > phpcs: FILE: /home/maciej/workspace/php-testing/src/Domain/Price.php ------------------------------------------------------------------------- FOUND 1 ERROR AFFECTING 1 LINE ------------------------------------------------------------------------- 28 | ERROR | Method name "Price::get_value" is not in camel caps format ------------------------------------------------------------------------- Time: 67ms; Memory: 6Mb

No more losing time on checking coding standards during code-review, it will be automated from now on!

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. Here comes PHPCPD – PHP Copy/Paste Detector. You install it by running

php composer.phar require --dev sebastian/phpcpd 1 php composer.phar 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"/> 1 2 3 4 5 6 7 < 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" / >

Example output of a duplicated code check run on vendors directory:

phpcpd 4.0.0 by Sebastian Bergmann. Found 74 clones with 2929 duplicated lines in 97 files: - /home/maciej/workspace/php-testing/vendor/phpspec/phpspec/src/PhpSpec/Matcher/TriggerMatcher.php:81-102 /home/maciej/workspace/php-testing/vendor/phpspec/phpspec/src/PhpSpec/Matcher/TriggerMatcher.php:114-135 - /home/maciej/workspace/php-testing/vendor/squizlabs/php_codesniffer/src/Reports/Full.php:81-114 /home/maciej/workspace/php-testing/vendor/squizlabs/php_codesniffer/src/Reports/Code.php:162-195 (...) 1 2 3 4 5 6 7 8 9 10 11 12 phpcpd 4.0.0 by Sebastian Bergmann. Found 74 clones with 2929 duplicated lines in 97 files: - /home/maciej/workspace/php-testing/vendor/phpspec/phpspec/src/PhpSpec/Matcher/TriggerMatcher.php:81-102 /home/maciej/workspace/php-testing/vendor/phpspec/phpspec/src/PhpSpec/Matcher/TriggerMatcher.php:114-135 - /home/maciej/workspace/php-testing/vendor/squizlabs/php_codesniffer/src/Reports/Full.php:81-114 /home/maciej/workspace/php-testing/vendor/squizlabs/php_codesniffer/src/Reports/Code.php:162-195 (...)

Want really deep code analysis?

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

Installation is very easy – just install php-ast extension (in Ubuntu you can try to run sudo apt-get install php-ast ) and run:

php composer.phar require --dev phan/phan 1 php composer.phar 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/' ], ]; 1 2 3 4 5 6 7 8 9 10 11 < ? 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"/> 1 2 3 4 5 < 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 now run your code analysis and if you made a mistake (eg. declared wrong phpdoc type for a class property), you should see a message like this:

MyProject > phan: src/Domain/PriceComparator.php:17 PhanTypeMismatchProperty Assigning \Domain\PriceConverter to property but \Domain\PriceComparator::priceConverter is int src/Domain/PriceComparator.php:35 PhanNonClassMethodCall Call to method convert on non-class type int 1 2 3 4 MyProject > phan: src/Domain/PriceComparator.php:17 PhanTypeMismatchProperty Assigning \Domain\PriceConverter to property but \Domain\PriceComparator::priceConverter is int src/Domain/PriceComparator.php:35 PhanNonClassMethodCall Call to method convert on non-class type int

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

Summary of part I