Проблема 1
При совместной разработке (от 2х человек) code style становится критичным. Если каждый будет писать в своём стиле, вскоре проект превратится в бардак, где по разному стилю можно будет оценивать возраст кода будто по кольцам на деревьях.
На собеседованиях я часто слышу от кандидатов, что мы пользуемся проверками, встроенными в IDE. Но это полрешения, так как такие проверки носят рекомендательный, а не обязательный характер. Очень легко не заметить предложение по правке от IDE. А кроме того, приходится синхронизировать настройки IDE между всей командой. А что делать, если у кого-то другой редактор? Результат непредсказуем, а значит и ненадёжен.
Проблема 2
Чем больше кодовая база проекта, тем проще допустить ошибку или опечатку. Вручную проверка кода перед каждым коммитом возможна, но требует много времени и большой внимательности. Результат опять непредсказуем и ненадёжен.
Решение
Нужно договориться об общих правилах с командой и автоматизировать процесс проверки кода, сделав её независимой от IDE, платформы, настроения, желания и внимательности человека.
Такие инструменты уже есть — code style и статические анализаторы кода.
Единый стиль кода
Поскольку я специализируюсь на Symfony, то для код-стайла выбираю PHP-CS-Fixer. Он написан при участии создателя Symfony и имеет из коробки поддержку рекомендованного сообществом Symfony Code Standards. Есть ещё PHPCodeSniffer, но он умеет только обнаруживать ошибки, но не исправляет их автоматически, поэтому я рекомендую именно PHP-CS-Fixer.
Совет: обязательно изучите и следуйте code style и рекомендациям вашего фреймворка. С большой вероятностью в будущих проектах вам встретится именно общепринятый в сообществе фреймворка code style, поэтому будет проще вписаться в новую команду, если вы придёте к этому стилю как можно раньше. К тому же, уже принятый в сообществе стиль поможет избежать ненужных холиваров внутри команды.
Устанавливаем через composer и настраиваем PHPCSFixer в Symfony-проекте:
cd /ваш/проект
composer req --dev friendsofphp/php-cs-fixer
# Если используете docker-compose, то устанавливайте в нём
docker-compose exec web composer req --dev friendsofphp/php-cs-fixer
Создаём в корне проекта файл .php_cs:
<?php
$finder = PhpCsFixer\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests'
])
;
return PhpCsFixer\Config::create()
->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'concat_space' => ['spacing' => 'one'],
'increment_style' => ['style' => 'post'],
'no_extra_blank_lines' => ['tokens' => [
'extra',
'parenthesis_brace_block',
'square_brace_block',
'throw',
'use',
]],
'no_superfluous_phpdoc_tags' => false,
'phpdoc_align' => false,
'phpdoc_annotation_without_dot' => false,
'trailing_comma_in_multiline_array' => false,
'yoda_style' => false
])
->setFinder($finder)
;
Теперь вы можете запустить проверку всего проекта (или конкретного пути) с помощью команды
# только поиск ошибок
./vendor/bin/php-cs-fixer fix --dry-run --diff ./
# поиск ошибок и их автоматический фикс
./vendor/bin/php-cs-fixer fix ./
Ошибки и качество кода
Для отлова опечаток и даже поиска более сложных ошибок мы воспользуемся статическим анализатором кода. Прелесть анализаторов в том, что они не выполняют код, а просто читают и анализируют — это очень быстро. Кроме того, каждый из них имеет свои уровни строгости, поэтому можно внедрять их в существующий проект постепенно: фиксим ошибки с минимальным уровнем, коммитим, повышаем уровень, фиксим, коммитим, снова повышаем.
Самые популярные статичиские анализаторы кода:
Я покажу, как настроить и внедрить PHPStan, а вы можете добавить в проект все три по аналогии. Устанавливаем через composer сам PHPStan и несколько полезных плагинов — PHPUnit, Symfony, Doctrine.
cd /ваш/проект
composer req --dev phpstan/phpstan phpstan/phpstan-doctrine phpstan/phpstan-phpunit phpstan/phpstan-symfony
Должен создаться в корне проекта файл .phpstan.neon.dist, редактируем его:
includes:
- vendor/phpstan/phpstan-doctrine/extension.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-symfony/extension.neon
parameters:
reportUnmatchedIgnoredErrors: false #to avoid throwing of errors ... if no errors matching the ignored one is raised
scanFiles: #we need this for some twig based functions
- '%rootDir%/../../../vendor/twig/twig/src/Extension/CoreExtension.php'
bootstrapFiles:
- '%rootDir%/../../../vendor/bin/.phpunit/phpunit-X.X.X/vendor/autoload.php'
ignoreErrors:
- message: '/^Service "[^"]+" is private.$/'
path: '%rootDir%/../../../tests/'
level: 0
symfony:
container_xml_path: '%rootDir%/../../../var/cache/development/App_KernelDevelopmentDebugContainer.xml'
Внимательно пройдитесь по этим настройкам и укажите корректные пути. Особое внимание:
- bootstrapFiles — включает путь до текущего phpunit. Если его не используете, то можете закоментить.
- level — уровень строгости проверок от 0 до 8. 0 — самый слабый уровень, подходит для первого внедрения.
Теперь вы можете запустить проверку всего проекта (или конкретного пути) с помощью команды
./vendor/bin/phpstan analyse src tests
Советы по внедрению
Несколько советов по внедреню PHP-CS-Fixer в существующий проект с большой командой.
- Выберите одного ответственного, кто запустит скрипты и пофиксит весь проект у себя на локалке, а потом создаст ПР / смерджит в develop или master.
- Лучшее время — первое утро нового спринта. Обычно после спринта все ветки смёрджены, и это позволит залить все найденные правки в основную ветку проекта без конфликтов.
- В статических анализаторах используйте для начала минимальный уровень строгости проверок. У каждого анализатора для этого своя школа, читайте документацию.
- Первый фикс — самый критичный момент, так как правок может быть очень много. Все последующие фиксы обычно не создают ощутимых конфликтов и гит их смёрдживает самостоятельно.
Чтобы не запоминать длинные пути до скриптов, мы добавим несколько задач (tasks) в composer.json:
{
"require": { ... },
"require-dev": { ... },
...
"scripts": {
...
"csfix": "./vendor/bin/php-cs-fixer fix",
"csfix-validate" : "./vendor/bin/php-cs-fixer fix --dry-run --diff",
"phpstan": "./vendor/bin/phpstan analyse src tests",
"code-quality": [
"@phpcsfixer-validate",
"@phpstan"
]
Теперь мы можем запускать проверки в упрощённом формате:
composer csfix
composer phpstan
composer code-quality
Обязательный запуск проверок перед коммитом
Внедрение этих инструментов позволяет удостовериться, что код соответствует принятому в команде стилю и не имеет очевидных опечаток и синтаксических ошибок. Однако это ещё не решает проблему забывчивости программистов и необязательности проверок.
Воспользуемся Git-хуками (git hooks) — это скрипты, которые мы можем попросить git выполнить при возникновении какого-то события: момент перед созданием коммита, момент после создания коммита, момент добавления сообщения в коммит и т. п.
Полный список git-хуков вы можете увидеть у себя в репозитории:
$ ls -al .git/hooks
-rwxr-xr-x 1 mk staff 478 Jul 13 13:04 applypatch-msg.sample
-rwxr-xr-x 1 mk staff 896 Jul 13 13:04 commit-msg.sample
-rwxr-xr-x 1 mk staff 3327 Jul 13 13:04 fsmonitor-watchman.sample
-rwxr-xr-x 1 mk staff 189 Jul 13 13:04 post-update.sample
-rwxr-xr-x 1 mk staff 424 Jul 13 13:04 pre-applypatch.sample
-rwxr-xr-x 1 mk staff 382 Dec 1 14:46 pre-commit
-rwxr-xr-x 1 mk staff 1638 Jul 13 13:04 pre-commit.sample
-rwxr-xr-x 1 mk staff 416 Jul 13 13:04 pre-merge-commit.sample
-rwxr-xr-x 1 mk staff 1348 Jul 13 13:04 pre-push.sample
-rwxr-xr-x 1 mk staff 4898 Jul 13 13:04 pre-rebase.sample
-rwxr-xr-x 1 mk staff 544 Jul 13 13:04 pre-receive.sample
-rwxr-xr-x 1 mk staff 1492 Jul 13 13:04 prepare-commit-msg.sample
-rwxr-xr-x 1 mk staff 3610 Jul 13 13:04 update.sample
Создадим скрипт, который будет запускать проверки каждый раз перед попыткой создать коммит.
vim .git/hooks/pre-commit
Добавляем внутрь файла:
#!/bin/sh
export PATH=/usr/local/bin:$PATH
# Если вы используете docker-compose (рекомендуется), то добавьте
export COMPOSE_INTERACTIVE_NO_CLI=1
if [ -t 1 ]; then
# If we're in a terminal, redirect stdout and stderr to /dev/tty and
# read stdin from /dev/tty. Allow interactive mode for CaptainHook.
exec >/dev/tty 2>/dev/tty </dev/tty
fi
errors=0
composer code-quality
# или, если вы используете docker-compose (рекомендуется), то
docker-compose exec -T web php bin/composer code-quality
if [ "$?" -ne 0 ]; then
errors=1
fi
if [ "$errors" -eq 1 ]; then
echo "Errors detected!"
exit 1
fi
Теперь можете попробовать сделать ошибку в .php файле и создать коммит. Git должен ругнуться:
Устанавливаем git-hook автоматически
Финальный шаг — добавить git-hook в .git/hooks/ папку при запуске composer. Тогда ни один разработчик не сможет пропустить запуск проверок по невнимательности. Для этого добавим в composer.json такую строку:
{
...,
"scripts": {
"post-autoload-dump": "mkdir -p .git/hooks && cp config/pre-commit .git/hooks/pre-commit",
...
}
}
Итог
- Мы добавили в проект PHPCsFixer для код-стайла и PHPStan для поиска ошибок и опечаток.
- Запускаем их при каждой попытке создать коммит.
- И всё это начинает работать автоматически после первого же composer install.