一个错误的历史:为什么mysql_*是上个世纪的产物
\n\n想象一下:你正在用PHP编写一个网站,连接到MySQL数据库,并使用大学里就熟悉的函数mysql_query()。一切正常,网站运行良好,用户也很满意。但有一天,你在日志中发现了一条奇怪的警告:「Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future」。这不仅仅是解释器的抱怨——这是一个不容忽视的危险信号。
在Stack Overflow上,问题「Why shouldn't I use mysql_* functions in PHP?」 获得了数千次浏览和数百条评论。这并非偶然。在本文中,我们将深入探讨为什么mysql_*函数(如mysql_connect、mysql_query、mysql_fetch_array等)成为了PHP开发者的头号公敌,通过实际代码示例展示如何替换它们,并提供逐步迁移计划。
\n\n什么是mysql_*以及为什么它们被移除?
\n\nmysql_*扩展(原始MySQL扩展)早在1997年就出现在PHP 2.0中。它简单直观:调用mysql_connect(),通过mysql_query()执行查询,获取结果。但这种简单性有其阴暗面。
从PHP 5.5.0开始,所有mysql_*函数都被标记为已弃用(deprecated),并在PHP 7.0中被完全移除。如果你尝试在PHP 7+上使用mysql_connect(),将会遇到致命错误:Call to undefined function mysql_connect()。原因并非PHP开发者的任性,而是出于安全性和架构方面的根本性问题。
放弃mysql_*的三个主要原因
\n\n- \n
- 缺乏SQL注入防护——这些函数不支持预处理语句(prepared statements),使得手动转义成为必需但经常被遗忘的步骤。 \n
- 过时的架构——不支持面向对象风格、存储过程、多查询和事务。 \n
- 缺乏发展——该扩展多年未更新,而MySQL本身却在不断变化(例如,出现了新的数据类型、字符集和认证机制)。 \n
实际示例:mysql_*如何毁掉你的网站
\n\n让我们看看一段经典代码,至今仍出现在旧教材和论坛中:
\n\n<?php\n// 过时代码 - 请勿使用!\n$link = mysql_connect('localhost', 'root', 'password');\nmysql_select_db('users_db', $link);\n\n$user_input = $_GET['username']; // 用户输入:' OR 1=1 --\n$query = "SELECT * FROM users WHERE username = '$user_input'";\n$result = mysql_query($query, $link);\n\nwhile ($row = mysql_fetch_assoc($result)) {\n echo "你好," . $row['username'] . "<br>";\n}\n?>\n\n问题出在哪里?如果攻击者在username参数中传入' OR 1=1 --,查询将变成:
SELECT * FROM users WHERE username = '' OR 1=1 --\n\n双破折号--会注释掉查询的剩余部分,而条件1=1始终为真。结果,你将获取到数据库中的所有用户,包括密码、电子邮件和其他敏感信息。这不是理论——这是一个真实的漏洞,每天都有成千上万的网站因此被攻破。
当然,你可以使用mysql_real_escape_string(),但是:
- \n
- 开发者经常忘记调用它。 \n
- 它无法防御所有类型的攻击(例如,基于字符集的攻击)。 \n
- 代码变得混乱且难以阅读。 \n
用什么替代mysql_*:两种现代方法
\n\n在现代PHP中,有两个优秀的扩展可用于操作MySQL:mysqli(MySQL Improved)和PDO(PHP Data Objects)。两者都支持