How I take care of code quality with GrumPHP
Tags: php, codequality
Published: 2021-01-30 20:13:30

Everyone knows how important is taking care of code quality in our projects. There's a lot of ways to do that - code reviews, code standards or unit tests. Some responsibilities can be handled by some static analysis tools. I'm pretty sure you have heard about **PHPStan**, **PHP Code Sniffer** or **PHPMD**. Configuration of each in the project might be awkward.

[**GrumPHP**](https://github.com/phpro/grumphp) keeps all-in-one configuration for all most popular tools. _"This composer plugin will register some git hooks in your package repository. When somebody commits changes, GrumPHP will run some tests on the committed code"_. The library has a lot of built-in tasks and you can start using them with few lines of configuration.

I would like to show you how to implement GrumPHP in project. So, let's get started.

**(TL;DR version can be found at the end of the article)**

## ✩ **GrumPHP installation**

Go to your project, open a terminal and install GrumPHP via composer command
```bash
composer require --dev phpro/grumphp
```

When composer asks you to create a `grumphp.yml` file
```bash
Do you want to create a grumphp.yml file? [Yes]:
```
just type `No`, we’re going to create the configuration manually.

```bash
Watch out! GrumPHP is sniffing your commits!
```
Good, looks like the package is installed.

## ✩ **GrumPHP configuration**

To start using GrumPHP we need `grumphp.yml` file. Let's create that file with minimal configuration in root directory of your project.

```yaml
# grumphp.yml

grumphp:
  tasks:
```

As I mentioned before, GrumPHP is run on every single commit. But there is a way to run checks manually when needed. Let's just run binary file from a composer vendor

```bash
./vendor/bin/grumphp run
``` 

In the terminal you should noticed a grummy man's face. He will supervise your code quality, so watch out!

```bash
             ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
           ▄▄▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌           
         ▄▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
  ▄▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
 ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
 ▐█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
   ▀█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
     ▀▀▓▓▓▓▓▓▓▓▓▓▓▓█▀▀▀▀▀▀▀▀▀▀▀▀▀▀████████████▄
      ▄████████▀▀▀▀▀                 ██████████   
     ███████▀                         ██████▀
      ▐████      ██▌          ██       ████▌      
        ▐█▌                            ███        
         █▌           ▄▄ ▄▄           ▐███        
        ███       ▄▄▄▄▄▄▄▄▄▄▄▄       ▐███         
         ██▄ ▐███████████████████████████
        █▀█████████▌▀▀▀▀▀▀▀▀▀██████████▌▐         
          ███████████▄▄▄▄▄▄▄███████████▌          
         ▐█████████████████████████████           
          █████████████████████████████           
           ██ █████████████████████▐██▀           
            ▀ ▐███████████████████▌ ▐▀            
                ████▀████████▀▐███                
                 ▀█▌  ▐█████  ▐█▌                 
                        ██▀   ▐▀                  
       _    _ _                         _ _
      / \  | | |   __ _  ___   ___   __| | |
     / _ \ | | |  / _` |/ _ \ / _ \ / _` | |
    / ___ \| | | | (_| | (_) | (_) | (_| |_|
   /_/   \_\_|_|  \__, |\___/ \___/ \__,_(_)
                  |___/
```

Ok, now we can setup some tasks and checks.

## ✩ **PHP CodeSniffer (PHPCS)**

What is [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer)?

>*PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.*

Let's install required dependency with the composer

```bash
composer require --dev squizlabs/php_codesniffer
```
Based on [PSR](https://www.php-fig.org/psr/), currently [PSR-12](https://www.php-fig.org/psr/psr-12/) is a recommended coding standard. 

Let's add PHP CodeSniffer task and determine standard in our configuration file.

```yaml
# grumphp.yml

grumphp:
  tasks:
    phpcs:
      standard: PSR12
```

Now, commit your changes (or run manually GrumPHP) to check your code for the first time.

Oops... GrumpPHP found some issues related to PSR12 standard so take a look on a message that points the file and brings more information about the error.
```bash
FILE: .../src/User.php
---------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
---------------------------------------------------------------
 27 | ERROR | [x] Expected 1 newline at end of file; 0 found
---------------------------------------------------------------

I can fix some stuff automatically, do you want me to? (yes/no) [no]:
```
There are two options to resolve these errors:
- go to the file and fix them manually, or
- trust GrumPHP and allow it to fix them automatically. To do so type `yes` in the terminal.

```bash
Auto-Fixing tasks...
Running fixer 1/1: phpcs... ✔

[WARNING] Please review the code changes that I made!
```

Remember, you should review all the changes made by the tool.

Great! Now the project is consistent with PSR12 standard, that’s a good start. 


## ✩ **PHPStan**

>_The [PHPStan](https://github.com/phpstan/phpstan) task focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code_

Install a required dependency by running the composer command
```bash
composer require --dev phpstan/phpstan
```

Next, add PHPStan as one of tasks in the configuration file

```yaml
# grumphp.yml

grumphp:
  tasks:
    phpcs:
      standard: PSR12
    phpstan:
      level: 5
```

There are 9 levels of checks from 0 to 8, where 0 is the loosest and 8 is the strictest. Which one should you choose? It depends.

If you are working with a legacy code or you are just starting to take care of your code quality - choose between 0 and 3. You can always upgrade the level in the future. 

When you are confident with your codebase and skills then I recommend choosing levels from 4 to 8.

Run GrumPHP. If there are some errors, you will see a message that may look like this
```bash
phpstan
=======

------ -------------------------------------------------------------------- 
  Line   src/User.php                                                        
 ------ -------------------------------------------------------------------- 
  21     Method User::id() should return string but returns int.  
 ------ --------------------------------------------------------------------
```

Unfortunately, all the fixes have to be done manually.

It looking good, we have installed two tasks already.


## ✩ **PHPUnit**

(If you don’t use [PHPUnit](https://github.com/sebastianbergmann/phpunit) in your project, skip this section)

I used to forgot running tests before committing my changes. Sometimes after that, a build on my pipeline failed.

To be sure unit tests always pass, let's add PHPUnit task to GrumPHP.

```yaml
# grumphp.yml

grumphp:
  tasks:
    phpcs:
      standard: PSR12
    phpstan:
      level: 5
    phpunit:
      always_execute: true
```

Because as a rule unit tests are executed really fast, set `always_execute` as `true`. It means unit test will be run on every commit. Thanks to that, if any test fails, GrumPHP will inform us about that incident.

```bash
phpunit
=======

PHPUnit 9.X.X by Sebastian Bergmann and contributors.

...

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
```

I would like to show you one more interesting and useful tool.


## ✩ **Security Checker**

Nowadays, everyone uses the composer and external dependencies. Libraries very often become affected by vulnerabilities and it's difficult to track changelog of them all.

GrumPHP and [Security Checker](https://github.com/enlightn/security-checker) can help to detect dangerous packages you use.

>_The Security Checker will check your composer.lock file for known security vulnerabilities._

Let's install the required dependency
```bash
composer require --dev enlightn/security-checker
```

Add task to `grumphp.yml` file and set `run_always: true` flag. It means that on every commit all dependencies will be checked by tool.

```yaml
# grumphp.yml

grumphp:
  tasks:
    phpcs:
      standard: PSR12
    phpstan:
      level: 5
    phpunit:
      always_execute: true
    securitychecker:
      run_always: true
```

Run GrumPHP and check the result.

```bash
securitychecker
===============

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

1 packages have known vulnerabilities.

guzzlehttp/guzzle (6.2.0)
-------------------------

 * [CVE-2016-5385][]: HTTP Proxy header vulnerability

[CVE-2016-5385]: https://github.com/guzzle/guzzle/releases/tag/6.2.1
```

As you can see above, there is pointed package with vulnerability that have to be updated.


## ✩ **Summary**

All looks good, four tasks passed successfully. It's a good first step towards better code quality in your project.

```bash
Running task 1/4: phpcs... ✔
Running task 2/4: phpstan... ✔
Running task 3/4: phpunit... ✔
Running task 4/4: securitychecker... ✔
             ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
           ▄▄▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌           
         ▄▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
  ▄▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
 ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
 ▐█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
   ▀█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌        
     ▀▀▓▓▓▓▓▓▓▓▓▓▓▓█▀▀▀▀▀▀▀▀▀▀▀▀▀▀████████████▄
      ▄████████▀▀▀▀▀                 ██████████   
     ███████▀                         ██████▀
      ▐████      ██▌          ██       ████▌      
        ▐█▌                            ███        
         █▌           ▄▄ ▄▄           ▐███        
        ███       ▄▄▄▄▄▄▄▄▄▄▄▄       ▐███         
         ██▄ ▐███████████████████████████
        █▀█████████▌▀▀▀▀▀▀▀▀▀██████████▌▐         
          ███████████▄▄▄▄▄▄▄███████████▌          
         ▐█████████████████████████████           
          █████████████████████████████           
           ██ █████████████████████▐██▀           
            ▀ ▐███████████████████▌ ▐▀            
                ████▀████████▀▐███                
                 ▀█▌  ▐█████  ▐█▌                 
                        ██▀   ▐▀                  
       _    _ _                         _ _
      / \  | | |   __ _  ___   ___   __| | |
     / _ \ | | |  / _` |/ _ \ / _ \ / _` | |
    / ___ \| | | | (_| | (_) | (_) | (_| |_|
   /_/   \_\_|_|  \__, |\___/ \___/ \__,_(_)
                  |___/

```

I have introduced you to GrumPHP with a really simple configuration. There is a lot of different tasks that you can use in your project and full list can be found in official GitHub repository: [GrumPHP Task Docs](https://github.com/phpro/grumphp/tree/master/doc/tasks).


## ✩ **TL;DR**

Install GrumPHP:
```bash
composer require --dev phpro/grumphp
Do you want to create a grumphp.yml file? [Yes]:
> No
```

Install dependencies
```bash
composer require --dev squizlabs/php_codesniffer
composer require --dev phpstan/phpstan
composer require --dev enlightn/security-checker
```

Create a file `grumphp.yml` in the project's root directory with the following configuration:
```yaml
# grumphp.yml

grumphp:
  tasks:
    phpcs:
      standard: PSR12       # PSR12 is recommended, but there are available i.a. PSR1, PSR2
    phpstan:
      level: 5              # levels: 0-8, where 0 is the loosest and 8 is the strictest
    phpunit:                # comment those lines if you ...
      always_execute: true  # ... don't use PHPUnit in your project
    securitychecker:
      run_always: true
```

Commit changes or just run binary file from the composer vendor
```bash
./vendor/bin/grumphp run
```

More build-in tasks can be found in official GitHub repository: [GrumPHP Task Docs](https://github.com/phpro/grumphp/tree/master/doc/tasks)