[PHP, Symfony] Автоматическая проверка кода за 5 минут

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
22-Мар-2021 17:32

Данная инструкция показывает как автоматизировать проверку на code style в вашем php проекте.
Давайте посмотрим как будет выглядеть настройка в новом проекте.
Шаг 1 — Делаем инициализацию composer (у кого он уже настроен, пропускаем)
Для этого в корне вашего проекта запускаем команду. Если у вас не установлен composer, то можете обратиться к официальной документации getcomposer.org
composer init

Шаг 2 — Добавляем .gitignore
###> phpstorm ###
.idea
###< phpstorm ###
/vendor/
###> friendsofphp/php-cs-fixer ###
/.php_cs
/.php_cs.cache
###< friendsofphp/php-cs-fixer ###

Шаг 3 — Добавляем нужные библиотеки
composer require --dev friendsofphp/php-cs-fixer symfony/process symfony/console  squizlabs/php_codesniffer

Шаг 4 — Добавляем обработчик хука
Сам обработчик можно написать на чем угодно, но так как статься про php то будем писать код на нем.

Создаем файлик в папочке hooks/pre-commit.php

SPL
#!/usr/bin/php
<?php
define('VENDOR_DIR', __DIR__.'/../../vendor');
require VENDOR_DIR.'/autoload.php';
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Process\Process;
class CodeQualityTool extends Application
{
    /**
     * @var OutputInterface
     */
    private $output;
    /**
     * @var InputInterface
     */
    private $input;
    const PHP_FILES_IN_SRC = '/^src\/(.*)(\.php)$/';
    public function __construct()
    {
        parent::__construct('Ecombo Quality Tool', '1.0.0');
    }
    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     *
     * @return void
     * @throws \Exception
     */
    public function doRun(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->output = $output;
        $output->writeln('<fg=white;options=bold;bg=red>Code Quality Tool</fg=white;options=bold;bg=red>');
        $output->writeln('<info>Fetching files</info>');
        $files = $this->extractCommitedFiles();
        $output->writeln('<info>Running PHPLint</info>');
        if (! $this->phpLint($files)) {
            throw new \Exception('There are some PHP syntax errors!');
        }
        $output->writeln('<info>Checking code style with PHPCS</info>');
        if (! $this->codeStylePsr($files)) {
            throw new \Exception(sprintf('There are PHPCS coding standards violations!'));
        }
        $output->writeln('<info>Well done!</info>');
    }
    /**
     * @return array
     */
    private function extractCommitedFiles()
    {
        $output = array();
        $against = 'HEAD';
        exec("git diff-index --cached --name-status $against | egrep '^(A|M)' | awk '{print $2;}'", $output);
        return $output;
    }
    /**
     * @param array $files
     *
     * @return bool
     *
     * @throws \Exception
     */
    private function phpLint($files)
    {
        $needle = '/(\.php)|(\.inc)$/';
        $succeed = true;
        foreach ($files as $file) {
            if (! preg_match($needle, $file)) {
                continue;
            }
            $process = new Process(['php', '-l', $file]);
            $process->run();
            if (! $process->isSuccessful()) {
                $this->output->writeln($file);
                $this->output->writeln(sprintf('<error>%s</error>', trim($process->getErrorOutput())));
                if ($succeed) {
                    $succeed = false;
                }
            }
        }
        return $succeed;
    }
    /**
     * @param array $files
     *
     * @return bool
     */
    private function codeStylePsr(array $files)
    {
        $succeed = true;
        $needle = self::PHP_FILES_IN_SRC;
        $standard = 'PSR2';
        foreach ($files as $file) {
            if (! preg_match($needle, $file)) {
                continue;
            }
            $phpCsFixer = new Process([
                'php',
                VENDOR_DIR.'/bin/phpcs',
                '-n',
                '--standard='.$standard,
                $file,
            ]);
            $phpCsFixer->setWorkingDirectory(__DIR__.'/../../');
            $phpCsFixer->run();
            if (! $phpCsFixer->isSuccessful()) {
                $this->output->writeln(sprintf('<error>%s</error>', trim($phpCsFixer->getOutput())));
                if ($succeed) {
                    $succeed = false;
                }
            }
        }
        return $succeed;
    }
}
$console = new CodeQualityTool();
$console->run();


В данном примере код будет проходить 3 проверки:
— проверка на синтаксические ошибки
— проверка на PSR2 через code sniffer
PSR2 можно заменить на любой другой который поддерживает code sniffer. Список поддерживаемых стандартов можно увидеть введя команду
vendor/bin/phpcs -i

Шаг 5 — Конфигурируем composer для реализации автозапуска проверки на pre-commit
Для того чтобы код проверки запускался на pre commit хук нам необходимо положить файлик с кодом, который сделали в 3 пункте положить в папку .git/hooks/pre-commit. Это можно сделать вручную но куда удобнее это дело автоматизировать. Для этого нам нужно написать обработчик, который будет копировать этот файлик и повешать его на событие которые вызывается после composer install. Для этого делаем следующее.
5.1 Создаем сам обработчик который будет копировать файлик pre-commit.php в папку хуков гита

Создаем файлик src/Composer/ScriptHandler.php

SPL
<?php
namespace App\Composer;
use Composer\Script\Event;
class ScriptHandler
{
    /**
     * @param Event $event
     *
     * @return bool
     */
    public static function preHooks(Event $event)
    {
        $io = $event->getIO();
        $gitHook = '.git/hooks/pre-commit';
        if (file_exists($gitHook)) {
            unlink($gitHook);
            $io->write('<info>Pre-commit hook removed!</info>');
        }
        return true;
    }
    /**
     * @param Event $event
     *
     * @return bool
     *
     * @throws \Exception
     */
    public static function postHooks(Event $event)
    {
        /** @var array $extras */
        $extras = $event->getComposer()->getPackage()->getExtra();
        if (! array_key_exists('hooks', $extras)) {
            throw new \InvalidArgumentException('The parameter handler needs to be configured through the extra.hooks setting.');
        }
        $configs = $extras['hooks'];
        if (! array_key_exists('pre-commit', $configs)) {
            throw new \InvalidArgumentException('The parameter handler needs to be configured through the extra.hooks.pre-commit setting.');
        }
        if (file_exists('.git/hooks')) {
            /** @var \Composer\IO\IOInterface $io */
            $io = $event->getIO();
            $gitHook = '.git/hooks/pre-commit';
            $docHook = $configs['pre-commit'];
            copy($docHook, $gitHook);
            chmod($gitHook, 0777);
            $io->write('<info>Pre-commit hook created!</info>');
        }
        return true;
    }
}

5.2 Настраиваем composer чтобы запускался обработчик
в composer.json добавляем следующую секцию
"scripts": {
        "post-install-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "post-update-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "pre-update-cmd": "App\\Composer\\ScriptHandler::preHooks",
        "pre-install-cmd": "App\\Composer\\ScriptHandler::preHooks"
    },
    "extra": {
        "hooks": {
            "pre-commit": "hooks/pre-commit.php"
        }
    }


pre-update-cmd, pre-install-cmd — перед install и update удаляется старый обработчик
post-install-cmd, post-update-cmd — после install и update будет устанавливаться обработчик на pre commit
В итоге файлкик composer.json примет следующий вид

composer.json

SPL
{
    "name": "admin/test",
    "authors": [
        {
            "name": "vitaly.gorbunov",
            "email": "cezar62882@gmail.com"
        }
    ],
    "minimum-stability": "stable",
    "require": {},
    "autoload": {
        "psr-4": {
            "App\": "src/"
        }
    },
    "scripts": {
        "post-install-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "post-update-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "pre-update-cmd": "App\\Composer\\ScriptHandler::preHooks",
        "pre-install-cmd": "App\\Composer\\ScriptHandler::preHooks"
    },
    "require-dev": {
        "friendsofphp/php-cs-fixer": "^2.16",
        "symfony/process": "^5.0",
        "symfony/console": "^5.0",
        "squizlabs/php_codesniffer": "^3.5"
    },
    "extra": {
        "hooks": {
            "pre-commit": "hooks/pre-commit.php"
        }
    }
}


Запускаем еще раз composer install чтобы файлик скопировался куда надо.
Все готово, теперь если вы попытаетесь закомитить код с кривым code style то git console вам об этом скажет.
В качестве примере давайте создадим в папке src файлик MyClass.php по следующим содержаением.
<?php
namespace App;
class MyClass
{
    private $var1; private $var2;
    public function __construct() {
    }
    public function test() {
    }
}

Пытаемся закомитить и получаем ошибки проверки кода.
MBP-Admin:test admin$ git commit -am 'test'
Code Quality Tool
Fetching files
Running PHPLint
Checking code style with PHPCS
FILE: /Users/admin/projects/test/src/MyClass.php
----------------------------------------------------------------------
FOUND 5 ERRORS AFFECTING 5 LINES
----------------------------------------------------------------------
  8 | ERROR | [x] Each PHP statement must be on a line by itself
10 | ERROR | [x] Opening brace should be on a new line
13 | ERROR | [x] Opening brace should be on a new line
15 | ERROR | [x] Function closing brace must go on the next line
    |       |     following the body; found 1 blank lines before
    |       |     brace
16 | ERROR | [x] Expected 1 newline at end of file; 0 found
----------------------------------------------------------------------
PHPCBF CAN FIX THE 5 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------
Time: 49ms; Memory: 6MB
In pre-commit line 53:
  There are PHPCS coding standards violations!

Ура, всё работает.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_php, #_symfony, #_instrumenty_dlja_vebrazrabotki (инструменты для веб-разработки), #_php, #_symfony
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 14:09
Часовой пояс: UTC + 5