PHP `Rector` (PHP 代码重构工具) 内部机制与自动化升级

各位观众老爷们,晚上好!今天咱们来聊聊 PHP 代码自动化升级的利器——Rector。这玩意儿可不是什么魔法棒,但用好了,也能让你的老代码焕发新生,简直是程序员居家旅行必备之良药。

开场白:代码升级的那些糟心事儿

相信大家都有过这种经历:项目要升级 PHP 版本了,结果发现代码里一堆 deprecated 的函数、过时的语法,手动改?那得改到猴年马月!而且稍不留神,还容易引入新的 Bug。想想就头皮发麻。

Rector 这时候就派上用场了。它可以自动帮你把老代码升级到新的 PHP 版本,还能修复一些常见的代码风格问题,简直就是代码界的“一键美颜”。

Rector 的核心机制:AST(抽象语法树)

要理解 Rector 的工作原理,就得先了解 AST。AST 可以理解为代码的一种抽象表示形式,它把代码的语法结构用树状结构组织起来。

举个例子,对于这段简单的 PHP 代码:

$a = 1 + 2;

它的 AST 可能会是这样的(简化版):

Assign
  Variable (a)
  BinaryOp (+)
    Scalar (1)
    Scalar (2)

Rector 的核心工作流程可以概括为以下几步:

  1. 解析代码: 把 PHP 代码解析成 AST。
  2. 遍历 AST: 遍历 AST 的每一个节点,找到需要修改的地方。
  3. 修改 AST: 根据预定义的规则,修改 AST 的节点。
  4. 生成代码: 把修改后的 AST 转换回 PHP 代码。

简单来说,Rector 就像一个外科医生,它先用 X 光(解析成 AST)透视你的代码,然后根据病情(预定义的规则)对症下药(修改 AST),最后把手术后的代码还给你。

Rector 的配置:规则,规则,还是规则!

Rector 的强大之处在于它提供了大量的预定义规则,可以处理各种各样的代码升级和重构任务。这些规则定义了什么样的代码需要修改,以及如何修改。

Rector 的配置文件通常是 rector.php,放在项目的根目录下。你可以通过这个文件来启用或禁用某些规则,也可以自定义规则。

一个简单的 rector.php 看起来像这样:

<?php

declare(strict_types=1);

use RectorConfigRectorConfig;
use RectorSetValueObjectSetList;

return static function (RectorConfig $rectorConfig): void {
    // 设置要扫描的目录
    $rectorConfig->paths([
        __DIR__ . '/src',
        __DIR__ . '/tests',
    ]);

    // 加载规则集
    $rectorConfig->sets([
        SetList::PHP_82, // 升级到 PHP 8.2
        SetList::CODE_QUALITY, // 提高代码质量
    ]);

    // 忽略某些文件或目录
    $rectorConfig->skip([
        __DIR__ . '/vendor',
    ]);
};

这个配置文件指定了:

  • 要扫描的目录:srctests
  • 要应用的规则集:PHP_82CODE_QUALITY
  • 要忽略的目录:vendor

Rector 的规则集:拿来主义的精髓

Rector 提供了大量的规则集,可以一次性启用多个相关的规则。常用的规则集包括:

规则集 描述
SetList::PHP_70 升级到 PHP 7.0
SetList::PHP_71 升级到 PHP 7.1
SetList::PHP_72 升级到 PHP 7.2
SetList::PHP_73 升级到 PHP 7.3
SetList::PHP_74 升级到 PHP 7.4
SetList::PHP_80 升级到 PHP 8.0
SetList::PHP_81 升级到 PHP 8.1
SetList::PHP_82 升级到 PHP 8.2
SetList::PHP_83 升级到 PHP 8.3
SetList::CODE_QUALITY 提高代码质量,例如修复代码风格问题、移除无用的代码等。
SetList::DEAD_CODE 移除无用的代码,例如未使用的变量、无用的方法等。
SetList::TYPE_DECLARATION 自动添加类型声明,例如参数类型、返回值类型等。
SetList::EARLY_RETURN 将复杂的条件判断转换为提前返回,提高代码可读性。
SetList::STRICT_BOOLEAN 强制使用严格的布尔类型判断,避免潜在的错误。
SetList::NAMING 修正命名规范,例如类名,方法名,变量名等等。

选择合适的规则集,可以大大简化你的配置工作。

Rector 的命令行:一键启动,自动升级

配置好 rector.php 之后,就可以使用命令行工具来运行 Rector 了。

常用的命令包括:

  • vendor/bin/rector process:运行 Rector,自动修改代码。
  • vendor/bin/rector process --dry-run:模拟运行 Rector,不修改代码,只显示修改的建议。这个命令非常有用,可以在实际修改代码之前,先预览一下 Rector 的修改结果。
  • vendor/bin/rector process --config rector.php:指定配置文件。
  • vendor/bin/rector generate:生成规则,可以根据现有代码生成自定义规则。

自定义规则:满足你的特殊需求

Rector 提供的预定义规则已经足够强大了,但有时候你可能需要自定义规则,来满足一些特殊的代码升级或重构需求。

自定义规则需要实现 RectorDefinition 接口,并重写 getNodeTypes()refactor() 方法。

  • getNodeTypes() 方法用于指定该规则要处理的 AST 节点类型。
  • refactor() 方法用于实现具体的代码修改逻辑。

举个例子,假设我们需要把所有 strlen() 函数替换成 mb_strlen() 函数,可以创建一个自定义规则:

<?php

declare(strict_types=1);

namespace AppRector;

use PhpParserNode;
use PhpParserNodeExprFuncCall;
use RectorCoreContractRectorRectorInterface;
use RectorCoreRectorAbstractRector;
use SymplifyRuleDocGeneratorValueObjectCodeSampleCodeSample;
use SymplifyRuleDocGeneratorValueObjectRuleDefinition;

final class StrlenToMbStrlenRector extends AbstractRector implements RectorInterface
{
    public function getRuleDefinition(): RuleDefinition
    {
        return new RuleDefinition('Change strlen() to mb_strlen()', [
            new CodeSample(
                '$length = strlen($string);',
                '$length = mb_strlen($string);'
            ),
        ]);
    }

    /**
     * @return array<class-string<Node>>
     */
    public function getNodeTypes(): array
    {
        return [FuncCall::class];
    }

    /**
     * @param FuncCall $node
     */
    public function refactor(Node $node): ?Node
    {
        if (!$this->isName($node, 'strlen')) {
            return null;
        }

        $node->name = new PhpParserNodeName('mb_strlen');

        return $node;
    }
}

这个规则做了以下几件事:

  1. 指定要处理的节点类型为 FuncCall (函数调用)。
  2. 判断函数名是否为 strlen
  3. 如果是 strlen,则把函数名替换成 mb_strlen

要在 rector.php 中启用这个规则,需要把它添加到 rules 数组中:

<?php

declare(strict_types=1);

use AppRectorStrlenToMbStrlenRector; // 注意引入自定义规则的命名空间
use RectorConfigRectorConfig;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig->paths([
        __DIR__ . '/src',
    ]);

    $rectorConfig->rules([
        StrlenToMbStrlenRector::class,
    ]);
};

Rector 的高级用法:与 CI/CD 集成

为了保证代码质量,可以把 Rector 集成到 CI/CD 流程中。例如,可以在每次提交代码时,自动运行 Rector,检查代码是否符合规范,并自动修复一些常见的代码问题。

以 GitHub Actions 为例,可以在项目的根目录下创建一个 .github/workflows/rector.yml 文件:

name: Rector

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  rector:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, gd, intl
          tools: rector

      - name: Install Dependencies
        run: composer install --no-interaction --no-progress --prefer-dist

      - name: Run Rector
        run: vendor/bin/rector process --dry-run

      - name: Apply Rector changes (if any)
        if: ${{ github.event_name == 'pull_request' }}
        run: |
          vendor/bin/rector process
          git add .
          git commit -m "Apply Rector changes"
          git push

这个配置文件做了以下几件事:

  1. 在每次 push 或 pull request 时,触发 Rector 任务。
  2. 使用 Ubuntu 作为运行环境。
  3. 安装 PHP 8.2,并安装所需的扩展。
  4. 安装项目的依赖。
  5. 运行 Rector,先进行 dry-run,检查代码是否符合规范。
  6. 如果是在 pull request 中,则自动应用 Rector 的修改,并提交到代码仓库。

Rector 的注意事项:避免过度自动化

Rector 虽然强大,但也不是万能的。在使用 Rector 的时候,需要注意以下几点:

  • 充分测试: 在应用 Rector 的修改之后,一定要进行充分的测试,确保代码的正确性。
  • 谨慎使用规则集: 某些规则集可能会引入破坏性的修改,需要谨慎使用。
  • 避免过度自动化: 不要试图用 Rector 解决所有问题。有些问题可能需要手动修改才能解决。
  • 持续集成: 将 Rector 集成到持续集成流程中,可以及时发现和修复问题。

Rector 的优缺点:一览表

优点 缺点
自动化代码升级:可以自动把老代码升级到新的 PHP 版本,节省大量的人力。 可能会引入破坏性的修改:某些规则可能会导致代码无法正常运行,需要充分测试。
提高代码质量:可以修复一些常见的代码风格问题,提高代码的可读性和可维护性。 学习成本:需要学习 Rector 的配置和使用方法,以及 AST 的相关知识。
减少技术债务:可以及时修复 deprecated 的函数和过时的语法,减少技术债务。 无法解决所有问题:有些代码问题需要手动修改才能解决。
可扩展性强:可以自定义规则,满足特殊的代码升级和重构需求。 依赖于 AST 的准确性:如果 AST 解析错误,可能会导致 Rector 的修改也出错。
与 CI/CD 集成:可以把 Rector 集成到 CI/CD 流程中,保证代码质量。 需要一定的配置:需要配置 rector.php 文件,选择合适的规则集。
大幅提升开发效率:减少人工代码审查时间,避免低级错误。 复杂的代码逻辑可能难以处理:对于过于复杂的代码逻辑,Rector 可能无法正确处理,需要人工介入。

总结:Rector,你的代码升级好帮手

总的来说,Rector 是一个非常强大的 PHP 代码自动化升级工具。它可以帮助你快速、高效地把老代码升级到新的 PHP 版本,并提高代码质量。当然,在使用 Rector 的时候,也需要注意一些事项,避免过度自动化,并进行充分的测试。

希望今天的分享对大家有所帮助。如果大家有什么问题,欢迎随时提问。

结束语:码字不易,多多支持!

好了,今天的讲座就到这里了。如果觉得有用,记得点个赞,分享给你的朋友们。祝大家编码愉快,bug 远离!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注