PHP代码重构核心是保障行为不变前提下提升可读性与安全性:统一用PDO异常模式+预处理防SQL注入,SQL逻辑抽离至DAO/Repository,依赖注入传PDO,函数按职责而非行数拆分,动态SQL须白名单校验。

PHP代码重构不是为了“看起来更现代”,而是让每次 git diff 都更安全、每次 phpunit 运行都更有底气。核心就一条:改完之后,行为不变,但下一个人接手时,能更快看懂“谁在用它?它依赖什么?它保证了什么?”
用PDO + 异常模式替代mysql_*和裸query
如果你还在用 mysql_connect() 或 mysql_query(),PHP 7.0+ 会直接报致命错误——它们已被彻底移除。即使用了 mysqli,若没启用异常模式、没做预处理,也只是把炸弹延迟引爆。
- 必须设置
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION),否则PDOException不抛出,错误静默失败 - 所有用户输入进SQL,必须走
prepare()+execute(),哪怕只是查单条:$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$id]); -
IN子句不能直接绑定数组,得动态拼占位符:str_repeat('?,', count($ids) - 1) . '?',再用array_values($ids)绑定 - 连接参数(
host、dbname、user、pass)从环境变量或配置文件读,绝不在代码里硬写
把SQL逻辑从控制器/模型里抽出来,放进DAO或Repository
在控制器里写 $pdo->query("UPDATE orders SET status = 'shipped' WHERE id = " . $_POST['id']),等于把数据库细节、安全责任、业务语义全搅在一起。下次加个状态校验或日志记录,就得动三处代码。
- 为每个实体建一个类,比如
UserRepository,只管数据存取,不处理权限、通知、缓存刷新 - 方法名要带业务语义:
findActiveUsersByRole(string $role): array,而不是query($sql, $params) - SQL字符串留在类内部,上层只调用方法;复杂查询可拆成私有方法,如
buildSearchClause()、applyPagination() - 返回值类型明确:用
PDO::FETCH_ASSOC或PDO::FETCH_CLASS,避免到处写fetch()后手动转数组
用依赖注入传PDO,别new、别global、别$_GLOBALS
在某个Service里直接 new PDO(...),会导致测试时无法Mock连接、无法切换测试库、无法验证SQL是否真被调用——你写的不是业务逻辑,是数据库耦合器。
立即学习“PHP免费学习笔记(深入)”;
- 构造函数注入最清晰:
public function __construct(private PDO $pdo) {}(PHP 8.0+ 属性提升语法可省略private字段声明) - 连接初始化交给启动层(如
index.php或 DI 容器),不要让业务类自己决定连哪台DB - 需要主从分离?注入两个命名实例(
$primaryPdo/$replicaPdo),或封装ConnectionManager类统一路由 - 全局变量(
$_SESSION、$_COOKIE)也一样:提取为参数传入,或通过接口抽象(如SessionInterface)
拆长函数时,先找“动词”再切“职责”,别只按行数分
一个200行的 processOrder() 函数,如果里面混着校验、库存扣减、支付调用、发短信、更新状态,按50行一段硬拆,只会得到四个更难理解的碎片。
- 先识别动词块:“校验地址合法性” → 提取为
validateShippingAddress(Address $address): void - “调第三方支付” → 封装为
PaymentGateway::charge(...),与订单逻辑解耦 - “发短信通知” → 独立为
SmsNotifier::sendOrderShipped(...),后续可轻松换渠道 - 每小段函数只做一件事、无副作用、输入输出明确;拒绝“我帮你把这段复制过去改个名”的伪重构
最容易被忽略的点是:重构不是改完就提交。动态字段排序、LIKE 模糊搜索、批量插入的占位符生成……这些地方没法走预处理,必须靠白名单校验或正则过滤,漏掉一个,前面所有PDO努力就归零。



















