将mysql_* 函数升级到 MySQLi API 的终极指南
新 mysqli 扩展的原因是利用 MySQL 系统版本 4.1.3 和更新版本中的新功能。将现有代码从 mysql_* 更改为 mysqli API 时,您应该利用这些改进,否则您的升级工作可能会徒劳无功。
mysqli 扩展有很多好处,对 mysql 扩展的主要改进是:
从mysql_* 函数升级到 MySQLi 时,重要的是要考虑这些特性,以及应该使用此 API 的方式的一些变化。
1。面向对象的接口与过程函数。
新的 mysqli 面向对象接口是对旧功能的重大改进,它可以使您的代码更清晰,更不容易受到印刷错误的影响。还有这个 API 的过程版本,但不鼓励使用它,因为它会导致代码可读性降低,更容易出错。
要使用 MySQLi 打开与数据库的新连接,您需要创建 MySQLi 类的新实例。
$mysqli = new \mysqli($host, $user, $password, $dbName);
$mysqli->set_charset('utf8mb4');
使用程序样式,它看起来像这样:
$mysqli = mysqli_connect($host, $user, $password, $dbName);
mysqli_set_charset($mysqli, 'utf8mb4');
请记住,只有前 3 个参数与 mysql_connect 中的相同。旧 API 中的相同代码是:
$link = mysql_connect($host, $user, $password);
mysql_select_db($dbName, $link);
mysql_query('SET NAMES utf8');
如果您的 PHP 代码依赖于 php.ini 中定义的默认参数的隐式连接,您现在必须打开 MySQLi 连接并在代码中传递参数,然后提供所有程序函数的连接链接或使用 OOP 样式.
更多信息见文章:How to connect properly using mysqli
2。支持预处理语句
这是一件大事。 MySQL 在 MySQL 4.1 (2004) 中增加了对本机准备语句的支持。准备好的语句是prevent SQL injection 的最佳方式。对原生预处理语句的支持被添加到 PHP 中是合乎逻辑的。只要数据需要与 SQL 语句一起传递,就应该使用准备好的语句(即WHERE、INSERT 或UPDATE 是常见的用例)。
旧的 MySQL API 有一个用于转义 SQL 中使用的字符串的函数,称为 mysql_real_escape_string,但 it was never intended for protection against SQL injections 自然不应该用于此目的。
新的 MySQLi API 提供了一个替代函数 mysqli_real_escape_string 以实现向后兼容性,该函数与旧函数存在相同的问题,因此除非准备好的语句不可用,否则不应使用该函数。
旧的mysql_*方式:
$login = mysql_real_escape_string($_POST['login']);
$result = mysql_query("SELECT * FROM users WHERE user='$login'");
准备好的语句方式:
$stmt = $mysqli->prepare('SELECT * FROM users WHERE user=?');
$stmt->bind_param('s', $_POST['login']);
$stmt->execute();
$result = $stmt->get_result();
MySQLi 中的准备好的语句对于初学者来说可能看起来有点反感。如果您正在开始一个新项目,那么决定使用更强大、更简单的PDO API 可能是个好主意。
3。增强的调试功能
一些老式的 PHP 开发人员习惯于手动检查 SQL 错误并直接在浏览器中显示它们作为调试的手段。然而,事实证明,这种做法不仅繁琐,而且存在安全风险。幸运的是 MySQLi 改进了错误报告功能。
MySQLi 能够将遇到的任何错误报告为 PHP 异常。 PHP 异常将在脚本中冒泡,如果未处理将立即终止它,这意味着错误语句之后的任何语句都不会被执行。该异常将触发 PHP 致命错误,并将表现为任何从 PHP 核心触发的错误,遵循 display_errors 和 log_errors 设置。要启用 MySQLi 异常,请使用行 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 并在您打开数据库连接之前将其插入。
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new \mysqli($host, $user, $password, $dbName);
$mysqli->set_charset('utf8mb4');
如果你习惯写这样的代码:
$result = mysql_query('SELECT * WHERE 1=1');
if (!$result) {
die('Invalid query: ' . mysql_error());
}
或
$result = mysql_query('SELECT * WHERE 1=1') or die(mysql_error());
您不再需要在代码中使用die()。
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new \mysqli($host, $user, $password, $dbName);
$mysqli->set_charset('utf8mb4');
$result = $mysqli->query('SELECT * FROM non_existent_table');
// The following line will never be executed due to the mysqli_sql_exception being thrown above
foreach ($result as $row) {
// ...
}
如果由于某种原因您不能使用异常,MySQLi 具有等效的错误检索功能。您可以使用mysqli_connect_error() 检查连接错误,使用mysqli_error($mysqli) 检查任何其他错误。注意mysqli_error($mysqli)中的强制参数,或者坚持OOP风格并使用$mysqli->error。
$result = $mysqli->query('SELECT * FROM non_existent_table') or trigger_error($mysqli->error, E_USER_ERROR);
查看这些帖子以获得更多解释:
mysqli or die, does it have to die?
How to get MySQLi error information in different environments?
4。其他变化
不幸的是,并非mysql_* 中的每个函数在 MySQLi 中都有对应的函数,只是在名称和连接链接中添加了一个“i”作为第一个参数。以下是其中的一些列表:
-
mysql_client_encoding() 已替换为 mysqli_character_set_name($mysqli)
-
mysql_create_db 没有对应的。改用准备好的语句或mysqli_query
-
mysql_drop_db 没有对应的。改用准备好的语句或mysqli_query
-
mysql_db_name & mysql_list_dbs 支持已被放弃,支持 SQL 的 SHOW DATABASES
-
mysql_list_tables 支持已被放弃,转而支持 SQL 的 SHOW TABLES FROM dbname
-
mysql_list_fields 支持已被放弃,转而支持 SQL 的 SHOW COLUMNS FROM sometable
-
mysql_db_query -> 使用mysqli_select_db() 然后查询或在查询中指定数据库名称
-
mysql_fetch_field($result, 5) -> 第二个参数(偏移量)在 mysqli_fetch_field 中不存在。您可以使用 mysqli_fetch_field_direct 记住返回的不同结果
-
mysql_field_flags、mysql_field_len、mysql_field_name、mysql_field_table & mysql_field_type -> 已替换为 mysqli_fetch_field_direct
-
mysql_list_processes 已被删除。如果您需要线程 ID,请使用 mysqli_thread_id
-
mysql_pconnect 已替换为 mysqli_connect() 和 p: 主机前缀
-
mysql_result -> 将mysqli_data_seek() 与mysqli_field_seek() 和mysqli_fetch_field() 结合使用
-
mysql_tablename 支持已被放弃以支持 SQL 的 SHOW TABLES
-
mysql_unbuffered_query 已被删除。有关更多信息,请参阅本文Buffered and Unbuffered queries