遗留 PHP 代码重构:高级技术与策略

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

遗留 PHP 代码重构:高级水平



处理 PHP 中的遗留代码是许多开发者不可避免的现实。这类代码通常是一个没有测试、存在全局依赖、业务逻辑与视图混杂的单一应用。简单的“凭感觉”重构可能导致致命错误。在本文中,我们将探讨高级重构技术,这些技术能够安全且高效地现代化遗留系统。



1. “Seam”策略:创建断点



重构遗留代码的第一步是创建接缝 (seams)。接缝是一个可以在不直接编辑代码的情况下改变其行为的地方。当代码没有测试覆盖且无法隔离运行时,这一点尤为重要。



考虑一个经典示例:在方法内部直接操作数据库。



<?php// 遗留代码class UserReport {    public function getActiveUsers() {        $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');        $stmt = $db->query("SELECT * FROM users WHERE active = 1");        return $stmt->fetchAll();    }}?>


问题:new PDO(...) 创建了一个硬依赖。要测试此方法,需要有一个真实的数据库。



解决方案:通过构造函数注入依赖。



<?php// 重构:通过 DI 创建接缝class UserReport {    private PDO $pdo;

public function __construct(PDO $pdo) { $this->pdo = $pdo; }

public function getActiveUsers(): array { $stmt = $this->pdo->query("SELECT * FROM users WHERE active = 1"); return $stmt->fetchAll(); }}

// 使用$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');$report = new UserReport($pdo);?>


现在你可以在测试中将 PDO 替换为模拟对象。这是一个简单但有效的接缝。



2. “Sprout Method”和“Sprout Class”模式



当你无法修改现有方法时(例如,由于缺少测试),请使用“Sprout”策略——在旧代码旁边创建新代码,逐步切换调用。



示例:我们有一个巨大的方法,用于验证和保存用户。



<?phpclass UserService {    public function saveUser(array $data): bool {        // 100 行验证、日志记录和保存代码        $email = $data['email'] ?? '';        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {            throw new InvalidArgumentException('Invalid email');        }        // ... 许多其他代码        $db = new PDO(...);        $stmt = $db->prepare("INSERT INTO users ...");        // ...    }}?>


与其立即重构整个方法,不如创建一个新的验证类。



<?phpclass UserValidator {    public function validate(array $data): void {        if (!filter_var($data['email'] ?? '', FILTER_VALIDATE_EMAIL)) {            throw new InvalidArgumentException('Invalid email');        }        // 添加其他检查    }}

class UserService { private UserValidator $validator;

public function __construct(UserValidator $validator) { $this->validator = $validator; }

public function saveUser(array $data): bool { $this->validator->validate($data); // 其余代码暂时保留 }}?>


现在你可以为 UserValidator 编写单元测试,而无需改动旧代码。你将逐步将逻辑从 saveUser 中提取到新类中。



3. 消除全局状态和静态方法



遗留代码经常使用全局变量、单例和静态方法。这使得代码几乎无法...

博客

书籍推荐