
本文深入探讨 php `mysqli` 扩展中面向对象与过程式两种风格的用法与转换。我们将对比二者差异,纠正常见错误,并提供从面向对象到过程式风格的转换示例。文章强调在现代 php 开发中,应优先选择面向对象风格或 pdo,并展示如何通过启用错误报告和简化结果获取来编写更简洁、健壮的数据库交互代码。
在 PHP 数据库交互中,mysqli 扩展提供了两种主要的操作风格:面向对象(Object-Oriented, OO)和过程式(Procedural)。理解这两种风格的差异及其适用场景,对于编写高效且可维护的数据库代码至关重要。
mysqli 扩展最初设计时,面向对象风格是其推荐的使用方式,而过程式风格主要是为了帮助那些从旧版 PHP(如 PHP 4 的 mysql 扩展)迁移的用户平滑过渡。因此,在新的项目或代码中,强烈建议采用面向对象风格。
两者核心差异在于函数调用方式和参数传递:
值得注意的是,使用 mysqli 的面向对象风格并不意味着您的整个应用程序就必须是面向对象的。它仅仅是一种更现代、更清晰的 API 使用方式。
立即学习“PHP免费学习笔记(深入)”;
用户在尝试将面向对象风格的代码转换为过程式时,常会遇到以下错误:
$connection = mysqli_connect('localhost', 'root', '', 'ems');
$query = "SELECT * FROM bookings WHERE MONTH(event_date) = ? AND YEAR(event_date)=?";
$get_dates = mysqli_query($connection, $query); // 错误的使用方式
while($row = mysqli_fetch_assoc($get_dates)){
$booked_dates[] = $row['event_date'];
}这段代码会抛出 Fatal error: Uncaught TypeError: mysqli_fetch_assoc(): Argument #1 ($result) must be of type mysqli_result 错误。
错误原因: 问题在于 mysqli_query() 函数的使用。mysqli_query() 适用于执行不带参数的简单查询,或者在非预处理语句模式下获取结果。当使用带有占位符(?)的预处理语句时,您不能直接将其传递给 mysqli_query()。预处理语句需要经过 prepare、bind_param、execute 等一系列步骤才能正确执行和获取结果。mysqli_query() 在这种情况下会返回 false(表示查询失败,因为 SQL 语法不完整或不被支持),而不是 mysqli_result 对象,导致后续 mysqli_fetch_assoc() 接收到错误类型的参数。
尽管不推荐,但了解如何将面向对象风格的代码转换为过程式风格,有助于理解 mysqli 的内部机制。以下是将原始面向对象代码转换为过程式风格的示例:
原始面向对象代码:
function build_calendar($month, $year){
$mysqli = new mysqli('localhost', 'root', '', 'ems');
$stmt = $mysqli->prepare("SELECT * FROM bookings WHERE MONTH(event_date) = ? AND YEAR(event_date)=?");
$stmt->bind_param('ss', $month, $year);
$bookings = array();
if($stmt->execute()){
$result = $stmt->get_result();
if($result->num_rows>0){
while($row = $result->fetch_assoc()){
$bookings[] = $row['event_date'];
}
$stmt->close();
}
}
// 返回 $bookings 数组
return $bookings;
}转换为过程式风格:
function build_calendar_procedural($month, $year)
{
// 1. 建立数据库连接
$mysqli = mysqli_connect('localhost', 'root', '', 'ems');
if (mysqli_connect_errno()) {
// 错误处理:连接失败
error_log("Failed to connect to MySQL: " . mysqli_connect_error());
return []; // 返回空数组或抛出异常
}
// 2. 准备预处理语句
$stmt = mysqli_prepare($mysqli, "SELECT * FROM bookings WHERE MONTH(event_date) = ? AND YEAR(event_date)=?");
if (!$stmt) {
// 错误处理:语句准备失败
error_log("Failed to prepare statement: " . mysqli_error($mysqli));
mysqli_close($mysqli);
return [];
}
// 3. 绑定参数
mysqli_stmt_bind_param($stmt, 'ss', $month, $year);
$bookings = array();
// 4. 执行语句
if (mysqli_stmt_execute($stmt)) {
// 5. 获取结果集
$result = mysqli_stmt_get_result($stmt);
if ($result) { // 检查结果集是否成功获取
// 6. 检查行数并遍历结果
if (mysqli_num_rows($result) > 0) {
while ($row = mysqli_fetch_assoc($result)) {
$bookings[] = $row['event_date'];
}
}
mysqli_free_result($result); // 释放结果集内存
} else {
// 错误处理:获取结果集失败
error_log("Failed to get result set: " . mysqli_stmt_error($stmt));
}
} else {
// 错误处理:语句执行失败
error_log("Failed to execute statement: " . mysqli_stmt_error($stmt));
}
// 7. 关闭语句和数据库连接
mysqli_stmt_close($stmt);
mysqli_close($mysqli);
return $bookings;
}可以看到,过程式风格的函数调用方式只是将面向对象方法名转换为以 mysqli_ 或 mysqli_stmt_ 开头的函数名,并将 mysqli 或 stmt 对象作为第一个参数传入。
为了编写更健壮、简洁且易于维护的数据库代码,建议遵循以下实践:
坚持使用面向对象风格 mysqli 或 PDO:
启用 mysqli 错误报告: 默认情况下,mysqli 错误可能不会直接抛出异常,而是返回 false 或 null,需要手动检查。通过启用错误报告,可以将 mysqli 错误转换为异常,从而简化错误处理。
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // 之后,任何 mysqli 错误都会抛出 mysqli_sql_exception 异常
简化结果获取: 对于获取所有行,可以使用 fetch_all(MYSQLI_ASSOC) 一次性获取所有结果,而不是手动循环 while($row = $result->fetch_assoc())。
优化后的面向对象 mysqli 示例:
// 在应用程序初始化阶段设置错误报告,通常在连接数据库之前
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
// 数据库连接对象通常作为全局变量或通过依赖注入传递
// 确保只创建一次连接
$mysqli_connection = new mysqli('localhost', 'root', '', 'ems');
/**
* 从数据库中获取指定月份和年份的预订日期。
*
* @param mysqli $mysqli 数据库连接对象。
* @param string $month 月份(例如 '01' 到 '12')。
* @param string $year 年份。
* @return array 预订日期数组,例如 ['YYYY-MM-DD', ...]。
* @throws mysqli_sql_exception 如果数据库操作失败。
*/
function build_calendar_optimized(mysqli $mysqli, string $month, string $year): array
{
// 准备预处理语句
$stmt = $mysqli->prepare("SELECT event_date FROM bookings WHERE MONTH(event_date) = ? AND YEAR(event_date)=?");
// 绑定参数
// 'ss' 表示两个参数都是字符串类型
$stmt->bind_param('ss', $month, $year);
// 执行语句
$stmt->execute();
// 获取结果集
$result = $stmt->get_result();
// 使用 fetch_all(MYSQLI_ASSOC) 一次性获取所有结果并返回关联数组
// array_column 用于从结果集中提取 'event_date' 列
$bookings_data = $result->fetch_all(MYSQLI_ASSOC);
// 提取 'event_date' 列的值
return array_column($bookings_data, 'event_date');
}
// 示例调用
try {
$bookings = build_calendar_optimized($mysqli_connection, '01', '2020');
print_r($bookings);
} catch (mysqli_sql_exception $e) {
echo "数据库操作失败: " . $e->getMessage();
// 记录错误或向用户显示友好信息
} finally {
// 确保在所有操作完成后关闭连接(如果连接不是持久化的)
// 在 Web 应用中,PHP 脚本执行结束时通常会自动关闭连接
// $mysqli_connection->close();
}
在这个优化后的版本中:
尽管 mysqli 提供了面向对象和过程式两种风格,但在现代 PHP 开发中,强烈推荐使用面向对象风格,或更进一步选择 PDO。面向对象风格的 mysqli 提供了更清晰、更现代的 API。通过启用错误报告 (mysqli_report) 和利用 fetch_all 等高级功能,可以编写出更简洁、更健壮、更易于维护的数据库交互代码。避免使用 mysqli_query 处理预处理语句,并始终遵循预处理语句的正确流程:准备、绑定参数、执行、获取结果。
以上就是PHP mysqli 连接:面向对象与过程式风格解析与优化实践的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号