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.