<!--
  canonical-url: https://www.avonture.be/blog/php-jakzal-phpqa
  published:     2024-04-07
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# Docker image that provides static analysis tools for PHP

> Use the jakzal/phpqa Docker image to instantly run dozens of static analysis and code quality tools for your PHP codebase, including linters and fixers, without any installation.

---
![Docker image that provides static analysis tools for PHP](/img/v2/clean_code.webp)

**TL;DR**

This article shows how to run a whole suite of PHP static analysis tools with zero installation via the `jakzal/phpqa` Docker image: `composer normalize` to reorder `composer.json`, `composer-unused` to spot unused dependencies, `parallel-lint` for syntax errors, `php-cs-fixer` and `phpcbf` for auto-formatting — plus how to pin a specific PHP version tag when scanning legacy codebases.

For years now, I'm using <https://github.com/jakzal/phpqa> to run a lot of static analysis tools on my PHP codebase.

The list of available tools is huge; see by yourself: [Available tools](https://github.com/jakzal/phpqa?tab=readme-ov-file#available-tools).

In this blog post, we will see how we can take advantage of all these tools and increase the quality of our scripts.

First, as always when we need to learn new software, we must talk about how to install it. The answer is easy here: there is nothing to do. Oh? Really? For sure! `jakzal/phpqa` is a Docker image so we don't need to install it, just to run it once. During the first call, Docker will download it so, yeah, no installation at all. Thank you Docker!

> **Docker CLI reminder:**
>
> As a reminder, the used Docker run command will always look like:
>
> * `docker run` to run a Docker image (*Oh? Really?*),
> * `-it` to start Docker interactively, this will allow the script running in the container to ask you for some prompts f.i.,
> * `--rm` to ask Docker to kill and remove the container as soon as the script has been executed (otherwise you'll have a lot of exited but not removed Docker containers; you can check this by not using the `--rm` flag then running `docker container list` on the console),
> * `-v "${PWD}":/project` to [share your current folder](/blog/docker-volume) with a folder called `/project` in the Docker container (use `${PWD}` if you are on Linux, use `%CD%` if you are on DOS),
> * `-w /project` to tell Docker that the current directory, in the container, will be the `/project` folder
> * then `jakzal/phpqa` which is the name of the Docker image to use (you can also specify a version like `python:3.9.18` if needed; see <https://hub.docker.com/_/python/tags>)

## Composer unused

[Composer unused](https://github.com/composer-unused/composer-unused) will **try** to understand which packages are mentioned in your `composer.json` file and, perhaps, not used anymore.

By running `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa composer-unused` in your project, you'll get something like this:

![composer unused](./images/composer_unused.webp)

So, the tool detects that I'm referencing `spatie/laravel-db-snapshots` in my `composer.json` file but, in my codebase, the tool (in fact, the namespace added by the tool) isn't used at all so, yes, probably, I can remove that dependency.

> **Caution:**
>
> Such a tool can produce false positive; just make some checks before removing the dependency. The best solution here is, of course, to have plenty of [unit tests](/blog/pest_tips) so you can run them before and after the change and see if your code still works as expected.

## Composer normalize

[Composer normalize](https://github.com/ergebnis/composer-normalize) will reorder your `composer.json` file to match best practices.

As you know, in a JSON file, there is no order so you can define your properties in any order. For instance:

**composer.json**

```json
{
  "name": "ergebnis/composer-normalize",
  "description": "Provides a composer plugin for normalizing composer.json.",
  "license": "MIT",
  "type": "composer-plugin"
}
```

or

**composer.json**

```json
{
  "license": "MIT",
  "description": "Provides a composer plugin for normalizing composer.json.",
  "type": "composer-plugin",
  "name": "ergebnis/composer-normalize"
}
```

or anything else. The above example is just four lines; imagine a file with a hundred lines. It's more logical, isn't it, to retrieve first the `name` of the tool, then a small `description`, the `license`, the `type` (is it a project, a library, ...) and so on and not, f.i., first the list of requirements.

Ok, so, to normalize your `composer.json` file, you can install the tool or, and this is the objective of this blog post, simply run `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa composer normalize` and that's all.

The result will be a rewritten `composer.json` file where properties are put in standard order.

> **Information:**
>
> Add `--dry-run` if you just want to see what the changes will be; but do not modify any files.

## PHP-Parallel-lint

As a third example, we'll see [PHP-Parallel-Lint](https://github.com/php-parallel-lint/PHP-Parallel-Lint).

This tool will scan all `.php` files present in your codebase and ensure there is no syntax error like a missing `}`.

The command to run here is `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa parallel-lint . --exclude vendor`.

Think about adding exclusions like `--exclude vendor` so you don't scan code that isn't yours.

As you can see below, in less than two seconds, the tool has checked 823 files of my codebase. Pretty fast.

```bash
PHP 8.3.3 | 10 parallel jobs
............................................................  60/823 (7 %)
............................................................ 120/823 (14 %)
............................................................ 180/823 (21 %)
............................................................ 240/823 (29 %)
............................................................ 300/823 (36 %)
............................................................ 360/823 (43 %)
............................................................ 420/823 (51 %)
............................................................ 480/823 (58 %)
............................................................ 540/823 (65 %)
............................................................ 600/823 (72 %)
............................................................ 660/823 (80 %)
............................................................ 720/823 (87 %)
............................................................ 780/823 (94 %)
...........................................                  823/823 (100 %)
```

## PHP-CS-FIXER

Another tool, [PHP-CS-FIXER](https://cs.symfony.com/). *The PHP Coding Standards Fixer (PHP CS Fixer) tool fixes your code to follow standards.*

Here the command will be `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa php-cs-fixer fix` to reformat all your codebase according to the selected rulesets. If none is mentioned, `PSR-12` will be the default one.

## PHP\_CodeSniffer

Another tool, [PHP\_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer). *PHP\_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.*

This time, the command will be `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa phpcbf .`

## A whole host of other tools await you

As you can see, we can use a lot of tools using `jakzal/phpqa` in a zero-installation schema. That's really impressive. You can find a lot more tools than described in this article, just go to [Available tools](https://github.com/jakzal/phpqa?tab=readme-ov-file#available-tools) and see which ones can help you.

Two natural next steps once you've picked your tools: run them **before** committing, thanks to [Git pre-commit hooks](/blog/git-precommit), and run them again in your CI, as described in [GitLab - Running Docker-out-of-Docker in your CI](/blog/gitlab-docker-out-of-docker).

## Change PHP versions

Let's imagine your codebase is a legacy one (not written for PHP 8.3 but f.i. PHP 7.4).

An instruction like `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa parallel-lint . --exclude vendor` (as we have seen above) will scan the code with PHP 8.3 (situation end March 2024) and you can have plenty of errors since you're using a PHP 8.3x tool on a PHP 7.4x codebase.

How to solve this? Really easy in fact. As you know, by using `docker run [...] jakzal/phpqa [...]` you're asking Docker to use the `latest` version of the image and that version will change. Every time the owner of the image publishes a newer version, that `latest` image is updated and perhaps, in a few months, it'll be PHP 8.4 by default.

So, to solve our issue: we need to use a specific tag for PHP 7.4. *And if the tools complain about your PHP settings rather than your code, see [Update php.ini when using a Docker image](/blog/docker-php-ini).* Simply jump on <https://hub.docker.com/r/jakzal/phpqa/tags> and type `7.4` in the `Filter Tags` area.

![Tags](./images/tags.webp)

You'll find some tags. Now you can start `docker run -it --rm -v "${PWD}":/project -w /project jakzal/phpqa:1.80-php7.4-alpine parallel-lint . --exclude vendor` to run the PHP 7.4 linter.
