Обновление версии Laravel или PHP в большом проекте — это всегда десятки часов рутины: заменить устаревшие хелперы, переписать legacy-фабрики, добавить типы, вычистить мёртвый код. Делать это руками долго и опасно — легко пропустить место или внести ошибку. Здесь на помощь приходит Rector — инструмент, который анализирует код на уровне абстрактного синтаксического дерева (AST) и применяет заранее описанные правила трансформации автоматически.
В этой статье разберём на практике, как подключить Rector к Laravel-проекту, безопасно обновить версию PHP и фреймворка, навести порядок в коде с помощью наборов правил и встроить всё это в CI. Все команды и конфиги проверены по официальной документации getrector.com и пакету driftingly/rector-laravel (актуальная версия 2.5.0).
Что такое Rector и зачем он нужен
Rector — это CLI-инструмент для PHP, который умеет делать две вещи: instant upgrades (автоматически применять breaking changes при переходе между версиями PHP и фреймворков) и refactoring (улучшать качество кода — добавлять типы, упрощать конструкции, удалять дублирование). В отличие от обычного поиска с заменой по регулярным выражениям, Rector понимает структуру кода: он строит AST, находит нужные узлы и переписывает их корректно, сохраняя форматирование.
Работает Rector нативно на PHP 7.2 и выше. Это значит, что даже старый проект можно постепенно подтягивать к современным стандартам. Главный принцип работы с инструментом — не торопиться: применять правила небольшими порциями, чтобы каждый pull request было легко отревьюить и смержить.
Установка и первый запуск
Ставим Rector как dev-зависимость через Composer:
composer require rector/rector --dev
Теперь создадим базовый конфиг rector.php в корне проекта. Современный синтаксис конфигурации — текучий (fluent) интерфейс через RectorConfig::configure():
<?php
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/routes',
__DIR__ . '/tests',
]);
Запускать Rector нужно всегда сначала в режиме предпросмотра — флаг --dry-run покажет диф изменений, но не тронет файлы:
vendor/bin/rector --dry-run
Когда вы убедились, что предложенные изменения корректны, применяем их уже без флага:
vendor/bin/rector
Обновление PHP — по одному набору за раз
Самая частая задача — подтянуть синтаксис под актуальную версию PHP. Добавляем в конфиг метод withPhpSets():
return RectorConfig::configure()
->withPaths([__DIR__ . '/app', __DIR__ . '/tests'])
->withPhpSets();
Но здесь есть важный нюанс. Пустой withPhpSets() возьмёт версию из composer.json и применит все наборы вплоть до неё — это могут быть сотни правил разом. Ревьюить такой PR невозможно. Поэтому документация Rector рекомендует включать версии по одной:
return RectorConfig::configure()
->withPaths([__DIR__ . '/app', __DIR__ . '/tests'])
->withPhpSets(php83: true);
Прогнали, проверили диф, смержили — и только потом переходим к php84: true и дальше. Такой пошаговый подход превращает рискованную миграцию в серию маленьких безопасных коммитов.
Наборы качества кода
Помимо версионных апгрейдов, Rector умеет улучшать сам код. Для этого есть «подготовленные наборы», которые включаются через withPreparedSets():
return RectorConfig::configure()
->withPaths([__DIR__ . '/app', __DIR__ . '/tests'])
->withPreparedSets(
deadCode: true,
codeQuality: true,
typeDeclarations: true,
earlyReturn: true,
);
Набор deadCode вычистит недостижимые ветки и неиспользуемые переменные, codeQuality упростит избыточные конструкции, typeDeclarations добавит типы аргументов и возвращаемых значений, а earlyReturn переведёт вложенные условия на ранний возврат. Как и с PHP-наборами, лучше включать их по одному.
Rector для Laravel
Базовый Rector ничего не знает о специфике фреймворка. Чтобы получить правила именно для Laravel (и связанных пакетов вроде Cashier и Livewire), ставим расширение, которое поддерживает сообщество:
composer require --dev driftingly/rector-laravel
Самый удобный сценарий — автоматическое определение нужных правил по версиям пакетов из composer.json. Для этого подключаем провайдер наборов:
<?php
use Rector\Config\RectorConfig;
use RectorLaravel\Set\LaravelSetProvider;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app', __DIR__ . '/tests'])
->withSetProviders(LaravelSetProvider::class)
->withComposerBased(laravel: true);
Если хочется контролировать целевую версию вручную, используйте уровневые наборы из LaravelLevelSetList — наборы для старших версий включают в себя младшие:
use RectorLaravel\Set\LaravelLevelSetList;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app'])
->withSets([
LaravelLevelSetList::UP_TO_LARAVEL_130,
]);
Тематические наборы и точечные правила Laravel
Отдельно от версионных апгрейдов есть наборы из LaravelSetList, улучшающие конкретные аспекты кода:
use RectorLaravel\Set\LaravelSetList;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app'])
->withSets([
LaravelSetList::LARAVEL_CODE_QUALITY,
LaravelSetList::LARAVEL_COLLECTION,
LaravelSetList::LARAVEL_IF_HELPERS,
LaravelSetList::LARAVEL_ARRAY_STR_FUNCTION_TO_STATIC_CALL,
]);
Например, набор LARAVEL_IF_HELPERS заменит конструкции вида if (...) { abort(403); } на лаконичный хелпер abort_if(...), а LARAVEL_COLLECTION перепишет цепочки методов коллекций на более читаемые и эффективные варианты.
Часть правил требует ручной настройки — их добавляют через withConfiguredRule(). Классический пример — вычистка отладочных вызовов перед релизом:
use RectorLaravel\Rector\FuncCall\RemoveDumpDataDeadCodeRector;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app'])
->withConfiguredRule(RemoveDumpDataDeadCodeRector::class, [
'dd', 'dump', 'var_dump',
]);
Полезны и такие правила, как RouteActionCallableRector (превращает строковые экшены вида 'UserController@index' в массив [UserController::class, 'index']) и WhereToWhereLikeRector (заменяет where('col', 'like', $v) на современный whereLike('col', $v)).
Пропуск правил и файлов
Не каждое правило безопасно применять везде. Чтобы исключить конкретное правило или каталог, используйте withSkip():
return RectorConfig::configure()
->withPaths([__DIR__ . '/app'])
->withSkip([
__DIR__ . '/app/Legacy',
RemoveDumpDataDeadCodeRector::class,
]);
Интеграция в CI/CD
Чтобы код не «разъезжался», запускайте Rector в пайплайне в режиме проверки. В CI используется тот же --dry-run: если Rector нашёл, что можно изменить, он завершится с ненулевым кодом и провалит сборку. Пример шага для GitHub Actions:
- name: Rector check
run: vendor/bin/rector --dry-run
Так разработчики будут обязаны прогонять Rector локально до пуша. Для ускорения на больших проектах включите кеширование — Rector хранит результаты анализа и не перепроверяет неизменённые файлы.
Итоги
Rector превращает миграции и рефакторинг из многодневной рутины в управляемый и предсказуемый процесс. Ключевые принципы работы: ставим rector/rector и расширение driftingly/rector-laravel, всегда сначала запускаем --dry-run, включаем наборы по одному и оформляем небольшие pull request’ы. Начните с обновления PHP по версиям, затем подключите наборы качества кода и Laravel-специфичные правила, а финальным шагом встройте проверку в CI. В результате вы получаете кодовую базу, которая обновляется быстро, безопасно и без героических усилий всей команды.