|
|
@@ -37,6 +37,7 @@ class FreshRSS_BooleanSearch {
|
|
|
if ($level === 0) {
|
|
|
$input = $this->parseUserQueryNames($input, $allowUserQueries);
|
|
|
$input = $this->parseUserQueryIds($input, $allowUserQueries);
|
|
|
+ $input = self::escapeRegexParentheses($input);
|
|
|
$input = trim($input);
|
|
|
}
|
|
|
|
|
|
@@ -132,6 +133,20 @@ class FreshRSS_BooleanSearch {
|
|
|
return $input;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Temporarily escape parentheses used in regex expressions.
|
|
|
+ */
|
|
|
+ public static function escapeRegexParentheses(string $input): string {
|
|
|
+ return preg_replace_callback('#(?<=[\\s(:!-]|^)(?<![\\\\])/.*?(?<!\\\\)/[im]*#',
|
|
|
+ fn(array $matches): string => str_replace(['(', ')'], ['\\u0028', '\\u0029'], $matches[0]),
|
|
|
+ $input
|
|
|
+ ) ?? '';
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function unescapeRegexParentheses(string $input): string {
|
|
|
+ return str_replace(['\\u0028', '\\u0029'], ['(', ')'], $input);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Example: 'ab cd OR ef OR "gh ij"' becomes '(ab cd) OR (ef) OR ("gh ij")'
|
|
|
*/
|