引言:为什么遗留代码重构不是奢侈品,而是必需品
每位PHP开发者迟早都会遇到遗留代码。这可能是一个10年前用PHP 5.3编写的项目,或者是一个“勉强能运行”但每次修改都令人痛苦的应用程序。重构这类代码不仅仅是“梳理”风格,而是一个至关重要的过程,它能提高应用程序的可维护性、性能和安全性。
在本文中,我们将使用现代语言特性(PHP 7.4–8.3)和经过验证的模式,探讨重构遗留PHP代码的实用方法。您将学习如何在不破坏现有系统的情况下安全地进行更改。
1. 评估并记录当前状态
第一步也是最重要的一步——了解您正在处理的内容。不要盲目尝试重构。
1.1. 创建依赖关系图
使用诸如PhpMetrics或Deptrac之类的工具来可视化类之间的关联。这有助于识别“上帝对象”(god objects)和循环依赖。
// 遗留代码中典型“上帝”类的示例class UserManager { public function save($data) { // 200行代码:验证、发送邮件、日志记录、数据库操作 } public function sendNewsletter() { // 还有150行 } public function generateReport() { // 还有100行 }}1.2. 编写特征测试
在更改代码之前,请确保当前行为已通过测试记录。使用“Golden Master”(快照测试)方法:
// 记录当前行为的测试public function testLegacyCalculation(): void{ $calculator = new OldCalculator(); $result = $calculator->compute(10, 20); // 如果没有规范,只需保存结果 $this->assertSame(30, $result);}建议: 如果项目完全没有测试,请从关键业务操作的集成测试开始。使用PHPUnit配合vfsStream来隔离文件系统。
2. 逐步改进:从全局函数到面向对象编程
大部分遗留PHP代码由全局函数和过程式风格组成。逐步迁移到面向对象编程是重构的关键阶段。
2.1. 封装全局函数
将全局函数替换为类的静态方法,然后替换为完整的服务。
// 之前:全局函数function send_email($to, $subject, $message) { mail($to, $subject, $message);}
// 之后:服务类class MailService { public function __construct( private MailerInterface $mailer ) {} public function send(string $to, string $subject, string $message): bool { return $this->mailer->send($to, $subject, $message); }}2.2. 摆脱全局变量
全局变量(直接使用$GLOBALS、$_SESSION)是错误的主要来源。使用Registry模式或依赖注入(DI)。
// 糟糕:直接操作全局变量function getCurrentUser() { global $db; $id = $_SESSION['user_id']; return $db->query("SELECT * FROM users WHERE id = $id");}
// 良好:显式依赖注入class UserProvider { public function __construct( private PDO $db, private SessionInterface $session ) {} public function getCurrentUser(): ?User { $id = $this->session->get('user_id'); return $this->db->query('SELECT * FROM users WHERE id = ?', [$id]); }}3. 安全替换过时结构
PHP在不断发展,许多旧结构已变得不安全且