PHP遗留代码重构:从A到Z
处理遗留代码是每个PHP开发者生活中不可避免的一部分。迟早你会遇到一个用PHP 5.6(甚至4)编写的项目,没有自动加载,使用全局变量,业务逻辑与HTML混杂在一起。重构这样的代码不仅仅是“梳理”,而是一个战略性的过程,可以拯救项目免于技术债务,使其变得可维护。
在本文中,我们将探讨如何系统地进行PHP遗留代码重构,不破坏现有功能,并将“泥潭”转变为现代、可测试的代码。
1. 诊断与评估:从何开始?
在修改代码之前,需要了解你面对的是什么。从项目审计开始。
步骤1. 确定“痛点”
- 依赖项:是否使用了过时的库?例如,使用
mysql_*函数而不是PDO或MySQLi。 - 架构:是否有任何结构(MVC)?还是所有代码都在一个
index.php文件中? - 测试:是否存在自动化测试?如果没有,这是你的首要任务。
步骤2. 分析工具
使用静态分析器快速识别问题区域:
- PHPStan / Psalm:发现类型错误和签名不匹配。
- Rector:自动将语法更新到新的PHP版本。
- PhpMetrics:显示代码的圈复杂度和耦合度。
运行Rector从PHP 7.4更新到8.0的示例:
vendor/bin/rector process src/ --set php802. “无痛手术”策略:逐步计划
重构遗留代码不是“大爆炸”。它是一系列小而安全的步骤。使用“绞杀藤”技术——逐步用新模块替换旧模块,而不是一次性重写所有内容。
步骤1. 用测试覆盖代码
没有测试,重构就像玩俄罗斯轮盘赌。从特征测试开始:
class LegacyCalculatorTest extends TestCase { public function testOldCalculation(): void { // 运行旧代码并记录结果 $result = legacy_calculation(10, 5); $this->assertEquals(15, $result); }}这将在修改前创建一张“安全网”。
步骤2. 隔离“脏”逻辑
如果代码使用全局变量($GLOBALS),将它们提取为函数参数:
// 之前(全局依赖)function processOrder() { global $db; $result = $db->query("SELECT * FROM orders");}
// 之后(依赖注入)function processOrder(PDO $db) { $result = $db->query("SELECT * FROM orders");}步骤3. 替换过时函数
例如,将mysql_*替换为PDO:
// 旧代码$link = mysql_connect('localhost', 'user', 'pass');mysql_select_db('test', $link);$result = mysql_query("SELECT * FROM users", $link);
// 新代码(PDO)$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');$stmt = $pdo->query("SELECT * FROM users");$result = $stmt->fetchAll(PDO::FETCH_ASSOC);3. 遗留PHP的典型问题及其解决方案
问题1:“意大利面条式代码”(逻辑与表示层混合)
解决方案:将代码分层。将业务逻辑提取到单独的类中,将HTML放入模板(例如,使用Twig)。
// 不好:逻辑和HTML在同一个文件中if ($user->isAdmin()) { echo "<h1>你好,管理员!</h1>";} else { echo "<h1>你好,用户!</h1>";}
// 好:控制器 + 模板//