PHP 怎么还技术债

wen PHP项目 2

本文目录导读:

PHP 怎么还技术债

  1. 第一步:建立“安全网”(最高优先级)
  2. 第二步:处理“最大债务”——遗留代码(Legacy Code)
  3. 第三步:升级“地基”——PHP 版本与依赖(Medium)
  4. 第四步:清理“数据层”债务(High)
  5. 第五步:重构“业务逻辑”(Long-term)
  6. 第六步:基础设施与监控(Quick wins)
  7. 避坑指南(特别提醒)
  8. 实战案例(小 Demo)

在 PHP 项目中,技术债(Technical Debt)是普遍存在的,还债的核心思路是“步步为营,先把风险最高的地方补上,而不是一次性推倒重来”

以下是针对 PHP 项目的系统性还债策略,按优先级从高到低排列:

第一步:建立“安全网”(最高优先级)

在改任何代码之前,如果没有测试保护,重构就是在走钢丝。

  1. 引入 PHPUnit 或 Pest
    • 为核心业务逻辑(如订单计算、支付回调、权限判断)编写单元测试。
    • 为关键用户路径(如登录、下单)编写 Feature 测试。
  2. 开启静态分析
    • PHPStanPsalm:运行 vendor/bin/phpstan analyse -l max,先把报错数量清零,再逐步提升等级(Level)。
    • PHP_CodeSniffer:统一代码风格(PSR-12)。
  3. 接入 CI/CD:在 GitHub Actions 或 GitLab CI 中,强制运行测试和静态分析,让“红牌”阻止合并,防止新债产生。

第二步:处理“最大债务”——遗留代码(Legacy Code)

PHP 老项目常见的问题是没有命名空间、函数全局混杂、直接操作 MySQL。

策略:渐进式重构,而非重写

  1. 引入自动加载(Composer)
    • 如果没有用 Composer,先引入,将 require / include 逐步替换为 PSR-4 自动加载。
  2. “绞杀者模式”(Strangler Pattern)
    • 不要试图一次性重写整个项目,新建一个 App 命名空间(PSR-4)。
    • 写新功能时,在新命名空间下用现代 PHP(类型声明、依赖注入)编写。
    • 老代码先留着,通过“适配器”调用,随着时间推移,新代码逐渐替代旧代码。
  3. 解耦 God Object(上帝类)
    • 如果某个类有 5000 行代码,先使用 IDE(如 PhpStorm)的“提取接口”和“提取方法”功能,把大方法拆成小方法,把不相关的职责拆成独立的类(如把 User 类中的邮件发送逻辑拆到 Mailer)。

第三步:升级“地基”——PHP 版本与依赖(Medium)

升级 PHP 版本是性价比极高的还债方式,性能和安全都会大幅提升。

  1. 小步升级:如果你还在 PHP 7.4,先升到 8.1,不要直接跳 8.4(因为很多扩展可能不兼容)。
  2. 使用 Rector 进行自动化升级
    • vendor/bin/rector process src --set php81(Rector 能自动修改废弃函数、语法,能帮你把 mysql_query 改成 mysqli 或 PDO)。
  3. 依赖管理
    • 运行 composer outdated,对于不再维护的包,寻找替代品或重写。
    • 锁死版本:确保 composer.lock 在版本控制中,保证环境一致。

第四步:清理“数据层”债务(High)

老 PHP 最痛的是 SQL 注入和混乱的数据库操作。

  1. 全面使用 PDO 或 Doctrine DBAL
    • 全局搜索 mysql_mysqli_query强制替换为 PDO 预处理语句,这是安全红线,优先级最高。
  2. 引入 Migration(迁移)
    • 使用 Phinx 或 Doctrine Migrations,将 DDL(建表语句)写成 SQL 文件入库。
    • 废除 dump.sql 和手动修改数据库的习惯,确保表结构变更可回滚、可追溯。

第五步:重构“业务逻辑”(Long-term)

  1. 消灭全局变量
    • global $db$config 替换为依赖注入(DI)容器(如 PHP-DI 或 Laravel 的容器)。
  2. 类型安全
    • 给函数参数和返回值加上 intstringarray 类型声明。
    • 如果历史原因不能改,可以使用 assert()@param 注释过渡,但最终目标是强类型。
  3. 处理“魔法数字”
    • if ($status == 1) 改成 if ($order->isPaid()) 或使用常量 OrderStatus::PAID

第六步:基础设施与监控(Quick wins)

  1. 启用 OPcache:让运行速度直接提升 30%。
  2. 日志:引入 Monolog,废除 error_log 写 txt 的习惯,接入 ELK 或 Sentry。
  3. 错误处理:废除 抑制符,使用 try-catch 捕获异常。

避坑指南(特别提醒)

  • 不要重写! 如果业务代码超过 10 万行,重写大概率会导致项目失败。
  • 保持“童子军原则”:每次接触某段代码时,顺手把那一小块区域整理干净(重命名一个变量、抽取一个小函数)。
  • 时间盒:每周设定固定 3 小时专门做重构,不计入常规需求排期。

实战案例(小 Demo)

假设你有一段老代码:

// 旧代码:没有类型,直连数据库,逻辑混乱
function get_user($id) {
    $result = mysql_query("SELECT * FROM users WHERE id=" . $id);
    $row = mysql_fetch_array($result);
    if ($row['active'] == 1) {
        return $row['email'];
    } else {
        return null;
    }
}

还债第一步(安全)

// 改成 PDO 预处理
function get_user(int $id): ?string {
    $stmt = $pdo->prepare("SELECT email, active FROM users WHERE id = :id");
    $stmt->execute(['id' => $id]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    return ($row && $row['active'] === 1) ? $row['email'] : null;
}

还债第二步(结构)

// 放到 UserRepository 类中,注入 PDO
final class UserRepository {
    public function __construct(private PDO $pdo) {}
    public function findActiveEmailById(int $id): ?string {
        $stmt = $this->pdo->prepare("SELECT email FROM users WHERE id = :id AND active = 1");
        $stmt->execute(['id' => $id]);
        $email = $stmt->fetchColumn();
        return $email ?: null;
    }
}

PHP 还债的核心不是“写新代码”,而是“建立安全网 -> 隔离旧代码 -> 局部更新”,只要静态分析通过率在提升,测试覆盖率在增加,你就是在正确的还债路上。

抱歉,评论功能暂时关闭!