Rector – это утилита для обновления кодовой базы PHP проекта под современные стандарты. И речь идёт не о PSR-12, а о более интересных преобразованиях, сейчас расскажу!
Представьте, что мы хотим добавить больше типизации в код, например, проставить типы возвращаемых из функций значений. Если код написан более-менее прямолинейно и без лишней магии, то, вообще говоря, статический анализ может вывести тип возвращаемого значения, не смотря на весь динамизм PHP. Почему бы не автоматизировать эту задачу?
Вместо ручного изучения и правки тысяч или десятков или сотен тысяч строк кода, запускаем утилиту Rector и она автоматически проставляет типы возвращаемых из функций значений!
А что насчёт простановки типов свой классов, которые появились в PHP 7.4? Вполне посильная задача для статического анализа и автоматизации.
Или, например, заменить использование Laravel фасадов на Dependency Injection.
Пару недель назад я опробовал Rector на одном не самом старом, но и не самом новом проекте, подтянул кодовую базу до приятного современного вида и в целом остался доволен.
Для начала нужно установить саму утилиту Rector как composer пакет в dev секцию. Под капотом используется php-parser
от Никиты Попова, строится абстрактное синтаксическое дерево, затем применяются модификации.
Установить с помощью composer require
мне не удалось – попал на конфликты версий с уже установленными пакетами. Что хорошо, в README репозитория Rector есть упоминание как раз такого случая – при конфликтах рекомендуется использовать заранее скомпилированную phar версию, чем я и воспользовался. Также есть Docker образ, на случай если локальная версия PHP не подходит для запуска Rector.
В комплекте идёт более 500 правил или преобразований – их можно включать или выключать по отдельности с помощью файла конфигурации. Есть также готовые наборы из правил, sets, например: symfony40 или code-qualiy – что конкретно скрывается за этими наборами нужно смотреть в документации.
Можно сформировать свои собственные наборы или даже написать собственное преобразование.
Также настройками задаются пути к директориям с кодом для обновления. Отдельный флаг --match-git-diff
запустит Rector только на изменённых в последнем коммите файлах, это удобно для ускорения процесса, особенно если кодовая база большая.
Так называемый dry-run
режим показывает будущие изменения в консоли, но не применяет их к файлам.
Я подошел к процессу следующим образом: сначала взял парочку наборов (sets): code quality и простановку типов. С типами вроде всё понятно, мне нравятся явно указанные типы, пусть будут! Что такое code-quality? Видимо, что-то связанное с качеством кода, а мы за всё хорошее.
Запустил не на всём проекте, а на одной из директорий, но без флага dry-run
– мне удобнее смотреть diff не в консоли, а в PhpStorm.
Оказалось, что всё не так-то и хорошо, особенно с простановкой типов параметров функций – при внимательном просмотре нашел много косяков.
Откатил все изменения из решил подойди к вопросу более вдумчиво. Открыл в документации список всех правил (которых, напомню, более 500) и стал внимательно читать, что каждое из них делает. Удобно, что под каждым правилом есть фрагмент кода до и после применения, очень наглядно.
Все правила разделены на категории, есть оглавление — это позволило сначала сосредоточиться на наиболее интересных лично для меня.
Есть категории относящиеся к конкретным фреймворкам и библиотекам – Symfony, Laravel, CakePHP Doctrine, Guzzle, PHPOffice и так далее. Есть правила, обновляющие код с использованием новых фишек самого языка, соответственно они разделены на группы по версиям PHP. Например, в PHP 7.3 появились функции array_key_first
и array_key_last
– преобразование находит фрагменты кода, где последовательно применяется reset
и key
и заменяет их на array_key_first
, аналогично end
и key
заменяется на array_key_last
.
А в PHP 7.4 появилась короткая запись анонимных функций – если в коде есть анонимки, состоящие из одного return, то они будут преобразованы к короткому синтаксису. И так далее, много мелких, достаточно безопасных и освежающих код преобразований!
Насчёт типов параметров: есть преобразование, которое подставляет тип на основе DocBlock комментариев, а есть и обратное, генерирует DocBlock комментарии на основе кода. Также в некоторых случаях типы могут быть выведены по способу использования переменной.
Чтобы прочувствовать, что именно работает хорошо, а что нет, я постепенно добавлял в конфиг по одному правилу, запускал, изучал diff.
Как я уже говорил, с простановкой типов параметров функции было достаточное число ошибок, которые пришлось поправить вручную.
А что-то мне совсем не понравилось, и я откатил. Например, разделитель тысяч в числовых константах в виде подчёркивания. В примерах обычно показывают, как замечательно начинают выглядеть миллионы и миллиарды, где от нулей в глазах рябит. На деле оказалось, что в моей кодовой базе большие числовые константы – это в основном тысячи, а прядок лишь одной тысячи отделённый подчёркиванием лично мне смотрится не очень.
После каждого успешного применения и проверки я делал отдельный коммит.
Что интересно, Rector ведь можно запускать на регулярной основе, как автоматическое форматирование кода. Главное выделить тот набор правил, в которых уверен и которые подходят для данного проекта. Например, всё с теми же типами параметров, после ручной правки косяков, я отключил это правило и на регулярной основе запускать его не планирую.
В целом у меня ушло 2 дня на работу плотную с кодовой базой в 100 тыс строк кода и постепенным пошаговым применением различных правил, просмотром дифов и обязательным прогоном тестов. Сами понимаете, без тестов в таком рефакторинге никуда.
Но даже если тестов нет или их мало, я считаю, что стоит попробовать Rector, хотя бы пройтись по категориям связанным с обновлением версий PHP, чтобы наглядно вспомнить, что было нового в последних версиях и применить эти преобразования – там всё достаточно надёжно с точки зрения статического анализа, изменения простые и понятные.
Пишите на современном PHP, а Rector вам поможет обновить уже написанный код!