如果你看过我之前写的 Rector 入门介绍,你应该已经知道它能自动升级 PHP 版本、清理死代码、做 early return 重构。但那只是它能力的冰山一角。本质上 Rector 是一个 AST(抽象语法树)级别的代码改造引擎——只要你能用代码描述"把模式 A 改成模式 B",Rector 就能在整个项目里精确执行。这意味着它的用途远不止"升级 PHP":框架迁移、编码规范强制、大规模重命名、技术债务批量清理——这些过去需要团队花几周手工干的活,Rector 可能一条命令就搞定了。
先搞清楚一件事:Rector 到底在干什么
很多人以为 Rector 是文本替换工具的高级版。不是的。它的工作流程是这样的:
源代码 → Tokenizer → AST(语法树)→ 规则匹配 → AST 变换 → 重新生成代码
关键区别在于 AST 这一层。文本替换看到的是字符串,AST 看到的是语义。举个例子:
// 你想把 Order 改成 PurchaseOrder
// 文本替换的风险:
$order = new Order(); // ← 这个该改
echo "Order #1234 shipped"; // ← 这个不该改,但文本替换可能误伤
// AST 替换的结果:
$order = new \App\Model\PurchaseOrder(); // ← 类引用,改了
echo "Order #1234 shipped"; // ← 字符串内容,不动
这就是为什么 Rector 能安全地做全项目级改造——它知道每个符号是什么意思。

场景一:框架版本迁移
这是 Rector 除了 PHP 版本升级之外最实用的能力。主流框架都有对应的规则集:
Symfony 迁移
Symfony 的跨版本迁移是出了名的痛苦——每次大版本升级官方都有一篇几十页的迁移指南。Rector 把这些指南变成了可执行的规则:
use Rector\Config\RectorConfig;
use Rector\Symfony\Set\SymfonySetList;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withSets([
// 从 Symfony 5.4 一路升到 7.1
SymfonySetList::SYMFONY_60,
SymfonySetList::SYMFONY_61,
SymfonySetList::SYMFONY_62,
SymfonySetList::SYMFONY_63,
SymfonySetList::SYMFONY_64,
SymfonySetList::SYMFONY_70,
SymfonySetList::SYMFONY_71,
// 构造器注入改为属性注入(Symfony 推荐的新写法)
SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION,
]);
它会帮你处理的事情包括但不限于:
-
AbstractController::get()和ContainerInterface调用改成依赖注入 - 旧的事件订阅器格式改成新的 Attribute 格式
-
@Route注解改成 PHP 8 的#[Route]属性 - 配置文件格式的变更(YAML 结构调整)
- 废弃的服务 ID 替换成新的名称

Laravel 迁移
Laravel 社区维护了 driftingly/rector-laravel 扩展包:
composer require driftingly/rector-laravel --dev
use Rector\Config\RectorConfig;
use RectoLaravel\Set\LaravelSetList;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app'])
->withSets([
LaravelSetList::LARAVEL_100, // Laravel 10 的代码风格
LaravelSetList::LARAVEL_110, // 升到 Laravel 11 的变更
LaravelSetList::LARVEL_CODE_QUALITY, // Laravel 最佳实践
]);
处理的内容包括:Facade 改为构造器注入、旧的 Model::factory() 写法更新、中间件格式变更等。
PHPUnit 迁移
测试代码的迁移往往被忽略,但其实同样重要:
use Rector\PHPUnit\Set\PHPUnitSetList;
return RectorConfig::configure()
->withPaths([__DIR__ . '/tests'])
->withSets([
PHPUnitSetList::PHPUNIT_90,
PHPUnitSetList::PHPUNIT_91,
PHPUnitSetList::PHPUNIT_100,
PHPUnitSetList::PHPUNIT_110,
]);
比如 PHPUnit\Framework\TestCase 中废弃的方法会被替换成新写法,数据提供者的格式也会统一。
场景二:用 CI 强制编码规范
大多数团队的 code review 会挑出一些重复性的问题:这里缺了返回类型声明、那里用了 strpos 而不是 str_contains、这个 if 嵌套太深了……这些问题每次都说,每次都有人犯。与其在 code review 里反复唠叨,不如让 CI 来当坏人。
基础配置:不允许不符合规范的代码合进来
# .github/workflows/rector.yml
name: Code Quality Gate
on: [pull_request]
jobs:
rector:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- run: composer install
- run: vendor/bin/rector process --dry-run
PR 提交后如果 Rector 发现任何可以改进的地方,CI 就会报错。开发者需要在本地运行 vendor/bin/rector process 修复后再提交。
进阶配置:区分"必须修"和"建议修"
不是所有规则都应该阻断合并。可以把规则分成两个级别:
// rector.php — 必须执行的规范(CI 阻断)
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withSets([
SetList::TYPE_DECLARATION, // 必须有类型声明
SetList::CODE_QUALITY, // 代码质量基本要求
SetList::DEAD_CODE, // 不能有死代码
SetList::EARLY_RETURN, // 嵌套 if 要 early return
])
->withSkip([
__DIR__ . '/src/Migrations/*', // 数据库迁移文件除外
]);
// rector-suggest.php — 建议性优化(CI 不阻断,仅报告)
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withSets([
SetList::PRIVATIZATION, // 建议 public 改 private
SetList::NAMING, // 命名规范建议
]);
# CI 中必须通过的跑 dry-run(有改动就失败)
- run: vendor/bin/rector process --config rector.php --dry-run
# 建议性的只输出报告,不阻断
- run: vendor/bin/rector process --config rector-suggest.php --dry-run || true
- run: echo "以上是建议性优化,不影响合并"
这样做的效果是:code review 可以专注于逻辑和架构问题,不再浪费时间在格式和风格上。
场景三:大规模重命名和命名空间重组
项目发展到一定阶段,总会遇到重构需求:某个类要改名、一组类要从 App\Legacy 迁到 App\Core、接口命名要从 IUserInterface 改成 UserInterface。这种全项目范围的手工修改既枯燥又容易漏。
类名全项目替换
入门文章里演示过基本的 RenameClassRector。实际项目中你可能一次要改几十个类:
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\Name\RenameClassRector;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withRules([RenameClassRector::class])
->withConfiguredRule(RenameClassRector::class, [
// 一次性批量映射所有要改的类
'App\\Legacy\\Models\\User' => 'App\\Models\\User',
'App\\Legacy\\Models\\Order' => 'App\\Models\\Order',
'App\\Legacy\\Models\\Product' => 'App\\Models\\Product',
'App\\Legacy\\Services\\UserService' => 'App\\Services\\UserService',
'App\\Legacy\\Services\\PaymentService' => 'App\\Services\\PaymentService',
'App\\Legacy\\Utils\\Helper' => 'App\\Support\\Helper',
'App\\Legacy\\Utils\\Validator' => 'App\\Support\\Validator',
// old interfaces with I prefix
'App\\Contracts\\IUserRepository' => 'App\\Contracts\\UserRepository',
'App\\Contracts\\IOrderService' => 'App\\Contracts\\OrderService',
]);
use 语句、实例化、类型提示、注解中的类名引用——全部自动替换。而且基于 AST,不会误伤注释或字符串里的同名文字。
命名空间前缀批量替换
如果你的项目要把整个命名空间下的所有类都改名,用 RenameClassRector 批量映射就行——把旧命名空间下的每个类都列出来:
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\Name\RenameClassRector;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withRules([RenameClassRector::class])
->withConfiguredRule(RenameClassRector::class, [
// 整个 Acme\Old 命名空间下的类逐一映射
'Acme\\Old\\Models\\User' => 'App\\New\\Models\\User',
'Acme\\Old\\Models\\Order' => 'App\\New\\Models\\Order',
'Acme\\Old\\Services\\UserService' => 'App\\New\\Services\\UserService',
// ... 其他类
]);
如果涉及的类很多(几十上百个),可以先写个脚本从代码库里扫描出所有类名,生成完整的映射表再喂给 Rector。这比手工一个个改可靠得多。
场景四:技术债务批量清理
老项目里总有一些"历史遗留"的代码模式,单个看都不严重,但几百个文件加起来就是巨大的维护负担。Rector 可以一次性把这些模式全部统一。
实际案例:一个文件同时触发多条规则
来看一段集齐了各种老项目味道的代码:
// 清理前
class UserService
{
private $repository;
private $cache;
public function __construct($repo, $cache) // 无类型参数
{
$this->repository = $repo; // 手动赋值
$this->cache = $cache;
}
public function isActive($user) // 无返回类型
{
if ($user !== null) {
if ($user->status === 'active') { // 三层嵌套 if
if (strtotime($user->lastLoginAt) > time() - 86400 * 30) {
return true;
}
}
}
return false;
}
private function formatName($user) // 未使用的私有方法
{
return $user->firstName . ' ' . $user->lastName;
}
private function unusedHelper() // 死代码
{
return 'nobody calls me';
}
}
function check_string_exists($haystack, $needle) // 无类型 + 旧 API
{
if (strpos($haystack, $needle) !== false) { // strpos 应该用 str_contains
return true;
}
return false;
}
启用四套规则集,一次运行:
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withSets([
SetList::PHP_80, // 同时升级语法(str_contains 等)
SetList::CODE_QUALITY, // 通用质量改进
SetList::DEAD_CODE, // 死代码清除
SetList::EARLY_RETURN, // 嵌套 if → early return
SetList::TYPE_DECLARATION, // 类型声明补全
]);
结果——同一个文件,8 条规则同时生效:

// 清理后
class UserService
{
public function __construct( // [1] 构造器属性提升
private $repository,
private $cache
) {}
public function isActive($user): bool // [2] 返回类型声明
{ // [3] 嵌套 if → early return
if ($user === null) {
return false;
}
if ($user->status !== 'active') {
return false;
}
return strtotime((string) $user->lastLoginAt) > time() - 86400 * 30;
}
// [4] formatName() 和 unusedHelper() 被删除了 —— 未使用的私有方法
}
function check_string_exists($haystack, $needle): bool // [5][6] 参数+返回类型
{ // [7] strpos → str_contains
return str_contains((string) $haystack, (string) $needle);
}
更多常见的"技术债务"模式
除了上面这些,还有一些高频场景:
重复条件判断简化:
// 前:同一变量反复比较
if ($role === 'admin' || $role === 'superadmin' || $role === 'owner') { ... }
// 后:自动改为 in_array
if (in_array($role, ['admin', 'superadmin', 'owner'], true)) { ... }
空值检查简化:
// 前:多个空值判断堆在一起
if ($data === false || $data === null || $data === '') { ... }
// 后:自动合并为 in_array
if (in_array($data, [false, null, ''], true)) { ... }
这些看起来都是小改动,但在一个 500 文件的项目里,可能有上百处这样的模式。手动改不仅慢,而且容易遗漏。
场景五:自定义规则——把团队约定写成代码
内置规则覆盖不了你团队的特殊约定?那就自己写。Rector 的自定义规则 API 其实很直观:
最简单的自定义规则示例
假设你们团队有个约定:所有 Controller 方法必须以 action 为前缀(某种旧项目的遗留规范),现在要去掉这个前缀:
<?php
// rules/RemoveActionPrefixRector.php
namespace App\Rector;
use PhpParser\Node;
use Rector\Rector\AbstractRector;
use Rector\ValueObject\MethodName;
use Rector\Contract\Rector\RectorInterface;
final class RemoveActionPrefixRector extends AbstractRector implements RectorInterface
{
public function getRuleDefinition(): \Rector\Rule\RuleDefinition
{
return new \Rector\Rule\RuleDefinition(
'去掉 Controller 方法名的 action 前缀',
[
new \Rector\ValueObject\CodeSample(
<<<'CODE_SAMPLE'
class UserController extends BaseController
{
public function actionList() {}
public function actionShow() {}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class UserController extends BaseController
{
public function list() {}
public function show() {}
}
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Node\Stmt\ClassMethod::class];
}
public function refactor(Node $node): ?Node
{
/** @var Node\Stmt\ClassMethod $node */
if (!str_starts_with($node->name->toString(), 'action')) {
return null;
}
$newName = substr($node->name->toString(), 6); // 去掉 'action' 前缀
$node->name = new Node\Identifier($newName);
return $node;
}
}
注册到配置中:
use Rector\Config\RectorConfig;
use App\Rector\RemoveActionPrefixRector;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withRules([RemoveActionPrefixRector::class]);
核心就三个方法:
| 方法 | 作用 |
|---|---|
getRuleDefinition() |
定义规则的名称和 before/after 示例 |
getNodeTypes() |
声明你要处理哪种 AST 节点 |
refactor() |
对匹配到的节点做实际的修改,返回修改后的节点或 null(不修改) |
更复杂的规则可以利用 $this->nodeNameResolver 解析类型、用 $this->betterNodeFinder 查找父节点、用 $this->fileModifier 同时修改多个文件。Rector 内部的 142 条核心规则都是用同样的 API 写的——你看它们的源码就能学会几乎所有技巧。

渐进式改造策略
Rector 最强大的用法之一是配合 git 做渐进式改造。不要试图一次把所有规则全打开然后 process——那样 diff 太大,review 不过来。
推荐做法:分批推进
# 第一批:只跑 TYPE_DECLARATION(最安全的改动)
vendor/bin/rector process --config rector-type-only.php
git commit -m "refactor: 补全类型声明"
# 第二批:跑 CODE_QUALITY + EARLY_RETURN
vendor/bin/rector process --config rector-quality.php
git commit -m "refactor: 代码质量改进"
# 第三批:跑 DEAD_CODE(风险最高,单独一批)
vendor/bin/rector process --config rector-deadcode.php
git commit -m "refactor: 清除死代码"
# 第四批:框架迁移
vendor/bin/rector process --config rector-framework.php
git commit -m "refactor: Symfony 5.4 → 7.1 迁移"
每批都是独立的 commit,出了问题可以用 git revert 精确回滚某一批。而且每批的 diff 都足够小,code review 时能看清每一处改动是否合理。
安全网:always 运行测试套件
# 在每次 Rector 执行前后都跑测试
vendor/bin/phpunit
# 或者写成一步
vendor/bin/rector process && vendor/bin/phpunit
如果 Rector 改坏了什么,测试会立刻抓住。这也是为什么我强烈建议先在有良好测试覆盖率的项目上使用 Rector——没有测试的保护,大规模自动化改造就是在裸奔。
总结
Rector 的本质是一个 可编程的代码改造引擎。PHP 版本升级只是它最显眼的应用场景,但它能做的事情远不止于此:框架迁移让你不再害怕大版本升级;CI 集成让 code review 从机械劳动变成真正的技术讨论;AST 级别的重命名让大规模重构不再提心吊胆;自定义规则让团队约定变成了可执行的代码;分批渐进的策略让改造过程可控且可回滚。
如果你正在维护一个有一定规模的老项目,或者你的团队在 code review 里反复说同样的话,Rector 值得认真考虑一下。它不能替代架构设计,但它能把那些"我知道应该改但实在懒得动"的体力活变成一行命令。
参考资料
- https://getrector.com/ — 官网
- https://github.com/rectorphp/rector — 源码(22K+ Stars)
- https://getrector.com/find-rule — 浏览全部 840+ 条规则
- https://getrector.com/docs/custom-rules — 自定义规则文档
- https://github.com/driftingly/rector-laravel — Laravel 扩展包
- 入门介绍:[rectorphp/rector] 自动升级和重构你的 PHP 代码(i1 开发类库)
原文标题: Rector 不止会升级 PHP:框架迁移、规范强制和大规模代码改造
原文地址: https://phpreturn.com/index/a6a1686e5c87cd.html
原文平台: PHP武器库
版权声明: 本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。