During the development of code (modules, libraries, themes), It's important that we integrate objective tools that measure the state of the code and provide you with information to measure the quality of your code so that you're able to detect and prevent problems derived from low-quality code: duplicated functions, excessively complex methods, developer dependent coding style... Ideally, we must incorporate these tools at the beginning of development but, if it hasn't been done yet, it must be done as soon as possible, write down the initial reports and work towards improving the amount of detected problems. It's recommended to automate these tests in your continuous integration system so they are run every time a new version of your software is uploaded (pull to the repository) so you can keep a record.
Libraries
There are a lot of tools to improve/audit the quality of the code, from the tool set that we use on a daily basis in Metadrop we can feature:
PHPCS / Drupal Coder
Extension of PHP_CodeSniffer (phpcs) used to detect code that does not follow Drupal standards in both sintax level and good code practices, and also security errors when building database queries. When all developers use the same coding style, it encourages a more uniform code and makes code revision easier.
https://packagist.org/packages/drupal/coder
PHPMD
PHP extension for detecting different problems such as excessively complex code (cyclomatic complexity) so we can easily detect conflicting parts that can become hotspots for errors and hampering when updating in the near future.
https://packagist.org/packages/phpmd/phpmd
PHPCPD
PHP extention for detecting duplicated code. It's very common to find in projects with very short deadlines code from functions that is not properly abstraced or even algorythms/procedures from indepenent modules that are exactly identical as a result of poor communication between team members. It's elementary to detect this factor and to solve it since it will cause inconsistencies and extra work load when updating/refactoring any component.
https://packagist.org/packages/sebastian/phpcpd
PHPLOC
PHP extension used to quickly measure size and analyse the structure of a PHP project.
https://packagist.org/phploc/phploc
Integration with Drupal
Integration with Drupal or any other PHP software is easy. In our case, the directory tree is as follows:
docroot/ <- código de drupal
build.xml <- fichero integración phing
logs_codereview/.. <- resultados de tests
...
Meaning, we have the project's directory with a docroot directory containing Drupal's code, a build.xml file with settings for integration with phing, and a logs_codereview directory where the analisys' results are placed.
PHING
For automating these processes we use PHING, if you know ANT, it's the same concept but for PHP.
Phing is a tool for building or compiling projects and is based on Apache Ant. It can do anything that's possible with a traditional compiling system like GNU make and its use of simple compilation XML files and PHP "task" extendable classes makes it so it's a highly flexible and easy to use compilation framework.
You can view a list of all the tools integrated with Phing on https://www.phing.info/docs/guide/trunk/ and remember that you can alway extend Phing or run a certain command with <exec> so you will always be free to do as you like.
Installing libraries on PHING
Phing can be easily installed with Composer:
# We create directoty, in OPT for example:
mkdir /opt/phing && cd /opt/phing
# We install libraries (composer):
composer require phing/phing
composer require drupal/coder
composer require sebastian/phpcpd
composer require phpmd/phpmd
composer require pdepend/pdepend
composer require phploc/phploc
composer require phpunit/phpunit
composer require phpunit/php-code-coverage
# Symlink in /usr/bin
ln -s /opt/phing/vendor/bin/phing /usr/local/bin/phing
# We check version:
phing -version
# We add Drupal coding standard to phpcs:
phpcs --config-set installed_paths /opt/phing/vendor/drupal/coder/coder_sniffer
Build XML
In the build.xml file we define which files will be processed, in this case, they're all inside the docroot directory (having build.xml in the parent directory) and all the processes (targets) that will be executed.
<?xml version="1.0"?>
<project name="code-review" default="phpcs">
<!- Ficheros a auditar ----->
<fileset dir="docroot" id="targetfiles">
<include name="sites/all/modules/custom/**/*.php" />
<include name="sites/all/modules/custom/**/*.module" />
<include name="sites/all/modules/custom/**/*.inc" />
<include name="sites/all/modules/custom/**/*.js" />
<include name="sites/all/modules/custom/**/*.info" />
<include name="sites/all/themes/custom/**/*.php" />
<include name="sites/all/themes/custom/**/*.module" />
<include name="sites/all/themes/custom/**/*.inc" />
<include name="sites/all/themes/custom/**/*.js" />
<include name="sites/all/themes/custom/**/*.info" />
<exclude name="sites/all/modules/custom/md_ckeditor/plugins/ckeditor/youtube/**/*.js" />
<exclude name="sites/all/modules/custom/md_ckeditor/plugins/ckeditor/archery_link/plugin.js" />
</fileset>
<!- PHPCS ----->
<target name="phpcs" description="Run phpcs">
<phpcodesniffer standard="Drupal" format="checkstyle" allowedFileExtensions="php,inc,module,install,profile">
<fileset refid="targetfiles" />
<formatter type="checkstyle" outfile="logs_codereview/checkstyle-result.xml" />
<config name="installed_paths" value="/opt/phing/vendor/drupal/coder/coder_sniffer"/>
</phpcodesniffer>
</target>
<!- PHPMD ----->
<target name="phpmd" description="Run phpmd">
<phpmd>
<fileset refid="targetfiles" />
<formatter type="xml" outfile="logs_codereview/pmd.xml" />
</phpmd>
</target>
<!- PHPCPC ----->
<target name="phpcpd" description="Run phpcpd">
<phpcpd>
<fileset refid="targetfiles" />
<formatter type="pmd" outfile="logs_codereview/cpd.xml"/>
</phpcpd>
</target>
<!- PHPLOC ----->
<target name="phploc" description="Measures the size of the project and count it's tests">
<phploc countTests="true" reportType="txt" reportName="loc" reportDirectory="logs_codereview">
<fileset refid="targetfiles" />
</phploc>
</target>
</project>
PHING Execution
When executing Phing, we indicate the build.xml and the targets that we want to execute.
phing -f build.xml phpcs phpmc phpcpc phploc
Phing output
In our example, we have dumped the output in the logs_codereview/ directory which will be later analyzed by the continuous integration system, Jenkins in our case.
$ ls -l logs_codereview/
checkstyle-result.xml
cpd.xml
loc.txt
pmd.xml
Conclusions
Thanks to Phing, integrating different static code analysis tools is a simple, easy to implement and highly recommended task so we can take one more step towards ensuring quality in the code.
Do you know of any other tools? Share them in the comments below!