Автоматическая проверка кода за 5 минут

—

от автора

Данная инструкция показывает как автоматизировать проверку на 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

#!/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

<?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

{     "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!                       

Ура, всё работает.

ссылка на оригинал статьи https://habr.com/ru/post/548336/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *