应从入口文件切入,先抽离初始化代码为bootstrap.php;再用PSR-4自动加载替代require_once;接着将全局函数封装进工具类;最后用轻量QueryBuilder封装数据库操作,禁用SQL注入风险点。

从入口文件开始切口,别碰核心逻辑
老项目往往一上来就 index.php 里塞了路由分发、数据库连接、模板渲染甚至业务判断。直接重写整个请求生命周期风险太高。先做最小干预:把 index.php 中重复出现的初始化代码(比如 require 'config.php'、new PDO(...))抽成一个独立的 bootstrap.php,只改这一处 include 路径,其他代码完全不动。这样后续替换数据库封装或日志组件时,只要改 bootstrap.php 里的单点实例化逻辑即可。
用命名空间 + 自动加载替代 require_once 堆叠
常见现象是满屏 require_once 'lib/Database.php'、require_once 'model/User.php',路径硬编码、顺序敏感、容易漏加。立刻停用所有 require/include 手动加载,改用 Composer 的 PSR-4 自动加载:
- 在
composer.json中声明"autoload": { "psr-4": { "App\": "src/" } } - 把原
model/User.php移到src/Model/User.php,顶部加namespace AppModel; - 运行
composer dump-autoload
注意:老项目类名可能不带命名空间,先不改类名,只加命名空间和移动位置;避免同时改命名+结构,否则错误堆叠难定位。
把全局函数封装进工具类,禁止新增裸函数
很多老项目靠一堆散装函数撑着,比如 format_date()、get_user_by_id()、send_sms()。这类函数调用无上下文、无法 mock、测试困难。重构动作不是重写,而是「包一层」:
立即学习“PHP免费学习笔记(深入)”;
- 新建
src/Helper/DateHelper.php,定义class DateHelper,把format_date()改成静态方法public static function format(string $timestamp): string - 原调用处从
format_date($time)改为DateHelper::format($time) - 保留旧函数名作为兼容层(可选):
function format_date(...$args) { return DateHelper::format(...$args); },但禁止在新代码里再用它
关键点:不删旧函数,只隔离调用路径;所有新功能必须走类方法,逐步收口。
数据库操作先套一层 QueryBuilder 封装,别急着上 ORM
直接切 Laravel Eloquent 或 Doctrine 对老项目是灾难——表字段命名不规范、SQL 拼接满天飞、事务混用。更务实的做法是自己写轻量 QueryBuilder:
- 封装
select()、where()、limit()等方法,底层仍用PDO::prepare()+ 参数绑定 - 保留原始 SQL 能力:提供
raw(string $sql, array $params)方法兜底 - 把原来散落在各处的
$pdo->query("SELECT * FROM user WHERE id = $id")替换为(new QueryBuilder())->from('user')->where('id', $id)->get()
这个封装不改变 SQL 语义,只是统一参数绑定和错误处理方式,后续才考虑升级为完整 ORM。最容易被忽略的是:老项目常有手动拼接 SQL 的 ORDER BY $_GET['sort'],这种必须立刻堵住,QueryBuilder 的 orderBy() 只接受白名单字段。



















