MySQL通用SELECT查询类执行触发无限循环问题排查
问题分析与解决方案
核心问题排查
你遇到的无限循环+重复日志问题,主要有以下几个根源,结合代码逐一分析:
1. 调用代码的语法错误
你贴出的调用代码存在语法错误:末尾多了一个闭合括号)和左大括号{,这会导致PHP解析失败。如果服务器配置了错误页面重定向到原脚本,就会触发无限请求循环,每次请求都会执行Select方法并写入日志。
修正后的调用代码:
$obj_artikel_startseite = new DbQuery(); $result = $obj_artikel_startseite->Select( array("*"), "psartikel", array( array("link" => "AND", "key" => "status", "value" => "freigeschaltet", "operand" => "="), array("link" => "OR", "key" => "status", "value" => "reserviert", "operand" => "=") ), array("id"), "DESC", 0, 3 );
2. LIMIT条件判断逻辑错误
你的LIMIT判断用了$limitIndex != null && $limitLength != null,但在PHP的松散比较中,0 != null会返回false(因为0和null松散相等),导致传入的0,3无法触发LIMIT子句。这虽然不是无限循环的直接原因,但会导致查询结果不符合预期。
修正为严格的非空判断:
if (!is_null($limitIndex) && !is_null($limitLength)) { // 强制转成整数,避免SQL注入风险 $statement .= " LIMIT " . (int)$limitIndex . "," . (int)$limitLength; }
3. 异常未捕获导致的递归循环
如果你的DatabaseFactory返回的PDO对象开启了PDO::ERRMODE_EXCEPTION错误模式,$query->execute()执行失败时会抛出异常。若全局异常处理逻辑中再次调用了Select方法,就会触发无限递归。
添加异常捕获逻辑,避免循环:
// 在$query->execute()前后添加try-catch try { $query->execute(); } catch(PDOException $e) { // 单独记录错误日志,避免干扰正常日志 $errorLog = date("Y-m-d H:i:s", time()) . " => Execute Error: " . $e->getMessage() . "\n"; file_put_contents($_SERVER["DOCUMENT_ROOT"] . '/../db-error.log', $errorLog, FILE_APPEND); return false; }
4. 日志写入的性能与锁问题
你当前的日志写入方式是先读取整个文件再追加,高并发(或循环请求)下容易出现文件锁冲突,也会加剧性能问题。改用FILE_APPEND模式直接追加:
// 替换原日志写入代码 $logContent = date("Y-m-d H:i:s", time()) . " => Statement: " . $statement . "\n"; file_put_contents($_SERVER["DOCUMENT_ROOT"] . '/../db.log', $logContent, FILE_APPEND); // 绑定值的日志同理 $bindLog = date("Y-m-d H:i:s", time()) . " => BindValue: " . $keystring . " -> " . trim(strip_tags($whereClause['value'])) . "\n"; file_put_contents($_SERVER["DOCUMENT_ROOT"] . '/../db.log', $bindLog, FILE_APPEND);
最终修正后的Select方法代码
public function Select($keys = array("*"), $table, $whereClauseArray = null, $orderValues = null, $orderDirection = null, $limitIndex = null, $limitLength = null) { $statement = "SELECT " . implode(',', $keys) . " FROM " . $table; // 处理WHERE子句 if (!empty($whereClauseArray)) { $whereClauseString = " WHERE "; $i = 0; foreach ($whereClauseArray as $whereClause) { if ($i > 0) { $whereClauseString .= " " . $whereClause['link'] . " "; } $whereClauseString .= $whereClause['key'] . " " . $whereClause['operand'] . " :" . $whereClause['key'] . "_" . $i; $i++; } $statement .= $whereClauseString; } // 处理ORDER BY子句(简化写法) if (!empty($orderValues)) { $orderClause = " ORDER BY " . implode(', ', $orderValues); $statement .= $orderClause . " " . $orderDirection; } // 处理LIMIT子句 if (!is_null($limitIndex) && !is_null($limitLength)) { $statement .= " LIMIT " . (int)$limitIndex . "," . (int)$limitLength; } $query = $this->database->prepare($statement . ";"); // 写入SQL语句日志 $logContent = date("Y-m-d H:i:s", time()) . " => Statement: " . $statement . "\n"; file_put_contents($_SERVER["DOCUMENT_ROOT"] . '/../db.log', $logContent, FILE_APPEND); // 绑定参数(先判断非空) if (!empty($whereClauseArray)) { $j = 0; foreach ($whereClauseArray as $whereClause) { $keystring = ":" . $whereClause['key'] . "_" . $j; $bindValue = trim(strip_tags($whereClause['value'])); $query->bindValue($keystring, $bindValue); // 绑定日志 $bindLog = date("Y-m-d H:i:s", time()) . " => BindValue: " . $keystring . " -> " . $bindValue . "\n"; file_put_contents($_SERVER["DOCUMENT_ROOT"] . '/../db.log', $bindLog, FILE_APPEND); $j++; } } // 执行查询并捕获异常 try { $query->execute(); } catch(PDOException $e) { $errorLog = date("Y-m-d H:i:s", time()) . " => Execute Error: " . $e->getMessage() . "\n"; file_put_contents($_SERVER["DOCUMENT_ROOT"] . '/../db-error.log', $errorLog, FILE_APPEND); return false; } // 返回结果(修正count(*)的判断逻辑) if ($keys === array("count(*)")) { return $query->fetchColumn(0); } else { return $query->fetchAll(); } }
注:原代码中if ($keys === "count(*)")的判断逻辑错误,因为$keys默认是数组类型,需改为判断$keys === array("count(*)")才能触发fetchColumn逻辑。
内容的提问来源于stack exchange,提问作者meDom
相关产品推荐
相关产品推荐

