Logo BrocksiNet

BrocksiNet

Try to clean the web

PHPStan with static SQL analysis as pre-commit hook for Shopware 6

You working on a customer project and you want to make sure that you write code like a Shopware 6 core developer? Then you need all the shiny linters that the core developers are using. This guide will explain how to setup PHPStan and easy-coding-standard as a pre-commit hook with CaptainHook in a docker container setup. That's it, enjoy. ���🤯

Requirement: Shopware 6 project running with docker containers.
Warning: This are just local checks, you should also setup for example github actions to run this checks when someone commited something.

What we will do

  • Setup PHPStan
  • Setup CaptainHook
  • Setup easy-coding-standard (practice)
  • FAQ

Setup PHPStan

Make sure PHPStan is already required or execute this composer command inside your docker container.

composer require --dev phpstan/phpstan

Create a phpstan.neon file (to configure PHPStan) in your root folder of the project.

parameters:
    phpVersion: 80140
    level: 6
    treatPhpDocTypesAsCertain: false
    checkMissingIterableValueType: false
    inferPrivatePropertyTypeFromConstructor: true
    paths:
        - custom/static-plugins
    bootstrapFiles:
        - vendor/shopware/core/DevOps/StaticAnalyze/PHPStan/phpstan-bootstrap.php
        - phpstan-dba-bootstrap.php
    symfony:
        constantHassers: false
        consoleApplicationLoader: vendor/shopware/core/DevOps/StaticAnalyze/PHPStan/console-application.php

Create a phpstan-dba-bootstrap.php file in your root folder of the project.

<?php

use staabm\PHPStanDba\DbSchema\SchemaHasherMysql;
use staabm\PHPStanDba\QueryReflection\RuntimeConfiguration;
use staabm\PHPStanDba\QueryReflection\PdoMysqlQueryReflector;
use staabm\PHPStanDba\QueryReflection\QueryReflection;
use staabm\PHPStanDba\QueryReflection\ReplayAndRecordingQueryReflector;
use staabm\PHPStanDba\QueryReflection\ReflectionCache;
use Shopware\Recovery\Update\Utils;

require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/vendor/shopware/recovery/Update/src/Utils.php';

$cacheFile = __DIR__ . '/.phpstan-dba.cache';

$config = new RuntimeConfiguration();
// $config->debugMode(true);
// $config->stringifyTypes(true);
// $config->analyzeQueryPlans(true);

$database = getenv('DATABASE_URL');
$utils = new Utils();
$connection = $utils::getConnection(__DIR__);

QueryReflection::setupReflector(
    new ReplayAndRecordingQueryReflector(
        ReflectionCache::create(
            $cacheFile
        ),
        new PdoMysqlQueryReflector($connection),
        new SchemaHasherMysql($connection)
    ),
    $config
);

Add a script to your composer.json to execute/test phpstan.

"scripts": {
    "phpstan": "php -d memory_limit=-1 vendor/bin/phpstan analyse --configuration phpstan.neon --debug",
},

Test if the script is running and execute this inside your php docker container.

composer run-script phpstan

It will return a list of php files he analyzed and maybe some error for example like the following.

/app/custom/static-plugins/CookieManager/src/Service/CustomCookieProvider.php
 ------ ----------------------------------------------------------------------------------------------------------------- 
  Line   Project/src/Storefront/Controller/ExampleRequestFormController.php                                            
 ------ ----------------------------------------------------------------------------------------------------------------- 
  60     Method Project\Example\Storefront\Controller\ExampleRequestFormController::response() has no return type specified.  
 ------ ----------------------------------------------------------------------------------------------------------------- 
                                                                                                                      
[ERROR] Found 1 error
                                                                                                                                                                                             
Script php -d memory_limit=-1 vendor/bin/phpstan analyse --configuration phpstan.neon --debug handling the phpstan event returned with error code 1

Now fix all the errors 😉 If your code is shiny you can go on with setup CaptainHook.

Setup CaptainHook

Make sure CaptainHook is already required or execute this composer command inside your docker container.

composer require --dev captainhook/captainhook

Let's create a file called captainhook.json in your project root folder.

{
    "config": {
      "run-mode": "docker",
      "run-exec": "docker exec project-shopware-shop-app_server-1"
    },
    "pre-commit": {
        "enabled": true,
        "actions": [
            {
                "action": "composer run-script phpstan"
            }
        ]
    }
}

You should replace project-shopware-shop-app_server-1 with the name of your container (docker ps --format "{{.Names}}"). You can see that we run the phpstan command we defined in our composer.json before.

You can also configure CaptainHook with this command (this will create the captainhook.json file for your).

vendor/bin/captainhook configure

To install the git hooks we need to tell captainhook to install them. Update the CONTAINER_NAME before you execute the command. If you do NOT install them ... the checks will not executed in the pre-commit action. If you install them ... the checks will be executend and when there is an Error git will not commit any thing until you fix that error and commit again.

vendor/bin/captainhook install --run-mode=docker --run-exec="docker exec CONTAINER_NAME"

You can test if the hook is working just by commiting something with some PHPStan error. Also you can check in projectrootfolder/.git/hooks for a file called pre-commit. You can also install many other hooks for example to check the commit message.

Setup easy-coding-standard

The Shopware Core developers are not only using PHPStan, they also use easy-coding-standard to combine multiple coding rules into one configuration file. Look at this ecs.php Shopware 6 core config file.

This you can do for practice

  • Add easy-coding-standard with composer to your project (dev)
  • Setup a new easy-coding-standard configuration file that is checking the path to custom/static-plugins (so the custom project code)
  • Test and execute the easy-coding-standard check (if it is working)
  • Add the new easy-coding-standard check to your composer scripts
  • Add the composer script to your CaptainHook git hooks also for pre-commit
  • Test if the new easy-coding-standard check is also running when you commit something

---

That's it, enjoy the new shiny code checks before you commit something. 😇 💙

If you want to validate your code live with PHPStorm, the quality tools and docker check out the linked blog post.

Released - 06.10.2022

Comments


About the author

Bild Bjoern
Björn MeyerSoftware Engineer
Björn is interested in the details of web technologies. He started with Joomla, Drupal, Typo3, vBulletin, Fireworks and Photoshop. Today his focus is on PHP for example with Magento 2, Shopware 6 and Symfony. He also likes JavaScript with Angular, React, VueJs and TypeScript. In his free time, he seeks constant balance with swimming, running and yoga.