Byte Ebi's Logo

Byte Ebi 🍀

A Bit everyday A Byte every week

Guarding Code Quality with GrumPHP

Check if your code smells before you make a mistake.

Ray

In a collaborative development environment, Code Review is a crucial process.
If someone writes code that violates rules or fails tests, it can lead to project errors.
Manual syntax checks or running tests manually are time-consuming and not fashionable.
These tasks should be automated, and that’s where GrumPHP comes in.

What is GrumPHP

GrumPHP is an open-source Composer package that utilizes git hooks.
When someone changes the code and commits it, GrumPHP checks the code content based on predefined tasks.
If the tests fail, the commit is aborted.

GrumPHP comes with a set of common tasks built-in, allowing you to use it with minimal custom configuration.
You can find most of the tasks you might need on the Tasks page in the official documentation.
Let’s go through the installation process and add four commonly used tasks.

Installing GrumPHP

Let’s use the partially developed LaraPeko package as an example.
The term “project” mentioned here refers to this package.

First, navigate to the project directory and execute:

composer require --dev phpro/grumphp

An interactive prompt will appear, asking if you want to create the configuration file grumphp.yml with various options.
You can choose any option or skip this step. The next step involves installing yamllint.
If you chose yamllint in the previous step, you’re good to go.

You can test if the checks are working by running:

vendor/bin/grumphp run

If you run:

composer install

and see the message “GrumPHP is sniffing your commits!” it means GrumPHP is now sniffing your commits.

grumphp composer install

Adding Check Tasks

Let’s gradually install and explain four basic check tasks:

  1. yamllint
  2. PHPLint
  3. Phpunit
  4. Phpcs

If you didn’t create grumphp.yml in the previous step, create the file in the project root directory with the following content:

grumphp:
    tasks: {

    }

You can find an example grumphp.yml configuration file on the official GitHub page, and the Parameters file provides detailed explanations and settings.

1. yamllint

Checks the validity of the yaml file structure in your project, useful for configuration files in formats such as Drone.

According to the YamlLint documentation , no additional configuration is required.
Just add yamllint to the tasks list in grumphp.yml:

grumphp:
    tasks: {
        yamllint: null,
    }

2. PHPLint

Checks for syntax errors, such as missing commas that might result in red syntax error warnings in IDEs.

Follow the official documentation for the PHPLint task: PHPLint .
Start by installing the required dependency only in the test environment:

composer require --dev php-parallel-lint/php-parallel-lint

Add the phplint task to grumphp.yml:

grumphp:
    tasks: {
        yamllint: null,
        phplint: ~,
    }

Refer to the documentation for many adjustable rules. For simplicity, we’ll stick with the default values.

3. Phpunit

Runs tests in a specified path. Fails the task if any tests fail.

Follow the documentation for the Phpunit task: Phpunit . Install Phpunit in the test environment:

composer require --dev phpunit/phpunit

Create the phpunit.xml configuration file as required:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>

        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
</phpunit>

Test if the tasks are executed by running the following command.
Since we configured Phpunit to look for tests in specific folders, ensure those folders exist in the project.

vendor/bin/grumphp run

You should see a green happy face indicating that the tests passed.

phpunit pass

If we modify a test method to make it fail:

public function testGetAhoy()
{
    $larapeko = new LaraPeko();
    $this->assertEquals('ahoy', 'DD');
}

And run the command again, you will see a red sad face along with an error message, making it clear which test failed.

phpunit fail phpunit fail

4. Phpcs

Checks if the submitted PHP code adheres to PSR standards .

Install the required dependency following the Phpcs task documentation: Phpcs .

composer require --dev squizlabs/php_codesniffer

Create a phpcs.xml configuration file to set the rules you want to check. Here, we use the basic PSR-12 rules:

<?xml version="1.0"?>
<ruleset name="PSR12">
    <description>The PSR12 coding standard.</description>
    <rule ref="PSR12"/>
    <file>app/</file>
    <file>config/</file>
    <file>routes/</file>
    <exclude-pattern>public/index.php</exclude-pattern>
    <exclude-pattern>server.php</exclude-pattern>
    <exclude-pattern>laravel-nova-*</exclude-pattern>
    <exclude-pattern>vendor</exclude-pattern>
    <exclude-pattern>resources</exclude-pattern>
    <exclude-pattern>database/</exclude-pattern>
    <exclude-pattern

>storage/</exclude-pattern>
    <exclude-pattern>node_modules/</exclude-pattern>
    <exclude-pattern>tests</exclude-pattern>
</ruleset>

Now, run the check:

vendor/bin/grumphp run

You should see that all configured tests have passed.

phpcs pass

If there are issues violating PSR-12, you will see a frowning face and details about the problems.

phpcs fail

Automatic Listening

All the tasks we did above were manual executions. It would be better if the code quality check happens automatically.
To achieve this, we can modify the composer.json file by adding scripts that run during the Composer process.

Add the following script to the composer.json file to initialize GrumPHP during the post-installation command:

    "scripts": {
        "post-install-cmd": [
            "@php ./vendor/bin/grumphp git:init"
        ]
    },

This way, whether you run git commit in the command line or perform the commit through a GUI, the defined GrumPHP tasks will be triggered before the commit.

Below is an example of a failure status message triggered by the phpcs task through a GUI.

grumphp gui fail

Recent Posts

Categories

Tags