
Автоформатування коду: PHP CodeSniffer и PHP CS Fixer
07.06.2019 22:39 | Інше
Відсутність форматування - перша ознака того, що перед вами код, дуже сумнівної якості. А рівень кваліфікації людей, які писали такий код, далекий навіть до джуніора. Однак реальне життя вносить свої корективи, і якщо вже Вам "прилетів" проект з таким чудо-кодом, в першу чергу треба привести форматування в порядок, а також подбати про те, щоб подібне більше не потрапляло в репозиторій. Тому тема розмови - PHP CodeSniffer, PHP CS Fixer і git pre-commit хук.
На laracasts є чудове відео на цю тему в контексті PhpStorm, тут же піде мова про те, чого не розповідав в тому відео Джефрі (ну а якщо хочете налаштувати фіксер під Sublime, дивітьсяе цю статтю).
PHP CodeSniffer
Давайте почнемо з PHP CodeSniffer. Цей інструмент допоможе виявити порушення форматування, а при відповідних налаштуваннях PhpStorm буде Вас про це інформувати шляхом виділення проблемних частин коду. Крім усього іншого, ми можемо скористатися консоллю для виведення інформації про помилки.
Корисні посилання:
Установка
Особисто моя рекомендація - встановлювати пакет не глобально, а на кожному проекті в якості залежності з тієї простої причини, що ми не можемо бути впевнені, чи встановлений інструмент на машині кожного розробника. Для цього виконуємо команду:
composer require "squizlabs/php_codesniffer=*" --dev
Стандарт форматування
Перш ніж рухатися далі обов'язково узгодьте стандарт з командою:
- якщо рішення залежить від Вас - поясніть всім, якого стандарту слід дотримуватися, і чи є які-небудь додаткові правила (наприклад, обов'язковий порожній рядок перед оператором
return
) - якщо Ви не тім-лід - узгодьте з ним правила (взагалі-то це його робота, але не завжди все ідеально)
- якщо Ви працюєте в інтернаціональній команді - знову-таки, перше, що потрібно зробити - узгодити стандарт з девелоперами "на тому кінці дроту"
Також гарненько подумайте про те, чи достатньо набору правил стандарту. Цілком можливо, що відповідь буде негативною. Перечитайте документацію, і додайте ті, які Вам знадобляться.
Можна кожен раз в консолі руками пописувати якісь додаткові правила або умови, вказувати стандарт і т.д. Але, ми ж розуміємо, що це не оптимальний варіант. Тому, в корені проекту створимо файл phpcs.xml
і додамо туди наступне (в якості прикладу використовую код з свого поточного проекту):
<?xml version="1.0"?>
<ruleset name="SIDev">
<description>The Extended PSR2 coding standard.</description>
<rule ref="PEAR">
<exclude name="PEAR.Commenting.FileComment.Missing" />
<exclude name="PEAR.Commenting.ClassComment.Missing" />
<exclude name="PEAR.Commenting.ClassComment.MissingAuthorTag" />
<exclude name="PEAR.Commenting.ClassComment.MissingCategoryTag" />
<exclude name="PEAR.Commenting.ClassComment.MissingLicenseTag" />
<exclude name="PEAR.Commenting.ClassComment.MissingLinkTag" />
<exclude name="PEAR.Commenting.ClassComment.MissingPackageTag" />
<exclude name="PEAR.Commenting.FileComment.MissingVersion" />
<exclude name="PEAR.Commenting.FileComment.MissingCategoryTag" />
<exclude name="PEAR.Commenting.FileComment.MissingPackageTag" />
<exclude name="PEAR.Commenting.FileComment.MissingAuthorTag" />
<exclude name="PEAR.Commenting.FileComment.MissingLicenseTag" />
<exclude name="PEAR.Commenting.FileComment.MissingLinkTag" />
<exclude name="PEAR.Commenting.FileComment.WrongStyle" />
<exclude name="PEAR.Commenting.FunctionComment.MissingParamComment" />
<exclude name="PEAR.Commenting.FunctionComment.SpacingAfterParamType" />
<exclude name="PEAR.Commenting.FunctionCallSignature.CloseBracketLine" />
<exclude name="PEAR.Files.IncludingFile.UseInclude" />
<exclude name="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket" />
<exclude name="PEAR.Functions.FunctionCallSignature.CloseBracketLine" />
<exclude name="PEAR.Functions.FunctionCallSignature.Indent" />
<exclude name="PEAR.NamingConventions.ValidVariableName.PrivateNoUnderscore" />
<exclude name="PEAR.WhiteSpace.ObjectOperatorIndent.Incorrect" />
<exclude name="PEAR.WhiteSpace.ScopeIndent.IncorrectExact" />
</rule>
<rule ref="PSR2">
<exclude name="PSR2.Methods.FunctionCallSignature.CloseBracketLine" />
<exclude name="PSR2.Methods.FunctionCallSignature.ContentAfterOpenBracket" />
<exclude name="PSR2.Methods.FunctionCallSignature.Indent" />
</rule>
<rule ref="PSR2.Namespaces.NamespaceDeclaration.BlankLineAfter" />
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="90"/>
<property name="absoluteLineLimit" value="130"/>
</properties>
</rule>
<rule ref="Generic.Commenting.DocComment.MissingShort">
<severity>0</severity>
</rule>
<rule ref="Generic.Commenting.DocComment.TagValueIndent">
<severity>0</severity>
</rule>
<rule ref="Generic.Commenting.DocComment.ContentAfterOpen">
<severity>0</severity>
</rule>
<rule ref="Generic.Commenting.DocComment.ContentBeforeClose">
<severity>0</severity>
</rule>
<rule ref="Generic.Arrays.DisallowLongArraySyntax" />
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
<exclude-pattern>database/*</exclude-pattern>
</rule>
<rule ref="PEAR.Commenting.FunctionComment.Missing">
<exclude-pattern>tests/*</exclude-pattern>
</rule>
<file>app/</file>
<file>database/</file>
<file>routes/</file>
<file>tests/</file>
</ruleset>
Пояснення:
- як бачите, ми також підключаємо правила форматування PEAR, але оскільки потрібні не всі, ми вказуємо які з них слід виключити
- також ми виключаємо деякі з правил PSR1 і Generic (які в себе включає PSR2)
- ніколи не слід перевіряти директорії
node_modules
іvendor
, оскільки немає гарантії, що розробники пакетів, дотримуються того ж стандарту, що і Ви, навіть якщо мова йде про конкретний стандарт без усіляких додаткових правил і винятків. Крім того, не перевіряйте директорії, де будуть розміщуватися кешовані файли. Оскільки мій проект написаний на Laravel, я зробив простіше і не виключав директорії, а вказав, що потрібно перевіряти тільки ті директорії, в яких буде "жити" наш вихідний код, а саме:app
,database
,routes
,tests
Наступний крок - пояснити сніффером, де дивитися правила. Відкриємо composer.json
і в розділі scripts
пропишемо, що за замовчуванням слід використовувати кастомний стандарт, і щоб вивід помилок зробити більш наочним, слід використовувати кольори. Крім цього, також слід виконувати скрипт після встановлення та оновлення залежностей - якщо хтось / щось змінить стандарт за замовчуванням, я хочу його знову встановити:
"scripts": {
"code-sniffer": [
"./vendor/bin/phpcs --config-set default_standard phpcs.xml",
"./vendor/bin/phpcs --config-set colors 1"
],
...
"post-install-cmd": [
"@code-sniffer"
],
"post-update-cmd": [
"@code-sniffer"
]
}
Основні команди
Пояснення: нижче я навмисно пишу vendor/bin/phpcs
, щоб акцентувати увагу на тому, що пакет встановлений в проекті. Якщо ж CodeSniffer встановлений глобально, використовуйте просто phpcs
Вивести список встановлених стандартів:
vendor/bin/phpcs -i
Вивести список сніффів, використовуваних в конкретному стандарті:
vendor/bin/phpcs --standart=PSR2 -e
Перевірити форматування файлу file.php
:
vendor/bin/phpcs /path/to/file.php
Перевірити весь проект:
vendor/bin/phpcs
Перевірити весь проект з виведенням інформації про те, де конткретно зустрілись помилки:
vendor/bin/phpcs -s
Вивести код-репорт, тобто звіт з сніппетами коду, де були виявлені помилки:
vendor/bin/phpcs --report=code
Є ще й інші команди, але, на мою думку, цілком вистачає перерахованих. Справедливості заради варто відзначити, що PHP CodеSniffer також включає і code beautifier fixer, тобто можна виправити помилки (відформатувати) командою:
vendor/bin/phpcbf
але я все ж надаю перевагу php-cs-fixer, оскільки він ма. більше можливостей
PHP CS Fixer
Установка
Знову-таки подумайте встановлювати інструмент глобально, або для кожного проекту. Другий варіант:
composer require friendsofphp/php-cs-fixer --dev
Глобальна установка:
composer global require friendsofphp/php-cs-fixer
Якщо вибираєте глобальну установку, переконайтеся, що в змінну PATH
доданий шлях до бінарних файлів composer-a. Перевірте це командою:
echo $PATH
Якщо шлях відсутній - додайте. Ці дії залежать від платформи. У моєму випадку (Ubuntu 18.04) необхідно було відкрити файл .bashrc який знаходиться домашньому каталозі /home/<your_username>
і дописати в кінці файлу:
export PATH="$PATH:$HOME/.composer/vendor/bin"
Користувацькі правила
Припускаю, що стандарт вже узгоджений. У корені проекту створюємо файл .php_cs.dist
і додаємо наступний код (знову-таки беру код з мого поточного проекту):
<?php
$finder = PhpCsFixer\Finder::create()
->exclude(['bootstrap', 'database', 'node_modules', 'public', 'storage', 'tests', 'vendor'])
->notPath('*')
->in(__DIR__);
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'blank_line_after_namespace' => true,
'blank_line_before_return' => true,
'linebreak_after_opening_tag' => true,
'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'],
'no_extra_blank_lines' => true,
'no_trailing_whitespace' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'phpdoc_order' => true,
'phpdoc_separation' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_trim' => true,
'single_blank_line_at_eof' => true,
'single_blank_line_before_namespace' => true,
])
->setFinder($finder);
Зверніть увагу, що крім стандарту PSR2 також вказані додаткові правила, які буде застосовано під час форматування. Наприклад, видалення непотрібних імпортів, видалення непотрібних операторів else
і return
, сортування phpdoc блоків і поділ їх на групи, видалення зайвих порожніх рядків і т.д. Всі доступні інструкції описані в документації - дуже раджу присвятити одні вихідні вивченню можливостей фіксери і просто поекспериментувати.
Автоформат
На відміну від CodeSniffer-а фіксеру не треба пояснювати де брати конфігурацію - він сам перегляне поточний каталог на предмет наявності необхідного файлу, і, якщо такий є, інструмент його підхопить. виконати автоформатування:
vendor/bin/php-cs-fixer fix
// или
php-cs-fixer fix
Если не указано иного, php-cs-fixer будет создавать файл с кэшем .php_cs.cache
- сразу добавьте название этого файла в .gitignore
Основні команди
Вивести список команд:
php-cs-fixer list
Вивести опис правил, які входять в конкретний стандарт:
php-cs-fixer describe @PSR2
І, як вже було сказано, відформатувати (для різноманітності вкажемо стандарт Symfony):
php-cs-fixer fix --rules=@Symfony /path/to/file.php
У випадку з fixer-ом я вважаю за краще використовувати не консоль, а налаштувати PhpStorm для використання цього інструменту, додати гарячі клавіші і використовувати за місцем в конкретних файлах.
Git pre-commit hook
Все це чудово, але не матиме ніякого сенсу, якщо вся команда не буде дотримуватися встановлених правил. Рішення - не пропускати коммітов з не відформатовані кодом. Для цього скористаємося перехоплювачами (хуками) Git. У корені проекту створимо файл pre-commit.sh
і скопіюємо в нього наступне:
#!/bin/sh
PASS=true
echo "\nValidating PHPCS:\n"
which ./vendor/bin/phpcs &> /dev/null
if [ $? -eq 1 ]; then
echo "\033[41mPlease install PHPCS\033[0m"
exit 1
fi
./vendor/bin/phpcs -s
if [ $? -eq 0 ]; then
echo "\033[32mPHPCS Passed! \033[0m"
else
echo "\033[41mPHPCS Failed! \033[0m"
PASS=false
fi
echo "\nPHPCS validation completed!\n"
if ! $PASS; then
echo "\033[41mCOMMIT FAILED:\033[0m Your commit contains files that should pass PHPCS but do not. \n"
echo "\033[41mCOMMIT FAILED:\033[0m Please fix the PHPCS errors and try again.\n"
exit 1
else
echo "\033[42mCOMMIT SUCCEEDED\033[0m\n"
fi
exit $?
Взагалі-то хуки git-а повинні розміщуватися в директорії projects/.git/hooks
, але справа в тому, що директорії .git немає в віддаленому репозиторії - вона створюється на локальній машині тільки після ініціалізації гіта або клонування проекту. Тобто якимось чином треба покласти цей хук на локальних машинах інших розробників і змінити права, дозволивши файлу виконуватися. І знову на допомогу приходить composer.json
. Створимо відповідний скрипт, і попросимо виконувати його після кожної інсталяції або апдейта. Разом з попереднім кодом розділ scripts
файлу composer.json
буде виглядати так:
"scripts": {
"code-sniffer": [
"./vendor/bin/phpcs --config-set default_standard phpcs.xml",
"./vendor/bin/phpcs --config-set colors 1"
],
"pre-commit-check": [
"cp pre-commit.sh .git/hooks/pre-commit",
"chmod +x .git/hooks/pre-commit"
],
...
"post-install-cmd": [
"@code-sniffer",
"@pre-commit-check"
],
"post-update-cmd": [
"@code-sniffer",
"@pre-commit-check"
]
}
Підведемо підсумки. Ми використовуємо:
- PHP CodeSnifferдля виведення інформації про помилки форматування
- PHP CS Fixerдля автоформатування і виправлення помилок
- Git pre-commit хукдля запобігання попаданню неформатованого коду в репозиторій шляхом відхилення коммітів
Увага: php-cs-fixer виправить тільки ті помилки, які він може виправити, не більше. Наприклад, якщо у Вас не написаний phpdoc, fixer за вас його не напише. Пам'ятайте про це.
На цьому на сьогодні все. Успіхів!