
本文深入探讨 php `mysqli` 扩展中面向对象与过程式两种风格的用法与转换。我们将对比二者差异,纠正常见错误,并提供从面向对象到过程式风格的转换示例。文章强调在现代 php 开发中,应优先选择面向对象风格或 pdo,并展示如何通过启用错误报告和简化结果获取来编写更简洁、健壮的数据库交互代码。
在 PHP 数据库交互中,mysqli 扩展提供了两种主要的操作风格:面向对象(Object-Oriented, OO)和过程式(Procedural)。理解这两种风格的差异及其适用场景,对于编写高效且可维护的数据库代码至关重要。
mysqli 的面向对象与过程式风格
mysqli 扩展最初设计时,面向对象风格是其推荐的使用方式,而过程式风格主要是为了帮助那些从旧版 PHP(如 PHP 4 的 mysql 扩展)迁移的用户平滑过渡。因此,在新的项目或代码中,强烈建议采用面向对象风格。
两者核心差异在于函数调用方式和参数传递:
面向对象风格:通过 mysqli 类的实例调用方法,例如 $mysqli->prepare()。过程式风格:通过全局函数调用,并将 mysqli 实例作为第一个参数传入,例如 mysqli_prepare($mysqli, …)。
值得注意的是,使用 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 代码转换为过程式风格
尽管不推荐,但了解如何将面向对象风格的代码转换为过程式风格,有助于理解 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 实践与优化
为了编写更健壮、简洁且易于维护的数据库代码,建议遵循以下实践:
坚持使用面向对象风格 mysqli 或 PDO:
PDO (PHP Data Objects) 是更推荐的选择,因为它提供了一致的接口来连接多种数据库,并且通常具有更简洁的 API。如果必须使用 mysqli,请始终选择面向对象风格。
启用 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_report() 实现了更强大的错误处理。函数接收 mysqli 对象作为参数,提高了代码的模块化和可测试性。使用 fetch_all(MYSQLI_ASSOC) 简化了结果的获取和处理,减少了循环代码。使用类型提示 (string, array, mysqli) 提高了代码的可读性和健壮性。
总结
尽管 mysqli 提供了面向对象和过程式两种风格,但在现代 PHP 开发中,强烈推荐使用面向对象风格,或更进一步选择 PDO。面向对象风格的 mysqli 提供了更清晰、更现代的 API。通过启用错误报告 (mysqli_report) 和利用 fetch_all 等高级功能,可以编写出更简洁、更健壮、更易于维护的数据库交互代码。避免使用 mysqli_query 处理预处理语句,并始终遵循预处理语句的正确流程:准备、绑定参数、执行、获取结果。
以上就是PHP mysqli 连接:面向对象与过程式风格解析与优化实践的详细内容,更多请关注php中文网其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1330947.html
微信扫一扫
支付宝扫一扫