PHPQA docker image as swiss army knife for PHP static testing


This article describes how to use and customize jakzal/phpqa to use it for PHP code testing both on local machines and CI process.


Jakub Zalas made a really nice docker image that consists of plenty of PHP testing tools to group them in a single docker image, instead of littering your local system with different tools, conflicting dependencies etc. This article will show, how it can be done to make them a convenient replacement for traditionally installed tools and how to customize it.

What jakzal/phpqa is

It's an image that includes a massive (https://github.com/jakzal/phpqa#available-tools) amount of tools related to PHP code verification. To install it, simply pull the image out of docker hub:

docker pull jakzal/phpqa

This will download image out of docker's repository. To run your selection of choice, you can use following command  (example from project's github page).

docker run --init -it --rm -v "$(pwd):/project" -v "$(pwd)/tmp-phpqa:/tmp" -w /project jakzal/phpqa phpstan analyse src

This command will mount your local directory in the docker container, run `phpstan analyse src` command in the container's command and output results. After it, it will remove the container used for the execution our of the system. Pretty neat, but it's a bit problematic to remember it?


How to use it more convenient?

Example from the previous paragraph works but may be complicated in the longer run. I wanted to replace small CLI tools I need to install (using composer, phar files, phive, etc) and execute them just like they were installed traditionally. I decided to use the small wrapper script. First, as I needed to have a shared alias for all my tools (eg. phpmd, phpcs, etc), I decided to use a small trick. First I prepared script `/usr/local/bin/corephpqa.sh` with following contents:


command='docker run --init -i --rm -v '$HOME:$HOME' -v '/$HOME/tmp:/$HOME/tmp' -w '"$(pwd)"' jakzal/phpqa:7.4-alpine'


What does it do? It simply prepares the command variable. My docker execution will pass standard input (`-i`) to command, docker will remove container after execution (`--rm`) and will mount my home directory to the container (`-v $HOME:$HOME`). As working directory in container it will use my working directory (I assume it's subdirectory of `$HOME`).

And now,  I created small script, at `/usr/local/bin/phpcs`:

source corephpqa.sh
$command phpcs $@

It simply includes the previous script and set command variable and executes it passing all user arguments to it. Thanks to that, executing this command will provide same results as it would be installed directly on your machine, without docker.

 ✘⚙ jsobiecki@pecet  ~/bin  phpcs ~/workspace/ratioweb/wosm/example/web/index.php

FILE: /home/jsobiecki/workspace/ratioweb/example/example/web/index.php
  3 | ERROR   | [ ] Missing short description in doc comment
  9 | WARNING | [ ] PHP version not specified
  9 | ERROR   | [ ] Missing @category tag in file comment
  9 | ERROR   | [ ] Missing @package tag in file comment
  9 | ERROR   | [ ] Missing @author tag in file comment
  9 | ERROR   | [ ] Missing @license tag in file comment
  9 | ERROR   | [ ] Missing @link tag in file comment
 14 | ERROR   | [x] File is being conditionally included; use
    |         |     "include_once" instead

Time: 231ms; Memory: 6MB

Thanks to that, for each tool you need from `phpqa` image, you can create such a simple script and provide tool on your local machine, without hassling with a massive amount of dependency, conflicts etc.

How to customize it?

Jakub Zalas made great job, but sometimes his image needs some tweaking, that may not be suitable for a traditional PR on github. How to do that? Simply use your own `Dockerfile` and override upstream image.

Let say I want to install Drupal code standard (original image is missing that) and change phpcbf command so it may use newly installed standard.

That  image would do the job:


FROM jakzal/phpqa:${BASE_IMAGE}

RUN composer global require drupal/coder --prefer-source
RUN composer global require slevomat/coding-standard
RUN phpcs --config-set installed_paths /tools/.composer/vendor/drupal/coder/coder_sniffer,\
RUN rm /tools/phpcbf && ln -s /tools/.composer/vendor-bin/phpcs/vendor/bin/phpcbf /tools/phpcbf

Having this Dockerfile in current directory,

docker build -t phpqa-local --build-arg BASE_IMAGE=7.4-alpine --build-arg PHP_VERSION=7.4 .

Will buld custom image phpqa-local, that has Drupal standard available. Simply update `/usr/local/bin/corephpqa.sh` and use proper image name there.

How to use it in your CI process?

Here, I don't have a simple answer, because it depends on your toolkit. In our organisation, we depend on gitlab-ci. For a tool like this, that depends on yaml configuration, the test script would look like this:

  image: jakzal/phpqa-local
    - phpcs web/index.php

This is of course just a silly pseudo code, but it should illustrate the idea. If your CI tool supports docker, it will be really really simple.

Written by Jarek Sobiecki
  • Toolkit
We love working alongside 
ambitious projects and people
Let's get in touch