一、那个被时代抛弃的“老朋友”
如果你维护过一些几年前的 PHP 项目,或者从网上复制过古老的教程代码,你一定对这段代码不陌生:
php
编辑
1<?php
2$link = mysql_connect('localhost', 'user', 'password');
3mysql_select_db('my_database', $link);
4$result = mysql_query("SELECT * FROM users WHERE id = 1");
5$row = mysql_fetch_assoc($result);
6echo $row['username'];
7mysql_close($link);
8?>
看起来简洁明了,对吧?但在 PHP 5.5.0 版本中,这一整套 mysql_* 函数家族已经被标记为 Deprecated(废弃) ;而在 PHP 7.0.0(2015年发布)中,它们被 彻底移除。
这意味着:只要你的服务器环境升级到了 PHP 7 或更高版本(目前主流已是 PHP 8.2+),任何包含 mysql_connect、mysql_query 等函数的代码都会直接抛出 Fatal error: Uncaught Error: Call to undefined function mysql_connect(),导致网站瞬间白屏,业务停摆。
这不是危言耸听,而是无数运维和开发者在深夜加班时血淋淋的教训。
二、为什么要“赶尽杀绝”?mysql_* 的三大原罪
PHP 官方之所以如此决绝地移除这些函数,并非为了折腾开发者,而是因为 mysql_* 扩展本身存在无法修复的先天缺陷:
1. 缺乏面向对象支持,架构落后
mysql_* 是过程式编程的产物,不支持面向对象(OOP)。在现代 PHP 开发中,OOP 是构建可维护、可扩展系统的基石。坚持使用过程式数据库操作,会让代码难以模块化,测试困难,且无法利用现代框架(如 Laravel, Symfony)的特性。
2. 致命短板:不支持预处理语句(Prepared Statements)
这是最严重的安全隐患。mysql_* 函数没有内置机制来防止 SQL 注入攻击。
开发者必须手动转义所有输入(使用 mysql_real_escape_string),但人为疏忽无处不在。一旦忘记转义,黑客只需构造一个特殊的输入字符串,就能窃取数据库、删除表甚至控制服务器。
错误示范(极易被注入):
php
编辑
1// 危险!如果 $_GET['id'] 为 "1 OR 1=1",所有用户数据将被泄露
2$id = $_GET['id'];
3$query = "SELECT * FROM users WHERE id = $id";
4$result = mysql_query($query);
而现代的 PDO 和 MySQLi 原生支持预处理语句,将 SQL 逻辑与数据分离,从根本上杜绝了此类风险。
3. 功能停滞,不再维护
mysql_* 扩展仅支持 MySQL 4.1 之前的旧特性,不支持新版本的 MySQL 功能(如事务处理的多语句支持、字符集的高级配置、存储过程的完善调用等)。自 PHP 5.5 起,该扩展已进入“维护模式”,不再接收任何新功能或安全补丁。
三、救星登场:PDO vs MySQLi,该选谁?
既然 mysql_* 已死,我们该投向谁的怀抱?目前主流的两大替代方案是 PDO (PHP Data Objects) 和 MySQLi (MySQL Improved) 。
表格
| 特性 | PDO (PHP Data Objects) | MySQLi |
|---|---|---|
| 数据库支持 | 支持 12+ 种数据库 (MySQL, PostgreSQL, SQLite, Oracle等) | 仅支持 MySQL |
| API 风格 | 纯面向对象 | 支持面向对象 和 过程式(兼容旧习惯) |
| 预处理语句 | 命名参数 (:name) 或 问号占位符 (?) | 仅支持问号占位符 (?) |
| 灵活性 | 高,切换数据库只需改 DSN | 低,绑定 MySQL |
| 推荐指数 | ⭐⭐⭐⭐⭐ (首选) | ⭐⭐⭐⭐ (仅限纯 MySQL 项目) |
结论:除非你确定项目永远只绑定 MySQL 且团队极度抗拒 OOP,否则 PDO 是绝对的首选。它的通用性和命名参数特性让代码更清晰、更安全。
四、实战:如何将 mysql_* 迁移到 PDO?
光说不练假把式。下面我们将开篇的那个“僵尸代码”重构为现代化的 PDO 写法。
场景:根据 ID 查询用户信息
❌ 旧代码 (PHP 7+ 会报错)
php
编辑
1<?php
2$link = mysql_connect('localhost', 'user', 'password');
3mysql_select_db('my_database', $link);
4
5// 假设 $id 来自用户输入
6$id = $_GET['id'];
7
8// 手动转义(容易遗漏)
9$id = mysql_real_escape_string($id);
10
11$result = mysql_query("SELECT * FROM users WHERE id = $id");
12$row = mysql_fetch_assoc($result);
13
14echo $row['username'];
15mysql_close($link);
16?>
✅ 新代码 (基于 PDO,安全且兼容 PHP 8+)
php
编辑
1<?php
2$dsn = 'mysql:host=localhost;dbname=my_database;charset=utf8mb4';
3$username = 'user';
4$password = 'password';
5
6$options = [
7 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 开启异常模式
8 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认关联数组
9 PDO::ATTR_EMULATE_PREPARES => false, // 禁用模拟预处理,使用原生
10];
11
12try {
13 // 1. 建立连接
14 $pdo = new PDO($dsn, $username, $password, $options);
15
16 // 2. 准备语句 (Prepared Statement)
17 // 使用命名参数 :id,清晰易读
18 $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
19
20 // 3. 执行并传入数据
21 // 数据会自动进行类型处理和转义,彻底杜绝 SQL 注入
22 $stmt->execute(['id' => $_GET['id']]);
23
24 // 4. 获取结果
25 $row = $stmt->fetch();
26
27 if ($row) {
28 echo htmlspecialchars($row['username'], ENT_QUOTES, 'UTF-8');
29 } else {
30 echo "用户未找到";
31 }
32
33} catch (PDOException $e) {
34 // 生产环境中不要直接输出 $e->getMessage(),应记录日志
35 error_log("Database error: " . $e->getMessage());
36 die("系统繁忙,请稍后重试");
37}
38?>
迁移关键点解析:
- DSN 连接字符串:取代了
mysql_connect和mysql_select_db,在一个字符串中定义主机、库名和字符集(推荐utf8mb4以支持 Emoji)。 - 异常处理:使用
try-catch块捕获数据库错误,而不是检查false返回值,代码逻辑更清晰。 - 预处理语句:
prepare()+execute()组合。注意看:id占位符,无论用户输入什么恶意字符,数据库都只将其视为“数据”,而非“指令”。 - 字符集设置:在 DSN 中直接指定
charset=utf8mb4,避免了乱码问题。
五、给维护者的行动建议
如果你的项目中还潜伏着 mysql_* 函数,请立即采取以下行动:
-
全局搜索:在 IDE 中搜索
mysql_,列出所有受影响的文件。 -
评估环境:确认当前生产环境的 PHP 版本。如果是 PHP 5.x,立即制定升级计划,因为 PHP 5 早已停止安全支持。
-
分步重构:
- 不要试图一次性重写整个项目。
- 创建一个通用的
Database.php类封装 PDO 连接。 - 按模块(如用户模块、文章模块)逐步替换旧的查询逻辑。
-
自动化测试:在重构前后运行测试用例,确保业务逻辑未被破坏。
-
锁定版本:在 Composer 或部署脚本中,强制要求 PHP 版本 >= 7.4 (推荐 8.1+),防止未来再次出现兼容性问题。
结语
技术迭代的浪潮从未停歇。mysql_* 函数的退场,标志着 PHP 生态从“草莽生长”走向了“规范安全”的成熟期。
对于开发者而言,拥抱 PDO 或 MySQLi 不仅仅是为了让代码在 PHP 7/8 上运行,更是为了构建一个更安全、更易维护、面向未来的系统。别让几行废弃的函数,成为阻碍你项目发展的绊脚石。
现在,就打开你的编辑器,开始那场迟来的“大扫除”吧!