PHP 7 兼容 mysql_connect 的代码层实现
在 PHP 7 版本中,mysql_connect 等 mysql 系列函数已经不再被支持。实际上从 PHP 5.5 起官方就标记这些函数为废弃(deprecated),到了 PHP 7 直接移除。老项目如果直接调用,会抛出类似下面的致命错误:
Uncaught Error: Call to undefined function mysql_connect()
为什么会出现这个错误
很多 2015 年前后上线的中小站点,代码里到处是 mysql_query()、mysql_fetch_array()。这些项目往往托管在共享虚拟主机上,PHP 版本升级由主机商说了算。当主机把 PHP 从 5.x 升到 7.x,全站瞬间白屏,错误日志里全是 "Call to undefined function"。即使是自建服务器,很多运维为了省事也会在升级时直接砍掉旧扩展,等线上报错才发现调用链上到处是 mysql 函数。这时候临时写兼容层,往往比逐行改代码更快恢复服务。
要让 PHP 7 兼容 mysql_connect,主要有两种方式:
| 方式 | 做法 | 适用场景 |
|---|---|---|
| 安装 mysql 扩展 | 在运行环境层面恢复旧函数(如 mysqlnd 兼容扩展) | 能自行修改服务器配置 |
| 代码层定义函数 | 定义同名函数,内部改用 mysqli | 不方便改服务器环境 |
方式二不依赖任何额外扩展,只需要在项目入口引入一段兼容代码即可,适合不方便修改服务器环境的场景。需要注意的是,这只是我们自己定义了同名函数,底层操作仍然是 mysqli 系列。
兼容代码
把下面的代码放到项目公共入口文件(如 config.php 或 common.php)中:
<?php
$dbhost = DATA_HOST;
$dbport = 3306;
$dbuser = DATA_USERNAME;
$dbpass = DATA_PASSWORD;
$dbname = DATA_NAME;
if (!function_exists('mysql_connect')) {
function mysql_connect($dbhost, $dbuser, $dbpass) {
global $dbport;
global $dbname;
global $mysqli;
$mysqli = mysqli_connect("$dbhost:$dbport", $dbuser, $dbpass, $dbname);
return $mysqli;
}
function mysql_select_db($dbname) {
global $mysqli;
return mysqli_select_db($mysqli, $dbname);
}
function mysql_fetch_array($result) {
return mysqli_fetch_array($result);
}
function mysql_fetch_assoc($result) {
return mysqli_fetch_assoc($result);
}
function mysql_fetch_row($result) {
return mysqli_fetch_row($result);
}
function mysql_query($query) {
global $mysqli;
return mysqli_query($mysqli, $query);
}
function mysql_escape_string($data) {
global $mysqli;
return mysqli_real_escape_string($mysqli, $data);
}
function mysql_real_escape_string($data) {
global $mysqli;
return mysqli_real_escape_string($mysqli, $data);
}
function mysql_close() {
global $mysqli;
return mysqli_close($mysqli);
}
}
提示:原文章中的
mysql_real_escape_string存在递归调用自身的问题,这里已修正为调用mysqli_real_escape_string,否则会出现无限递归导致脚本崩溃。
常见函数的对应关系
| 旧函数 | mysqli 替代 |
|---|---|
| mysql_connect | mysqli_connect |
| mysql_select_db | mysqli_select_db |
| mysql_query | mysqli_query |
| mysql_fetch_array | mysqli_fetch_array |
| mysql_fetch_assoc | mysqli_fetch_assoc |
| mysql_fetch_row | mysqli_fetch_row |
| mysql_num_rows | mysqli_num_rows |
| mysql_fetch_object | mysqli_fetch_object |
| mysql_real_escape_string | mysqli_real_escape_string |
| mysql_close | mysqli_close |
老项目里出现而上面没覆盖到的函数,都可以按同样的模式补齐:定义同名函数,内部转发给 mysqli 对应函数即可。
mysql 与 mysqli 的关键差异
两个系列最本质的区别在连接方式。老式 mysql 扩展在 mysql_connect 时如果传入相同参数,会复用已有连接,这在长生命周期脚本里容易埋雷;mysqli 默认每次 mysqli_connect 都建立新连接,行为更可预期。其次,mysqli 同时提供面向对象和面向过程两套接口,$mysqli->query() 与 mysqli_query($mysqli, ...) 完全等价,老代码习惯了过程式写法,迁移时心智负担更小。
另一个差异是字符集处理。mysql 时代很多项目靠 SET NAMES utf8 手动切换,升级后忘了设置,中文数据就会乱码。在兼容代码里显式调用 mysqli_set_charset 是最稳妥的兜底。
安全提醒
兼容代码只是"救急",它让旧的拼接式 SQL 继续能跑,但拼接 SQL 的注入风险一点没少。如果短期内必须保留兼容层,至少要把所有外部输入(尤其是 $_GET、$_POST 里直接拼进查询的部分)过一遍 mysql_real_escape_string,并且确认数据库账号用的是最小权限,而不是 root。长期来看,预处理语句才是正解——占位符让参数和 SQL 结构彻底分离,从根上杜绝注入。
使用说明
- 代码通过
if (!function_exists('mysql_connect'))判断,仅在旧函数不存在时才定义,不会与已安装扩展冲突; - 兼容函数内部通过
global $mysqli维护同一个数据库连接句柄,保证mysql_connect之后的所有调用共享连接; - 若项目需要指定端口、库名以外的连接参数,可在
mysqli_connect中补充; - 连接后建议补一行
mysqli_set_charset($mysqli, 'utf8mb4'),避免中文乱码——很多老项目升 PHP 7 后遇到的"数据变成问号"问题,其实和 mysql_connect 无关,而是字符集没有显式设置。
一个真实迁移场景
某二手交易站托管在共享主机上,主机商强制把 PHP 升到 7.2,全站报 500。接手后只做了两件事:在 config.php 顶部引入上面这段兼容代码,再核对所有文件里的 mysql 函数调用;半小时后全站恢复访问。随后花了两个周末,把高频调用的查询改写成 mysqli 预处理语句,最后删掉了兼容代码。
迁移建议
虽然兼容代码可以让老项目快速跑起来,但从长期维护角度看,仍然建议逐步把代码迁移到 mysqli 或 PDO 的写法:
- 先用兼容代码保证项目在 PHP 7 下正常上线,解决燃眉之急;
- 新建代码一律使用
mysqli或 PDO 预处理语句,避免 SQL 注入风险,例如:
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
- 利用空闲窗口分批替换旧调用,最后删除兼容代码。
如果你的项目运行在 PHP 环境,还可以参考后端对接分类中的后端与部署文章,例如LNMP 环境搭建指南,了解 PHP 与数据库环境的完整配置方式。数据库层的安全加固可参考SQL 注入防御实践。
原文链接:https://www.cnblogs.com/cqzhuomi/articles/17284418.html(博客园 CQZHUOMI,转载)