Внедрение PHP-CS-Fixer в легаси PHP-проекты

Источник: «Introducing PHP-CS-Fixer into legacy projects»
Если вы работаете над унаследованным PHP-проектом, какой может быть стратегия внедрения PHP-CS-Fixer, снижающая риск и приглашающая других разработчиков к сотрудничеству?

Вы работаете над унаследованным legacy PHP-проектом и хотите использовать friendsofphp/php-cs-fixer для обеспечения согласованного стандарта кодирования. Но вы не знаете, как это сделать, не вызывая проблем.

Какой может быть стратегия внедрения PHP-CS-Fixer в ваш легаси PHP-проект, которая снижает риски и приглашает других разработчиков к сотрудничеству?

Требования

Если вы хотите, чтобы внедрение PHP-CS-Fixer в ваш легаси PHP-проект прошло успешно, должны выполняться следующие требования:

Установка PHP-CS-Fixer

Вы можете установить PHP-CS-Fixer с помощью composer, phive или загрузив PHAR со страницы релизов. Но вы можете подробно изучить варианты установки и инструкции PHP-CS-Fixer в README.md проекта. Я не буду повторять их.

Я рекомендую использовать composer для установки PHP-CS-Fixer, чтобы вы могли получать автоматические обновления зависимостей от Dependabot, Renovatebot или подобных сервисов. Если вы устанавливаете PHp-CS-Fixer с помощью phive или загружаете его вручную со страницы релизов, вам необходимо обновлять PHP-CS-Fixer вручную.

Ваш проект ещё не использует composer? Почему бы не начать использовать composer, сделав PHP-CS-Fixer своей первой зависимостью при разработке?

Вы не можете использовать composer, потому что ваш проект работает на версии младше PHP 5.3? Почему бы не использовать другую версию PHP для запуска средств разработки?

Я также рекомендую использовать последнюю версию PHP-CS-Fixer, чтобы вы могли воспользоваться возможностями и исправлениями, которые разработчики постоянно добавляют в инструмент.

Вы не можете использовать последнюю версию PHP-CS-Fixer, потому что ваш проект ещё не использует PHP 7.4 или выше? Опять, почему бы не использовать другую версию PHP для запуска средств разработки?

Например, последние пару недель я был занят обновлением проекта с PHP 5.6 до PHP 8.1. После настройки локальной среды разработки, работающей на PHP 5.6 в Docker, я настроил рабочий процесс GitHub Actions, который использует PHP 8.1 для установки и запуска инструментов разработки, включая PHP-CS-Fixer.

Если я могу использовать PHP 8.1 для запуска инструментов разработки проекта, работающего на PHp 5.6, то и вы сможете.

Ключом к успеху является тщательная настройка инструментов разработки.

Добавление базовой конфигурации для PHP-CS-Fixer

PHP-CS-Fixer требует две вещи для отчёта и исправления нарушений стандарта кодирования: список проверяемых файлов, и конфигурацию правил и наборов правил, которые он использует для создания и настройки соответствующих средств исправления.

Файл конфигурации .php-cs-fixer.php приведённый ниже, настраивает finder в setFinder($finder) и пустой массив правил в setRules([]).

<?php

$finder = PhpCsFixer\Finder::create()
->exclude([
'.build/',
'.docker/',
'.github/',
])
->ignoreDotFiles(false)
->in(__DIR__)
->name('.php-cs-fixer.php');

$config = new PhpCsFixer\Config();

$config
->setFinder($finder);
->setRules([]);

return $config;

finder позволяет настроить список каталогов, исключений, имён файлов и т.д. и возвращает список файлов, которые должен проверять PHP-CS-Fixer.

В зависимости от макета вашего проекта, ваша конфигурация finder может выглядеть по-другому.

Пустой массив правил переопределит конфигурацию правил по умолчанию. PHP-CS-Fixer анализирует файл PHP до и после применения средств исправления, чтобы гарантировать, что он не пытается исправить файл, содержащий недопустимый PHP код, и не оставит после себя файл с недопустимым PHP-кодом. Когда PHP-CS-Fixer находи файл, который содержит недопустимый PHP-код, он пропустит его исправление и выдаст предупреждение.

Даже с пустым набором правил PHP-CS-Fixer является ценным инструментом, поскольку он может найти файлы содержащие недопустимый PHP-код.

Теперь, когда у вас есть первоначальная конфигурация для PHp-CS-Fixer, пришло время приступить к работе с PHP-CS-Fixer.

Запуск PHP-CS-Fixer в GitHub Actions

Как уже упоминалось, вам необходимо запускать PHP-CS-Fixer в двух средах: в среде непрерывной интеграции и в локальной среде разработки.

Следующая команда запускает PHp-CS-Fixer с параметром --dry-run и сообщает о нарушениях стандарта кодирования:

vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff --dry-run --verbose

В зависимости от того, как вы установили PHP-CS-Fixer и назвали файл конфигурации, команда может выглядеть по-разному.

Но важно использовать параметр --dry-run при запуске PHP-CS-Fixer в системе непрерывной интеграции: PHP-CS-Fixer завершит своё выполнение с ненулевым кодом выхода, когда обнаружит нарушения стандарта кодирования и прервёт сборку.

Я чувствую себя в GitHub как дома и люблю использовать GitHub Actions как систему непрерывной интеграции. Приведённый ниже рабочий процесс GitHub Actions выполнит Checkout вашего репозитория, настроит PHP, установит зависимости с помощью composer и запустит PHP-CS-Fixer с параметром --dry-run.

name: "Integrate"

on:
pull_request: null
push:
branches:
- "main"

jobs:
coding-standards:
name: "Coding Standards"

runs-on: "ubuntu-latest"

strategy:
matrix:
php-version:
- "8.1"

steps:
- name: "Checkout"
uses: "actions/checkout@v3.5.0"

- name: "Set up PHP"
uses: "shivammathur/setup-php@v2.24.0"
with:
coverage: "none"
php-version: "$NaN"

- name: "Validate composer.json and composer.lock"
run: "composer validate --ansi --no-check-publish"

- name: "Install locked dependencies with composer"
run: "composer install --ansi --no-interaction --no-progress"

- name: "Run friendsofphp/php-cs-fixer"
run: "vendor/bin/php-cs-fixer fix --ansi --config=.php-cs-fixer.php --diff --dry-run --verbose"

В зависимости от настройки вашего проекта и системы непрерывной интеграции ваша конфигурация может отличаться, но шаги будут примерно такими же.

С помощью этого рабочего процесса GitHub Actions вы не сможете закоммитить и запушить PHP-код, который не соответствует вашим стандартам кодирования, без сбоя сборки.

Вы можете улучшить работу по стандартам кодирования, кэшируя зависимости, установленные с помощью composer в файл кэша PHP-CS-Fixer между запусками.

Запуск PHP-CS-Fixer в среде разработки

Следующая команда запустит PHP-CS-Fixer и исправит нарушения стандарта кодирования:

vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff --verbose

В зависимости от того, ка вы установили PHP-CS-Fixer и назвали файл конфигурации, команды может отличаться. Опять, вам не нужны сообщения о нарушениях стандарта кодирования в среде разработки; вам нужны исправления.

Эта команда немного длинная для ввода, и если вы хотите часто запускать PHp-CS-Fixer, вы, вероятно захотите использовать запуск задач или какой-либо другой инструмент облегчающий запуск инструментов в среде разработки.

Если вы используете Makefile, добавьте в свой Makefile цель coding-standards.

.PHONY: coding-standards
coding-standards: vendor
vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff --verbose

vendor: composer.json composer.lock
composer validate --strict
composer install --no-interaction --no-progress

Установив цель coding-standards в свой Makefile, вы можете выполнить следующую команду, чтобы позволить PHp-CS-Fixer исправить нарушения стандартов кодирования:

make coding-standards

Если вы предпочитаете сценарии composer, добавьте сценарий coding-standards в ваш composer.json.

{
"scripts": {
"coding-standards": "@php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff --verbose"
}
}

Задав сценарий coding-standards в файле composer.json, вы можете запустить следующую команду, чтобы позволить PHP-CS-Fixer исправить нарушения стандартов кодирования:

composer coding-standards

Развитие конфигурации PHP-CS-Fixer

теперь, когда вы используете PHP-CS-Fixer в своей системе непрерывной интеграции и среде разработки, осталось одно: вы хотите, чтобы PHP-CS-Fixer применял исправления совместимые с версией PHP, которую вы используете в продакшене, а пока у вас только пастой массив правил.

Вот что хорошо помогло мне.

Во-первых, получите полный список правил для всех доступных исправлений, например из Пользовательского набора правил в ergebnis/php-cs-fixer-config-template.

Во-вторых, настройте конфигурацию правил, отключив все исправления. На момент написания статьи было около 250 правил.

<?php

$finder = PhpCsFixer\Finder::create()
->exclude([
'.build/',
'.docker/',
'.github/',
])
->ignoreDotFiles(false)
->in(__DIR__)
->name('.php-cs-fixer.php');

$config = new PhpCsFixer\Config();

$config
->setFinder($finder);
->setRules([
'align_multiline_comment' => false,
'array_indentation' => false,
'array_push' => false,
'array_syntax' => false,
// ...
'visibility_required' => false,
'void_return' => false,
'whitespace_after_comma_in_array' => false,
'yoda_style' => false,
]);

return $config;

В качестве альтернативы, если вы уже делитесь конфигурациями PHP-CS-Fixer между проектами, переопределите существующую конфигурацию правил, отключив все правила.

В-третьих, просмотрите список правил сверху вниз, снизу вверх или как вам больше подходит, а затем включите и настройте по одному правилу за раз — но только тогда, когда это возможно. Убедитесь, что применяются исправления совместимые с вашей версией PHP, работающей в продакшене.

Пример одновременного включения и настройки правила

Давайте рассмотрим конкретные примеры, рассматривая и настраивая правило array_syntax, когда вы работаете с pull request и обзорами кода перед слияниями.

  1. Сначала создайте ветку, например feature/array-syntax.
  2. Скопируйте имя правила.
  3. Перейдите на https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.16.0 (измените 3.16.0 версией PHP-CS-Fixer, которую используете). Нажмите T, чтобы открыть навигацию по файлам. Вставьте имя правила в поле ввода (в данном случае array-syntax). Выберите файл документации в раскрывающемся списке (array_syntax.rst).
  4. Проверьте документацию. Можно ли безопасно включить средство исправления array-syntax? Например, в PHP 5.4 появился сокращённый синтаксис массива. Ваш легаси PHP-проект работает на PHP 5.3 в продакшене? Тогда вам, вероятно следует включить средство исправления array_syntax и указать параметру syntax значение long (['syntax' => 'long']). Или ваш легаси проект работает на PHP 5.4 или выше? Тогда вам, вероятно следует включить средство исправления и указать параметру syntax значение short (['syntax' => 'short']).
  5. Сделайте коммит и пуш изменений файла конфигурации .php-cs-fixer.php.
  6. Откройте pull request и задокументируйте в теле, что этот pull request активирует array_syntax средство исправления. Открытие pull request запустит рабочий процесс GitHub Actions.
  7. Запустите PHP-CS-Fixer в вашей среде разработки. Если PHp-CS-Fixer применил исправления, рабочий процесс GitHub Actions завершится ошибкой. Сделайте коммит и пуш исправлений отдельным коммитом, чтобы сборка прошла успешно.
  8. Посмотрите изменения в pull request и убедитесь, что они имеют смысл. При необходимости попросите кого-нибудь ещё посмотреть ваш pull request.
  9. Выполните слияние pull request и удалите ветку.
  10. Проверьте ветку по умолчанию в среде разработки.
  11. Получите из репозитория последние изменения.
  12. Удалите ветку feature.

Повторите описанные шаги для каждого правила.

Вы можете найти пример pull request, который включает и настраивает средство исправления синтаксиса array_syntax для официального веб-сайта PHP здесь

Недостатки включения и настройки одного правила за раз

На момент написания этой статьи PHP-CS-Fixer поставляется с 250 правилами. Включение и конфигурирование одного правила за раз может занять некоторое время, особенно если вы работаете с pull request и требуете ревью кода перед слиянием. Но вы можете значительно ускорить процесс используя Ship/Show/Ask

Если вы не хотите тратить время, удачи вам в применении всех правил и одновременном просмотре всех исправлений!

Преимущества включения и настройки одного правила за раз

На мой взгляд, включение и настройка одного правила за раз имеет следующие преимущества:

Дополнительные материалы

Предыдущая Статья

Новое в Symfony 6.3 — Улучшения производительности

Следующая Статья

Улучшение Laravel шаблонов с Blade директивами