diff --git a/app/Libraries/Search/BeatmapsetQueryParser.php b/app/Libraries/Search/BeatmapsetQueryParser.php index 4f948e0556a..68739cc6f3b 100644 --- a/app/Libraries/Search/BeatmapsetQueryParser.php +++ b/app/Libraries/Search/BeatmapsetQueryParser.php @@ -15,7 +15,8 @@ public static function parse(?string $query): array $options = []; // reference: https://github.com/ppy/osu/blob/f6baf49ad6b42c662a729ad05e18bd99bc48b4c7/osu.Game/Screens/Select/FilterQueryParser.cs - $keywords = preg_replace_callback('#\b(?\w+)(?(:|=|(>|<)(:|=)?))(?(".*")|(\S*))#i', function ($m) use (&$options) { + // (modified to allow escaping quotes) + $keywords = preg_replace_callback('#\b(?\w+)(?(:|=|(>|<)(:|=)?))(?("(?:\\"|[^"])*")|(\S*))#i', function ($m) use (&$options) { $key = strtolower($m['key']); $op = str_replace(':', '=', $m['op']); switch ($key) { @@ -226,9 +227,16 @@ private static function makeIntRangeOption($operator, $value) private static function makeTextOption($operator, $value) { - if ($operator === '=') { - return presence(trim($value, '"')); + if ($operator !== '=') { + return null; + } + + $len = strlen($value); + if ($len > 1 && $value[0] === '"' && $value[$len - 1] === '"') { + $value = substr($value, 1, $len - 2); } + + return presence(strtr($value, ['\"' => '"'])); } private static function statePrefixSearch($value): ?int diff --git a/tests/Libraries/Search/BeatmapsetQueryParserTest.php b/tests/Libraries/Search/BeatmapsetQueryParserTest.php index a51d7f924d6..652bfdaffe8 100644 --- a/tests/Libraries/Search/BeatmapsetQueryParserTest.php +++ b/tests/Libraries/Search/BeatmapsetQueryParserTest.php @@ -92,6 +92,7 @@ public function queryDataProvider() ['find me songs by artist=singer please', ['keywords' => 'find me songs by please', 'options' => ['artist' => 'singer']]], ['really like artist="name with space" yes', ['keywords' => 'really like yes', 'options' => ['artist' => 'name with space']]], ['weird artist=double"quote', ['keywords' => 'weird', 'options' => ['artist' => 'double"quote']]], + ['weird artist="escaped \"quote\"" thing', ['keywords' => 'weird thing', 'options' => ['artist' => 'escaped "quote"']]], ['artist=> null, 'options' => ['artist' => '> 'unrecognised=keyword', 'options' => []]], ['cs=nope', ['keywords' => 'cs=nope', 'options' => []]],