[rectorphp/rector]自动升级和重构你的PHP代码

2026-04-17 奥古斯宏 #Rector #PHP #代码重构 #自动化
PHP 8.4 都出了,项目还卡在 7.4?Rector 基于 AST 自动把旧代码升级到新语法,不是查找替换,是真正理解代码结构的自动改造

你的项目还跑在 PHP 7.4 上,想升到 8.x 但面对几百个文件的语法差异无从下手?团队里新人写的代码风格参差不齐,code review 累得半死?Rector 就是用来解决这些问题的。它能自动把旧版 PHP 代码升级到新版本,也能按规则批量重构代码质量,而且整个过程可以先用 dry-run 预览,确认无误再真正改动。

Rector 官网首页

安装

composer require rector/rector --dev

安装完成后在项目根目录创建 rector.php,所有规则都在这里配置。没有 init 命令,手动建就行:

<?php
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
    ->withSets([
        SetList::PHP_80,
    ]);

先预览不改文件:

vendor/bin/rector src --dry-run

确认没问题后去掉 --dry-run 正式执行:

vendor/bin/rector src

实际跑起来是这个效果,diff 清楚地告诉你每处会怎么改:

Rector dry-run 运行效果

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

一条命令升级 PHP 版本

这是 Rector 最核心的能力。内置了从 PHP 5.3 到 8.6 全系列版本的升级规则集,一条命令完成过去需要数周的手工迁移。

先看效果。这是一段典型的 PHP 7.x 风格代码:

// 升级前
class UserService
{
    private $repository;
    private $cache;

    public function __construct($repo, $cache)
    {
        $this->repository = $repo;
        $this->cache = $cache;
    }

    public function findUser($id)
    {
        $key = 'user_' . $id;
        $data = $this->cache->get($key);
        if ($data === false) {
            $data = $this->repository->find($id);
            $this->cache->set($key, $data, 3600);
        }
        return $data;
    }
}

SetList::PHP_80 跑一次之后:

// 升级后
class UserService
{
    public function __construct(
        private $repository,
        private $cache  // 构造器属性提升(PHP 8.0 特性)
    ) {
    }

    public function findUser(string $id)  // 参数加了类型声明
    {
        $key = 'user_' . $id;
        $data = $this->cache->get($key);
        if ($data === false) {
            $data = $this->repository->find($id);
            $this->cache->set($key, $data, 3600);
        }
        return $data;
    }
}

构造函数里的赋值模式被自动识别并改成了 构造器属性提升(constructor promotion),这是 PHP 8.0 的语法糖——属性声明、参数声明、赋值三步合成一步。

跨版本升级也只需要链式添加规则集:

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withSets([
        SetList::PHP_80,   // 命名参数、match 表达式、str_contains 等
        SetList::PHP_81,   // 枚举、readonly 属性、never 返回类型
        SetList::PHP_82,   // true 类型、动态属性弃用、随机数扩展
        SetList::PHP_83,   // 类常量类型化、json_validate() 等
        SetList::PHP_84,   // 属性钩子、不带括号的新实例化等
    ]);

每个规则集对应一个 PHP 版本的语法变更和废弃特性替换。框架也能一起升——Symfony、Laravel、PHPUnit、Doctrine 都有对应的社区规则包,官方维护的是 Symfony、PHPUnit 和 Doctrine 三个。

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

批量重构代码质量

不升级 PHP 版本也没关系,Rector 同样能帮你清理代码。而且多条规则可以同时生效,一次运行解决一堆问题。

来看一个真实案例。下面这段代码集齐了常见的老项目味道:无类型声明、嵌套过深的 if-else、未使用的私有方法、strpos 旧写法:

// 重构前
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 (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)
{
    if (strpos($haystack, $needle) !== false) {
        return true;
    }
    return false;
}

一次性启用代码质量相关的四套规则:

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withSets([
        SetList::CODE_QUALITY,   // 通用质量改进
        SetList::DEAD_CODE,      // 死代码清除
        SetList::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);  // [8] 三元简化
}

一个文件,一次运行,8 条规则同时生效。构造器属性提升、类型声明、early return、死代码清除、str_contains 替换、布尔返回值简化——全部自动完成。

拆开看每条规则单独做了什么:

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

死代码清除

未使用的私有方法是老项目的顽疾。没人敢删,因为不确定哪里在用反射调用。但如果确定没用,DEAD_CODE 规则集会直接帮你清掉:

// 前
class ReportGenerator
{
    public function build()
    {
        $data = $this->fetchData();
        return $this->format($data);
    }

    private function fetchData() { return []; }
    private function format($data) { return json_encode($data); }

    private function deprecatedMethod()
    {
        // 旧代码遗留
        return null;
    }
}

// 后 —— deprecatedMethod() 被删除,因为它从未被调用
class ReportGenerator
{
    public function build()
    {
        $data = $this->fetchData();
        return $this->format($data);
    }

    private function fetchData() { return []; }
    private function format($data) { return json_encode($data); }
}

建议配合 git diff 逐处审查删除内容,毕竟反射或动态调用的场景 Rector 无法感知。

Early Return

嵌套三层以上的 if-else 是可读性灾难:

// 前
public function process(User $user): ?string
{
    if ($user->isActive()) {
        if ($user->hasPermission('write')) {
            return $user->getName();
        }
    }
    return null;
}

// 后
public function process(User $user): ?string
{
    if (!$user->isActive()) {
        return null;     // 先排除不符合的
    }
    if (!$user->hasPermission('write')) {
        return null;     // 再排除下一个
    }
    return $user->getName();  // 剩下的就是正常路径
}

注意:如果同时启用了 CODE_QUALITY,里面的 CombineIfRector 可能会把同样的嵌套 if 合并成 && 条件而不是 early return。两个规则存在竞争关系,根据团队偏好选其中一个就行。

类名/命名空间全项目重命名

要做类名迁移的时候,这条规则能省下几天的工作量:

// 前 —— 引用了旧的命名空间
use App\Legacy\Utils\Helper;
use App\Legacy\Models\Customer;

class OrderController
{
    public function show($id)
    {
        $helper = new Helper();       // 旧类名
        $customer = new Customer($id); // 旧类名
        return $helper->format($customer->getName());
    }
}

// 配置
->withConfiguredRule(RenameClassRector::class, [
    'App\Legacy\Utils\Helper' => 'App\Support\Helper',
    'App\Legacy\Models\Customer' => 'App\Models\User',
]);

// 后 —— use 语句和实例化全部替换
class OrderController
{
    public function show($id)
    {
        $helper = new \App\Support\Helper();  // 新命名空间
        $customer = new \App\Models\User($id); // 新类名
        return $helper->format($customer->getName());
    }
}

基于 AST 的替换,不是文本查找——不会误伤字符串或注释里出现的同名内容。

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

精确控制:跳过不想改的

不是所有自动修改都是你想要的。跳过特定文件、目录或者某条规则:

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withSets([SetList::CODE_QUALITY])
    ->withSkip([
        __DIR__ . '/src/Legacy/',           // 跳过整个目录
        __DIR__ . '/src/Migrations/*',      // 跳过 migration 文件
        ForeachToInArrayRector::class,       // 跳过某条具体规则
    ]);

第三方库生成的文件、数据库 migration、历史兼容层——加到 skip 列表就好。

接进 CI 流水线

最合理的用法不是本地偶尔跑一次,而是放进 CI,每次提交都检查:

# .github/workflows/rector.yml
name: Rector CI
on: [push, 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

--dry-run 模式下有 diff 输出时返回非零退出码,CI 就会报错。也可以用快捷命令一键生成 CI 配置:

vendor/bin/rector setup-ci

Rector 基于 AST 工作,输出代码的格式可能不太好看(缩进、空行可能乱掉)。在它后面接一个格式化工具就行——ECS、PHP-CS-Fixer 或 Prettier for PHP 都可以。另外 PHP + HTML 混合的模板文件处理完后建议人工检查一遍。

Rector 核心包内置 142 条规则,加上社区扩展包总共有 840+ 条,覆盖 PHP 5.3 到 8.6 全版本升级和主流框架迁移。对于还在维护老项目或者追求代码质量的团队来说,这个工具值得放进工具链里。

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

参考资料

Rector 规则浏览器

最近浏览
累计浏览次数:1
评论
点击登录
phpreturn,PHP武器库,专注PHP领域的项目和资讯,收录和介绍PHP相关项目。

本站所有权利归 phpreturn.com 所有

举报/反馈/投稿邮箱:phpreturn@ulthon.com

鲁ICP备19027671号-2