Давайте посмотрим как будет выглядеть настройка в новом проекте.
Шаг 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 то будем писать код на нем.
#!/usr/bin/php<?phpdefine('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 в папку хуков гита
<?phpnamespace 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 примет следующий вид
{ "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 по следующим содержаением.
<?phpnamespace App;class MyClass{ private $var1; private $var2; public function __construct() { } public function test() { }}
Пытаемся закомитить и получаем ошибки проверки кода.
MBP-Admin:test admin$ git commit -am 'test'Code Quality ToolFetching filesRunning PHPLintChecking code style with PHPCSFILE: /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: 6MBIn pre-commit line 53: There are PHPCS coding standards violations!
Ура, всё работает.