Rector для PHP: полное руководство с примерами
Rector — это мощный инструмент для автоматического рефакторинга PHP-кода. Он представляет собой библиотеку, которая анализирует ваш код с помощью абстрактного синтаксического дерева (AST) и применяет к нему заданные правила трансформации. Это позволяет автоматизировать рутинные задачи по обновлению кода, такие как переход на новые версии PHP, внедрение новых фич языка, исправление устаревших конструкций и улучшение качества кода в целом.
Зачем нужен Rector? В современной PHP-разработке проекты быстро растут, и поддерживать их в актуальном состоянии становится всё сложнее. Ручной рефакторинг тысяч строк кода — это трудоёмкий и подверженный ошибкам процесс. Rector решает эту проблему, позволяя выполнять сложные преобразования за секунды. Он особенно полезен при обновлении с PHP 7.x на 8.x, при переходе с устаревших фреймворков (например, Zend Framework 1 на Laminas), или при внедрении строгих типов и современных паттернов.
Rector работает по принципу "набор правил". Вы определяете, какие преобразования нужно выполнить (например, "заменить все вызовы array_* на str_* функции"), а Rector находит все подходящие места в коде и вносит изменения. Инструмент полностью настраиваемый: вы можете использовать готовые наборы правил от сообщества или создавать свои собственные. Rector интегрируется в CI/CD пайплайны, что позволяет автоматически проверять и исправлять код при каждом коммите.
Установка
Rector устанавливается через Composer — стандартный менеджер пакетов для PHP. Выполните следующую команду в корне вашего проекта:
composer require rector/rector --dev
Флаг --dev означает, что пакет будет установлен только для разработки, что является правильной практикой, так как Rector не нужен в production-окружении.
После установки проверьте, что всё работает:
vendor/bin/rector --version
Если вы используете глобальную установку (не рекомендуется для проектов), можно выполнить:
composer global require rector/rector
Для работы Rector требуется PHP 7.2 или выше. Рекомендуется использовать PHP 8.0+ для максимальной производительности и поддержки всех возможностей.
Быстрый старт: минимальный рабочий пример
Создадим простой PHP-файл, который нужно отрефакторить. Допустим, у нас есть устаревший код, использующий create_function(), который был удалён в PHP 8.0.
Шаг 1. Создайте файл example.php:
<?php
$oldFunction = create_function('$a, $b', 'return $a + $b;');
echo $oldFunction(1, 2); // Выведет 3
Шаг 2. Создайте конфигурационный файл rector.php в корне проекта:
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Function_\CreateFunctionToAnonymousFunctionRector;
return static function (RectorConfig $rectorConfig): void {
// Указываем, какие директории анализировать
$rectorConfig->paths([
__DIR__ . '/example.php',
]);
// Регистрируем правило для замены create_function на анонимные функции
$rectorConfig->rule(CreateFunctionToAnonymousFunctionRector::class);
};
Шаг 3. Запустите Rector:
vendor/bin/rector process --dry-run
Флаг --dry-run покажет, какие изменения будут сделаны, но не применит их. Чтобы реально изменить файлы, выполните:
vendor/bin/rector process
Результат: Файл example.php будет преобразован в:
<?php
$oldFunction = function ($a, $b) {
return $a + $b;
};
echo $oldFunction(1, 2); // Выведет 3
Этот простой пример демонстрирует основную идею: вы определяете правило, а Rector автоматически находит и исправляет все подходящие места в вашем коде.
Основные методы/функции/классы
Rector предоставляет богатый API для настройки и расширения. Ниже приведены 8 ключевых компонентов, с которыми вы будете работать чаще всего.
1. RectorConfig::paths()
Сигнатура: public function paths(array $paths): void
Что делает: Определяет список файлов или директорий, которые Rector будет анализировать и рефакторить. Это обязательный метод для конфигурации.
Пример:
<?php
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
__DIR__ . '/tests',
__DIR__ . '/app/Controllers',
]);
};
2. RectorConfig::rule()
Сигнатура: public function rule(string $rectorClass): void
Что делает: Регистрирует одно правило рефакторинга. Правило — это класс, реализующий определённое преобразование кода. Можно регистрировать как встроенные правила, так и свои собственные.
Пример:
<?php
use Rector\Config\RectorConfig;
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(NullToStrictStringFuncCallArgRector::class);
};
3. RectorConfig::sets()
Сигнатура: public function sets(array $sets): void
Что делает: Подключает готовые наборы правил (sets). Это самый удобный способ применить сразу множество правил для обновления до определённой версии PHP или для внедрения определённого стиля кода.
Пример:
<?php
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([
SetList::PHP_80,
SetList::PHP_81,
SetList::CODE_QUALITY,
]);
};
4. RectorConfig::importNames()
Сигнатура: public function importNames(bool $import = true, bool $importDocBlock = true): void
Что делает: Включает или отключает автоматический импорт имён классов (use statements). Полезно для приведения кода к современному стандарту, где все классы импортируются явно.
Пример:
<?php
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->importNames();
// или с настройками:
// $rectorConfig->importNames(true, false); // только для кода, не для docblock
};
5. RectorConfig::skip()
Сигнатура: public function skip(array $skips): void
Что делает: Позволяет исключить определённые файлы, директории или даже конкретные правила из обработки. Это необходимо, когда некоторые части кода не должны изменяться (например, сторонние библиотеки или legacy-код).
Пример:
<?php
use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Function_\CreateFunctionToAnonymousFunctionRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->skip([
// Пропустить всю директорию vendor
__DIR__ . '/vendor',
// Пропустить конкретный файл
__DIR__ . '/src/Legacy/OldFile.php',
// Пропустить конкретное правило для конкретного файла
CreateFunctionToAnonymousFunctionRector::class => [
__DIR__ . '/src/ThirdParty/SomeLibrary.php',
],
]);
};
6. RectorConfig::autoloadPaths()
Сигнатура: public function autoloadPaths(array $autoloadPaths): void
Что делает: Указывает дополнительные пути для автозагрузки классов. Это полезно, если ваш проект использует нестандартную структуру автозагрузки, и Rector не может найти классы.
Пример:
<?php
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->autoloadPaths([
__DIR__ . '/vendor/autoload.php',
__DIR__ . '/src/autoload.php',
]);
};
7. RectorConfig::phpVersionFeatures()
Сигнатура: public function phpVersionFeatures(string $phpVersion): void
Что делает: Устанавливает целевую версию PHP, для которой Rector будет генерировать код. Это важно, когда вы пишете код для одной версии, но хотите, чтобы он был совместим с более старой или новой.
Пример:
<?php
use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersion;
return static function (RectorConfig $rectorConfig): void {
// Целевая версия PHP 8.1
$rectorConfig->phpVersionFeatures(PhpVersion::PHP_81);
};
8. RectorConfig::withPhpSets()
Сигнатура: public function withPhpSets(bool $php80 = false, bool $php81 = false, bool $php82 = false, bool $php83 = false): void
Что делает: Удобный метод для включения сразу всех правил, связанных с конкретной версией PHP. Это современная альтернатива использованию sets() для версионных обновлений.
Пример:
<?php
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
// Включить все правила для PHP 8.1 и 8.2
$rectorConfig->withPhpSets(
php81: true,
php82: true,
);
};
Реальный практический пример
Рассмотрим реальный сценарий: у нас есть проект на PHP 7.4, который мы хотим обновить до PHP 8.1. В проекте используется старый код с array_* функциями, которые в PHP 8.1 имеют более строгие сигнатуры, а также устаревшие конструкторы и отсутствие типов.
Создадим файл src/UserService.php:
<?php
namespace App\Service;
class UserService
{
private $users;
public function __construct()
{
$this->users = [];
}
public function addUser($name, $email)
{
$this->users[] = ['name' => $name, 'email' => $email];
}
public function getUsers()
{
return $this->users;
}
public function findUser($email)
{
$filtered = array_filter($this->users, function($user) use ($email) {
return $user['email'] === $email;
});
return array_values($filtered)[0] ?? null;
}
public function updateUser($email, $newName)
{
foreach ($this->users as &$user) {
if ($user['email'] === $email) {
$user['name'] = $newName;
return true;
}
}
return false;
}
}
Теперь создадим конфигурационный файл rector.php:
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
]);
// Подключаем наборы правил для PHP 8.1 и качества кода
$rectorConfig->sets([
SetList::PHP_81,
SetList::CODE_QUALITY,
SetList::TYPE_DECLARATION,
]);
// Дополнительные правила
$rectorConfig->rule(AddVoidReturnTypeWhereNoReturnRector::class);
$rectorConfig->rule(TypedPropertyFromStrictConstructorRector::class);
// Включаем импорт имён
$rectorConfig->importNames();
// Исключаем тестовые файлы
$rectorConfig->skip([
__DIR__ . '/tests',
]);
};
Запустим Rector:
vendor/bin/rector process --dry-run
После применения изменений (без флага --dry-run) файл UserService.php будет преобразован в:
<?php
namespace App\Service;
class UserService
{
private array $users;
public function __construct()
{
$this