Bladeren bron

Allow regex parentheses (#6926)

* Allow regex parentheses
While waiting for a new better search parser, auto-escape parentheses in regex expressions to allow them like in `/^(ab|cd)/`

* Allow escaped parenthesis in regex

* A couple more tests
Alexandre Alapetite 1 jaar geleden
bovenliggende
commit
60dd22d3b3
3 gewijzigde bestanden met toevoegingen van 31 en 0 verwijderingen
  1. 15 0
      app/Models/BooleanSearch.php
  2. 1 0
      app/Models/Search.php
  3. 15 0
      tests/app/Models/SearchTest.php

+ 15 - 0
app/Models/BooleanSearch.php

@@ -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")'
 	 */

+ 1 - 0
app/Models/Search.php

@@ -94,6 +94,7 @@ class FreshRSS_Search {
 	public function __construct(string $input) {
 		$input = self::cleanSearch($input);
 		$input = self::unescape($input);
+		$input = FreshRSS_BooleanSearch::unescapeRegexParentheses($input);
 		$this->raw_input = $input;
 
 		$input = $this->parseNotEntryIds($input);

+ 15 - 0
tests/app/Models/SearchTest.php

@@ -454,6 +454,21 @@ class SearchTest extends PHPUnit\Framework\TestCase {
 				'AND ((e.title LIKE ? AND e.title NOT LIKE ? AND e.content NOT LIKE ? AND e.title NOT LIKE ? AND e.content NOT LIKE ? ))',
 				['%ab%', '%ab%', '%cd%', '%cd%', '%ef%', '%ef%', '%gh%', '%ij%', '%ij%', '%kl%', '%kl%']
 			],
+			[
+				'/^(ab|cd) [(] \\) (ef|gh)/',
+				'((e.title ~ ? OR e.content ~ ?) )',
+				['^(ab|cd) [(] \\) (ef|gh)', '^(ab|cd) [(] \\) (ef|gh)']
+			],
+			[
+				'!/^(ab|cd)/',
+				'(NOT e.title ~ ? AND NOT e.content ~ ? )',
+				['^(ab|cd)', '^(ab|cd)']
+			],
+			[
+				'intitle:/^(ab|cd)/',
+				'(e.title ~ ? )',
+				['^(ab|cd)']
+			],
 		];
 	}