Автор фото: Stable Diffusion, pure Evolution v2
PHP Rector и как он вам [не] поможет
Все, кто планировал переход на новые версии PHP слышали о проекте Rector для автоматизации рефакторинга и адаптации новых фич. Каждый кто завершал такой переход может рассказать пачку историй о том как и когда всё пошло не по плану.
Что такое Rector
Rector это инструмент автоматизации рефакторинга и апгрейда кодовой базы. Он поможет с базовыми вещами вроде добавления тайп-хинтов и конвертацией PhpDoc в аттрибуты. Более того, типичные сценарии объединены в наборы правил: например набор Php52 для миграции на PHP 5.2 содержит сразу два правила: ContinueToBreakInSwitchRector и VarToPublicPropertyRector, которые заменят continue на break в switch-конструкциях и заменят ключевое слово var на public в объявлении полей класса, соответственно. Удобно!
В чём подвох?
Давайте рассмотрим типичный пример апгрейда PHP между мажорными версиями - ведь именно это опаснее всего и именно в таких случаях мы хотим подстелить соломку. К примеру, пусть мы хотим обновить наш страшный legacy проект с PHP 5.6 на PHP 7.0. Смотрим на несовместимые изменения в PHP 7.0 и видим среди прочих:
- Negative bitshifts теперь бросают эксепшен, однако в Rector нет правил на то, чтобы детектить такие случаи
- Удалены все ext/mysql и ext/mssql функции - Rector также не подсветит вам что вы их испльзуете
- Функции
set_socket_blockingиset_magic_quotes_runtime(с алиасомmagic_quotes_runtime) удалены - не обнаруживается Rector
и др.
Что лишнего в Rector?
В то же самое время в наборе правил Rector для Php70 есть следующие штуки:
- Использование
<=>вместо тернарных операторов - Замена if/else и тернарных операторов на оператор
<=> - Замена if+isset и тернарных операторов на
?? - Замена вложенных вызовов
dirname()на один с указанием глубины
Хорошие правила, полезные, но не первой необходимости. Такие правки увеличивают и без этого потенциально крупное изменение в коде, усложняя процесс ревью. В целом отлично когда вы рутинно апгрейдитесь с одной версии пакета на другую или между минорными версиями языка. Для случаев когда вы просто хотите чтобы при апгрейде ничего не сломалось, нужен более лаконичный набор правил.
Какие есть альтернативы?
А в общем-то альтернативы не то чтобы есть. phpstan и psalm могут помочь привести в порядок кодовую базу в соответствии с наборами правил, однако точно так же базовые наборы обычно слишком широки и вносят слишком много лишних изменений.
Важный урок, который я вынес из общения с этими инструментами: эти инструменты не сделают волшебно, бесшовно и безболезненно.
Некоторые автоматизированные правки могут прикрыть вам тыл, тогда как другие внесут ошибки ковариантности и контравариантности в наследовании. Такие ошибки интерпретатор отловит при первой же попытке запустить проект, но что если ошибки могут проявиться только в райнтайме?
Например, добавленный declare(strict_types=1); сделает ваш код более строгим и там где раньше срабатывало приведение типов, теперь появится ошибка!
Так что делать-то?
В конечном итоге мы хотим без ошибок выкатить в продакшен код на новой версии языка.
Это означает что мы хотим как можно больше ошибо найти на ранних стадиях перехода - чем раньше, тем лучше, тем дешевле. Так что самое очевидное - обложитесь тестами.
Если тестами обкладываться сложно, долго и дорого (потому что у вас legacy) - найдите ключевой функционал и покройте тестами хотя бы его. Вероятно чтобы покрыть его тестами вам будет нужно сначала вручную критические части кода хорошенько отрефакторить и например уложить в классы с полноценным внедрением зависимостей, вынесением низкоуровневой работы с БД или сетью в отдельные классы, внедрением работы с интерфейсами и абстракцией вместо конкретных реализаций, и в целом обмазывании себя типизацией со всех сторон.
И все проблемы автоматических инструментов сводятся к тому что они могут быть лишь настолько умными, насколько это возможно. Если ваша функция работы с БД принимает аргумент $ref типа int[]|string[]|false|null|mixed, то поможет вам только собственный опыт в том как этот клубок распутать и что делать дальше, потому что применение автоматизации здесь совершенно невозможно.
В заключение
Может показаться что я утверждаю что такие инструменты как Rector, psalm и phpstan не нужны, однако это совершенно не так. Я лишь подсвечиваю тот момент, которому не всегда деляют много внимания в диферамбах о том, как быстро и легко обновить весь проект с егозависимостями.
Будьте готовы к тому что инструменты сделают хуже или привнесут неочевидные ошибки, однако эти же инструменты помогут найти самые проблемные участки кода, за которые уже придётся взяться вручную.
Успешного вам обновления.