PHP构建查询时检测OR运算符序列并为其内容添加括号
解决方案:给连续OR条件自动添加分组括号
思路一:构建条件时维护OR分组(推荐)
直接在拼接条件的阶段就按AND/OR逻辑分组,从根源避免字符串处理的麻烦,这比事后正则替换更可靠,也不容易出现边界错误。
修改后的PHP代码
// 初始化分组(首次添加条件) if (!isset($_SESSION['filter_groups_' . $c])) { // 处理第一个条件的NOT情况 $initialCondition = $operator === "NOT" ? "NOT $selected_condition" : $selected_condition; // 用二维数组存分组:外层是AND连接的组,内层是OR连接的条件 $_SESSION['filter_groups_' . $c] = [[$initialCondition]]; } // 处理后续添加的条件 $validOperators = ['OR', 'OR NOT', 'AND', 'AND NOT']; if (in_array($operator, $validOperators)) { $groups = $_SESSION['filter_groups_' . $c]; if (str_starts_with($operator, 'AND')) { // AND开头的运算符:新建一个OR分组 $condition = str_replace('AND ', '', $operator) . ' ' . $selected_condition; $groups[] = [$condition]; } else { // OR开头的运算符:把条件加到最后一个OR分组里 $condition = str_replace('OR ', '', $operator) . ' ' . $selected_condition; // 定位到最后一个分组 end($groups); $lastGroupKey = key($groups); $groups[$lastGroupKey][] = $condition; } $_SESSION['filter_groups_' . $c] = $groups; } // 生成最终的WHERE子句 $groups = $_SESSION['filter_groups_' . $c]; $queryClauses = []; foreach ($groups as $orGroup) { if (count($orGroup) === 1) { // 单个条件不需要括号 $queryClauses[] = $orGroup[0]; } else { // 多个OR条件用括号包裹 $queryClauses[] = '(' . implode(' OR ', $orGroup) . ')'; } } $finalWhereClause = 'WHERE ' . implode(' AND ', $queryClauses);
这个逻辑的核心是:
- 用二维数组
filter_groups保存结构:外层数组的每个元素是一组由OR连接的条件,组与组之间用AND连接 - 当用户选择
AND/AND NOT时,新建一个OR分组;选择OR/OR NOT时,把条件追加到当前最后一个OR分组 - 最后拼接时,自动给包含多个条件的OR分组加上括号
思路二:正则替换处理已生成的字符串(备选)
如果不想修改原有构建逻辑,可以用正则对最终生成的WHERE子句做替换,但要注意条件文本中不能包含AND/OR这类关键字(否则会误匹配)。
正则替换代码
$originalWhere = "WHERE condition AND condition OR NOT condition AND condition OR condition OR condition"; // 第一步:给AND后面的OR组开头加左括号 $step1 = preg_replace('/AND\s+(?=(?:(?!AND\s).)*OR\s)/', 'AND (', $originalWhere); // 第二步:给每个AND(非括号内的)前面加右括号 $step2 = preg_replace('/\s+AND\s+(?![^(]*\))/', ') AND ', $step1); // 第三步:如果结尾是未闭合的OR组,补右括号 if (str_contains($step2, '(') && !str_ends_with(trim($step2), ')')) { $finalWhere = $step2 . ')'; } else { $finalWhere = $step2; } // 输出结果:WHERE condition AND (condition OR NOT condition) AND (condition OR condition OR condition) echo $finalWhere;
正则说明
- 第一个正则
/AND\s+(?=(?:(?!AND\s).)*OR\s)/:匹配后面跟着OR条件的AND,替换成AND ( - 第二个正则
/\s+AND\s+(?![^(]*\))/:匹配不在括号内的AND,替换成) AND,实现闭合前一个OR组 - 最后补全结尾可能缺失的右括号
内容的提问来源于stack exchange,提问作者Victor Canela
相关产品推荐
相关产品推荐

