7 Continuous Integration Tools for PHP Laravel Developers
Tags: php, laravel, beginners, cicd
Published: 2019-05-24 10:17:14

---
title: 7 Continuous Integration Tools for PHP Laravel Developers
published: true
description: 
tags: php, laravel, beginners, cicd
canonical_url: https://semaphoreci.com/blog/7-continuous-integration-tools-for-php-laravel
cover_image: https://thepracticaldev.s3.amazonaws.com/i/xp9rnj6q2p5a8eo5lces.png
---

In this article we'll cover 7 tools that can increase code quality, reduce development time, easily reveal errors and generally make your life as a PHP developer easier.

The goal is to set up various tests for our code, without having to
provision or maintain any servers. With a fast feedback cycle, we'll
be able to catch errors early and fix them before they grow.

Sounds good? Let's get started.

Code analysis tests
-------------------

Code analysis consists of scanning the source for certain structures
that may point to deeper problems, such as design flaws and bad coding
practices, usually called code smells.

Some code analysis tools focus on finding out bad patterns: functions
with too many parameters, fat classes or too deeply nested structures.
While others check for style: indentation rules, name conventions, etc.

Every analysis must start with some standard to follow. These rules are,
at the end of the day, subjective but informed by previous experience.
The rules can be generally configured and customized.

### PHP Mess Detector

[PHP Mess Detector](https://phpmd.org) (phpmd) checks for code smells: awkward, overcomplicated or unused code. It's inspired by the [PMD](https://pmd.github.io/) project.

phpmd ships with several rulesets than can be enabled or disabled independently.

Basic usage:

``` bash
$ phpmd SOURCE_FILE_OR_DIR REPORT_FORMAT RULESETS
```

Built-in rules are:

-   cleancode: enforce clean code base rules.
-   codesize: complexity rules, excessively long classes, etc.
-   controversial: camelcase rules, globals, etc.
-   design: forbid eval, goto, exit. Also coupling and depth rules.
-   naming: long and short identifier names, method name rules.
-   unusedcode: dead code and unused variables rules.

What does it look like?

``` bash
$ phpmd src text cleancode,codesize,controversial,design,naming,unusedcode
ProvisionerCommand.php:38    The variable $myvar_id is not named in camelCase.
ProvisionerCommand.php:38    Avoid variables with short names like $io. Configured minimum length is 3.
PermissionsComponent.php:53     Avoid unused private methods such as 'checkAccount'.
PagesController.php:349   Avoid excessively long variable names like $view_registration_count. Keep variable name length under 20.
ProvisionersController.php:106    The method delete uses an else expression. Else is never necessary and you can simplify the code to work without else.
```

If you've never done any code analysis on your project before, it is
likely to make you want to pull your hairs out. Don't worry and be
patient, after we have gotten our code in order our lives will be
easier.

Instead of setting the rules by command line, we can create an xml file
that can be checked in source control:

``` bash
$ phpmd src text myrules.xml
```

``` xml
<?xml version="1.0"?>
<ruleset name="Basic"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                     http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="
                     http://pmd.sf.net/ruleset_xml_schema.xsd">
    <description>
        First basic ruleset for code analysis...
    </description>

    <!-- Import all rule sets -->
    <rule ref="rulesets/cleancode.xml" />
    <rule ref="rulesets/codesize.xml" />
    <rule ref="rulesets/controversial.xml" />
    <rule ref="rulesets/design.xml" />

    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable" />
        <exclude name="LongVariable" />
    </rule>

</ruleset>
```

### PHP Code Sniffer

[PHP Code sniffer](https://github.com/squizlabs/PHP_CodeSniffer) (phpcs) is a style checker. If you've ever used a linter (jshint, pylint, checkstyle, etc) you already know what it does. `phpcs` can check for indentation, missing comments, naming conventions, etc.

PHP Code sniffer ships with various popular PHP styles such as PEAR, PSR2 and Zend among others. We can also make our own rules or mix and match
checks from existing ones.

The typical invocation goes:

``` bash
$ phpcs FILE_OR_DIR --standard=STANDARD_NAME
```

``` bash
$ phpcs FILE_OR_DIR --report-full --standard=PEAR
FILE: app/Providers/RouteServiceProvider.php
-----------------------------------------------------------------------------------------------------
FOUND 7 ERRORS AFFECTING 7 LINES
-----------------------------------------------------------------------------------------------------
  2 | ERROR | [ ] Missing file doc comment
  8 | ERROR | [ ] Missing doc comment for class RouteServiceProvider
 55 | ERROR | [x] Object operator not indented correctly; expected 12 spaces but found 13
 56 | ERROR | [x] Object operator not indented correctly; expected 12 spaces but found 13
 69 | ERROR | [x] Object operator not indented correctly; expected 12 spaces but found 13
 70 | ERROR | [x] Object operator not indented correctly; expected 12 spaces but found 13
 71 | ERROR | [x] Object operator not indented correctly; expected 12 spaces but found 13
-----------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 5 MARKED SNIFF VIOLATIONS AUTOMATICALLY
-----------------------------------------------------------------------------------------------------
```

PHP Code Sniffer also includes `phpcbf`, a program than can automatically fix some problems.

``` bash
$ phpcbf FILE_OR_DIR --report-full --standard=PEAR

PHPCBF RESULT SUMMARY
-------------------------------------------------------------------
FILE                                               FIXED  REMAINING
-------------------------------------------------------------------
app/Providers/RouteServiceProvider.php               5      2
app/Providers/BroadcastServiceProvider.php           3      5
app/Http/Middleware/Authenticate.php                 1      4
app/Http/Middleware/RedirectIfAuthenticated.php      3      6
app/Http/Controllers/UserController.php              10     20
app/Http/Controllers/Auth/RegisterController.php     8      9
app/Services/UserService.php                         9      22
app/Exceptions/Handler.php                           3      7
app/Console/Kernel.php                               2      4
-------------------------------------------------------------------
A TOTAL OF 44 ERRORS WERE FIXED IN 9 FILES
-------------------------------------------------------------------

Time: 201ms; Memory: 8MB
```

### PHP Copy Paste Detector

[PHP Copy Paste Detector](https://phpqa.io/projects/phpcpd.html) (phpcpd) does what it says on the tin: finds duplicate code inside your project.

Having duplicate code usually signals the need for refactoring, the repeated parts should find a new home in a shared library or component. Duplicates also force developers to make shotgun surgery: a single change must be repeated multiple times.

Basic usage:

``` bash
$ phpcpd FILE_OR_DIR
```

We can tell `phpcpd` how many lines must be repeated to be considered an error:

``` bash
$ phpcpd src --min-lines=40
phpcpd 4.1.0 by Sebastian Bergmann.

Found 1 clones with 45 duplicated lines in 2 files:

  - src/Controller/PagesController.php:32-77 (45 lines)
    src/Controller/Component/PermissionsComponent.php:9-54

1.14% duplicated lines out of 3950 total lines of code.
Average size of duplication is 45 lines, largest clone has 45 of lines

Time: 39 ms, Memory: 6.00MB
```

Unit testing: PHPUnit
---------------------

Unit testing ensures that our implementation does what it has been designed to do. Units are the smallest testable pieces of code, e.g. a class method, a function, an API call.

Unit tests also act as a form of living documentation, by reading what they do we can infer how the tested parts should work, what inputs do
they take and what outputs should them provide. They also validate that
code still valid after refactoring.

As new code is being written, we should also be creating tests to
validate its behavior.

[PHPUnit](https://phpunit.de/getting-started/phpunit-8.html) is the most popular testing framework for PHP, to drive our test cases. If you're looking for a tutorial to get started, this one can help:

- [Getting Started with PHPUnit in Laravel](https://semaphoreci.com/community/tutorials/getting-started-with-phpunit-in-laravel)

After our tests are in place, we call `phpunit` to get an error report:

``` bash
$ phpunit
PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

..F.                                                                4 / 4 (100%)

Time: 1.04 seconds, Memory: 18.00MB

There was 1 failure:

1) Tests\Unit\UserServiceTest::testItCanUpdateUser
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'updated name'
+'updatedx name'

tests/Unit/UserServiceTest.php:45

FAILURES!
Tests: 4, Assertions: 8, Failures: 1.
```

Browser test: Laravel Dusk
--------------------------

PHPUnit's biggest problem is its inability to test JavaScript on the frontend. [Dusk](https://laravel.com/docs/master/dusk) is a browser 
automation tool that overcomes this limitation by  testing the application on an actual browser.

Dusk interfaces with a real Chrome browser to programatically browse sites, perform actions, select elements and do assertions.

In order to test with Dusk, we need to start our application with Laravel's `artisan` tool:

``` bash
$ php artisan serve &
$ php artisan dusk
PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

F..                                                                 3 / 3 (100%)

Time: 11.34 seconds, Memory: 20.00MB

There was 1 failure:

1) Tests\Browser\LoginTest::testUserCanLogIn
Did not see expected text [Welcome, Test Account] within element [body].
Failed asserting that false is true.

vendor/laravel/dusk/src/Concerns/MakesAssertions.php:173
vendor/laravel/dusk/src/Concerns/MakesAssertions.php:144
tests/Browser/LoginTest.php:33
vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:67
tests/Browser/LoginTest.php:34

FAILURES!
Tests: 3, Assertions: 4, Failures: 1.
```

Security test: Sensiolabs
-------------------------

With [SensioLabs](https://github.com/sensiolabs/security-checker) security checker, we can scan our project dependencies for known vulnerabilities. It scans our composer file for dependencies and runs
them down through a vulnerabilities [database](https://security.symfony.com/):

``` bash
$ php security-checker security:check ../composer.lock

Symfony Security Check Report
=============================

No packages have known vulnerabilities.
```

Continuous Integration: Semaphore
---------------------------------

Continuous Integration (CI) allows to test early and test often.

We can set up a [CI pipeline](https://dev.to/markoa/ci-cd-pipeline-a-gentle-introduction-2n8k) to build the application on every push. The
pipeline ties all tools together in a single workflow, drives the tests and can optionally deploy the application.

[Semaphore](https://semaphoreci.com/) is a cloud-based continuous integration service that automatically scales to run tests on every `git push`. You can set a multi-stage CI pipeline from a single configuration file:

![PHP CI pipeline on Semaphore](https://thepracticaldev.s3.amazonaws.com/i/xp9rnj6q2p5a8eo5lces.png)

The point of separating the pipeline in a series of steps is to get faster feedback. For example, it makes no sense to run time-consuming, high-level browser tests if we've made a fundamental mistake in code.

To avoid setting up a pipeline from scratch, you can fork this open source project and add it on Semaphore:

{% github semaphoreci-demos/semaphore-demo-php-laravel %}

For a step-by-step explanation of the CI configuration, [read the article on Semaphore blog](https://semaphoreci.com/blog/7-continuous-integration-tools-for-php-laravel).

Happy building!