Continuous Code Quality Analysis with SonarQube

Nikolaus Huber
7 min readFeb 21, 2018

--

Sonar image of a shipwreck.

SonarQube is one of many static code analysis tools. I started to use it about nine years ago in the context of Java. Since then, the product improved a lot. Its major benefits: a centralized way to define quality criteria for different code bases as well as continuous collection of the quality metrics of your source code. Its support of 20+ languages makes SonarQube suitable for polyglott microservice architectures and with Sonarlint, it integrates with major IDEs.

Recently, I had the chance to apply SonarQube in the context of a PHP project. I was curious about how smooth it can be applied to PHP code and how it weill it integrates into our continuous integration pipeline. After all, it was more tricky than expected.

In this article, I want to share my experience with SonarQube analyzing PHP code and I will explain how to integrate the analysis into a CI pipeline using Docker. I will also show how you can use Sonarlint and SonarQube for static code analysis at development time. To make my results more reproducible, I applied the changes to PHP Process Manager, an open source PHP project. However, they should be applicable to other project as well.

Background

The process for continuously collecting code quality metrics requires two basic components:

  1. SonarQube, the server component responsible for persisting and providing the analysis results and
  2. the Sonar scanner, the client component that performs the analysis and pushes the results to the server.

A typical workflow looks like this:

Example continuous code inspection workflow.

Setting up SonarQube Server

You can use SonarCloud as a managed service or the Community Edition of SonarQube, which is free and open source. For our example, we use the SonarQube Docker Image by SonarSource. Please note that this image is configured with an embedded database and should not be used in prodcution. If you already have a running SonarQube server, you can skip to the next section.

To run SonarQube server on your local machine with Docker, you need to create a docker network first. This is necessary if you run the server and client in Docker containers. Skip it if you already have a running SonarQube server.

docker network create -d bridge sonar

This command creates a bridge network named sonar. Later, we will connect the Sonar scanner container to this network so it can access the server. Next, start the SonarQube server connected to the network created above:

docker run -d --name sonarqube --network=sonar \
-p 9000:9000 -p 9092:9092 sonarqube

You can now access SonarQube on http://localhost:9000. SonarQube comes with predefined rules, quality profiles and quality gates that will be used by Sonar scanner to analyze your code. You can adjust these settings to your needs either globally or on a per-project basis. In the following, I used the default values.

Analyzing Your Source Code with Sonar Scanner

The client component Sonar scanner is a command line tool. It can be configured using command line parameters or a properties file. From my perspective, it is more convenient to maintain a project-specific properties file in the same repository as the source code.

To demonstrate the Sonar scanner with a PHP project, I forked an open source repository (PHP Process Manager) and created a sonar-project.properties file for it:

To perform an analysis, I use this Docker image containing the Sonar scanner command line tool. I decided to package the Sonar scanner into a Docker image because for our PHP projects, we don’t use build tools and with Docker, it is easier to integrate into the CI pipeline. To perform an analysis manually, you can now start the analysis job from the repository root with the following command:

export DOCKERWORKDIR='/project-root';docker run --rm \
--mount type=bind,source="$(pwd)",target=$DOCKERWORKDIR \
-w=$DOCKERWORKDIR --network=sonar \
nikhuber/sonar-scanner:latest sonar-scanner

This command mounts the current working directory of the Docker host to a target directory inside the container (project-root). With the option -w we switch to that directory within the container. Finally, we execute the command sonar-scanner (the command line tool) in the container, which picks up the properties file in the work directory.

You can also pass configuration parameters at startup to override the parameters configured in your properties file. For example, you can override the version using -Dsonar.projectVersion=1.1 or the host URL -Dsonar.host.url=http://someotherhostname:9000. To do this, simply append the parameter to the command above. Once your analysis job completes successfully, you can inspect the results in the SonarQube server.

Code Coverage Metrics

It is possible to feed SonarQube with code coverage reports generated from test execution results. When analyzing Java code and JUnit test reports, collecting the coverage metric works basically “out of the box” with the Maven Sonar Plugin. However, for PHP code tested with PHPUnit, you need some minor adjustments.

You need to i) execute your unit tests with test execution report in JUnit format and ii) generate a report in Clover XML format. You can generate these files by passing command line parameters to PHPUnit or by adjusting your PHPUnit configurtion file. In the php-pm, I added the following lines to the phpunit.xm file:

<logging>
<log type="coverage-clover" target="clover.xml"/>
<log type="junit" target="junit-logfile.xml"/> </logging>

PHPUnit should now produce two files which you need to reference in the sonar project properties file (see example above). To reproduce these steps, follow the steps described in the readme of repository.

Finally, when you now perform an analysis and then open SonarQube server, you should see the analysis results including the coverage metrics.

SonarQube analysis results for PHP-PM.

CI Pipeline Integration

SonarQube provides built-in integrations for MSBuild, Maven, Gradle, Ant, and Makefiles. If you use such a tool for your build, it is quite easy to integrate SonarQube into your CI pipeline. For example, for Maven you can use the Maven Sonar Plugin.

However, in my PHP project (not the php-pm fork) there was no build tool but a CI pipeline implemented in Gitlab CI. In the following, I’ll explain how I integrated Sonar scanner using Docker. The part of the Gitlab CI pipeline definition covering the tests and analysis looks like this:

Test, analysis jobs from the Gitlab pipeline.

First, we define variables used by the pipeline jobs, e.g., the VERSION that we later pass to the Sonar scanner. Next, we define the pipeline consisting of two stages, test and analysis. This is necessary because the sonar scanner depends on the test results, i.e., the analysis job must be executed after the tests.

To pass the test results to the analysis stage, we need to collect the results as artifacts in the unittest job and specify them as a dependency in the sonar job (see the comments in the pipeline definition). Note that in this example, we mount a temporary filesystem to the home directory of the Sonar scanner container (.sonarscanner) to avoid that the Sonar scanner writes files to the host that cannot be deleted afterwards by the Gitlab runner.

With this configuration, each change pushed to the repository triggers the pipeline execution. The pipeline executes PHPunit and triggers the Sonar scanner. Sonar scanner analyses the source code, picks up the test logs and publishes the results on the SonarQube server.

Direct Feedback with Sonarlint

With SonarQube you have a central place to define rules and quality profiles and to continuously measure the quality of your code. However, as a developer you don’t want to wait for the CI pipeline to finish to get analysis results. Developers want to have direct feedback on changes, ideally while typing.

Here, Sonarlint can help. Sonarlint is a plugin for your IDE that fetches the ruleset from the SonarQube server and performs analysis as you work on the code.

Integration of Sonarlint in the development workflow.

Sonarlint can be easly integrated into IDEs, e.g., IntelliJ (it works the same for PHPStorm). The installation is quite simple and you can configure Sonarlint with the same settings as for Sonar scanner.

Conclusion

With its language support, SonarQube gives you the ability to centralliy monitor code quality of multiple services, even in a polyglott microservice environment. However, the setup and integration into a CI pipeline can be quite different for each project. My personal experience was that the effort to setup and integrate a PHP project into the CI pipeline was higher as for a Java project. The main reasons: I had to integrate the Sonar scanner manually into the pipeline and that the collection of coverage metrics from PHPUnit required additional configuration. With a management tool like Maven, this is easier. Nevertheless, with Sonarlint and SonarQube you have a toolchain that gives you the ability to specify and monitor quality criteria at a central place without losing development speed.

--

--