Remove AbstractSearchEngine::parseSearchQuery()
authorTim Düsterhus <duesterhus@woltlab.com>
Thu, 17 Dec 2020 08:27:26 +0000 (09:27 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 17 Dec 2020 08:28:41 +0000 (09:28 +0100)
Resolves #3815

wcfsetup/install/files/lib/system/search/AbstractSearchEngine.class.php
wcfsetup/install/files/lib/system/search/mysql/MysqlSearchEngine.class.php

index 4283eb8f99232d1dbc35755c930dca897f446360..d9855a734653478488662e074539cd79dbdd24c3 100644 (file)
@@ -2,7 +2,6 @@
 namespace wcf\system\search;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\SingletonFactory;
-use wcf\util\StringUtil;
 
 /**
  * Default implementation for search engines, this class should be extended by
@@ -34,99 +33,14 @@ abstract class AbstractSearchEngine extends SingletonFactory implements ISearchE
        }
        
        /**
-        * Manipulates the search term (< and > used as quotation marks):
-        * 
-        * - <test foo> becomes <+test* +foo*>
-        * - <test -foo bar> becomes <+test* -foo* +bar*>
-        * - <test "foo bar"> becomes <+test* +"foo bar">
-        * 
-        * @see http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
-        * 
-        * @param       string          $query
-        * @return      string
+        * @deprecated 5.4 - This method is dangerous. See https://github.com/WoltLab/WCF/issues/3815.
         */
        protected function parseSearchQuery($query) {
-               $query = StringUtil::trim($query);
-               
-               // expand search terms with a * unless they're encapsulated with quotes
-               $inQuotes = false;
-               $previousChar = $tmp = '';
-               $controlCharacterOrSpace = false;
-               $chars = ['+', '-', '*'];
-               $ftMinWordLen = $this->getFulltextMinimumWordLength();
-               for ($i = 0, $length = mb_strlen($query); $i < $length; $i++) {
-                       $char = mb_substr($query, $i, 1);
-                       
-                       if ($inQuotes) {
-                               if ($char == '"') {
-                                       $inQuotes = false;
-                               }
-                       }
-                       else {
-                               if ($char == '"') {
-                                       $inQuotes = true;
-                               }
-                               else {
-                                       if ($char == ' ' && !$controlCharacterOrSpace) {
-                                               $controlCharacterOrSpace = true;
-                                               $tmp .= '*';
-                                       }
-                                       else if (in_array($char, $chars)) {
-                                               $controlCharacterOrSpace = true;
-                                       }
-                                       else {
-                                               $controlCharacterOrSpace = false;
-                                       }
-                               }
-                       }
-                       
-                       /*
-                        * prepend a plus sign (logical AND) if ALL these conditions are given:
-                        * 
-                        * 1) previous character:
-                        *   - is empty (start of string)
-                        *   - is a space (MySQL uses spaces to separate words)
-                        * 
-                        * 2) not within quotation marks
-                        * 
-                        * 3) current char:
-                        *   - is NOT +, - or *
-                        */
-                       if (($previousChar == '' || $previousChar == ' ') && !$inQuotes && !in_array($char, $chars)) {
-                               // check if the term is shorter than the minimum fulltext word length
-                               if ($i + $ftMinWordLen <= $length) {
-                                       $term = '';// $char;
-                                       for ($j = $i, $innerLength = $ftMinWordLen + $i; $j < $innerLength; $j++) {
-                                               $currentChar = mb_substr($query, $j, 1);
-                                               if ($currentChar == '"' || $currentChar == ' ' || in_array($currentChar, $chars)) {
-                                                       break;
-                                               }
-                                               
-                                               $term .= $currentChar;
-                                       }
-                                       
-                                       if (mb_strlen($term) == $ftMinWordLen) {
-                                               $tmp .= '+';
-                                       }
-                               }
-                       }
-                       
-                       $tmp .= $char;
-                       $previousChar = $char;
-               }
-               
-               // handle last char
-               if (!$inQuotes && !$controlCharacterOrSpace) {
-                       $tmp .= '*';
-               }
-               
-               return $tmp;
+               throw new \BadMethodCallException('Using the generic `parseSearchQuery()` method is dangerous. See WoltLab/WCF#3815 (https://github.com/WoltLab/WCF/issues/3815).');
        }
        
        /**
-        * Returns minimum word length for fulltext indices.
-        * 
-        * @return      integer
+        * @deprecated 5.4 - This method was required for use in parseSearchQuery().
         */
        abstract protected function getFulltextMinimumWordLength();
        
index b460646775ef59d6eb4646f930e639042eee39a3..c0899a49112511f5cc941fd07855906cf9d15344 100644 (file)
@@ -7,6 +7,7 @@ use wcf\system\search\AbstractSearchEngine;
 use wcf\system\search\SearchEngine;
 use wcf\system\search\SearchIndexManager;
 use wcf\system\WCF;
+use wcf\util\StringUtil;
 
 /**
  * Search engine using MySQL's FULLTEXT index.
@@ -124,6 +125,96 @@ class MysqlSearchEngine extends AbstractSearchEngine {
                ];
        }
        
+       /**
+        * Manipulates the search term (< and > used as quotation marks):
+        * 
+        * - <test foo> becomes <+test* +foo*>
+        * - <test -foo bar> becomes <+test* -foo* +bar*>
+        * - <test "foo bar"> becomes <+test* +"foo bar">
+        * 
+        * @see http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
+        * 
+        * @param       string          $query
+        * @return      string
+        */
+       protected function parseSearchQuery($query) {
+               $query = StringUtil::trim($query);
+               
+               // expand search terms with a * unless they're encapsulated with quotes
+               $inQuotes = false;
+               $previousChar = $tmp = '';
+               $controlCharacterOrSpace = false;
+               $chars = ['+', '-', '*'];
+               $ftMinWordLen = $this->getFulltextMinimumWordLength();
+               for ($i = 0, $length = mb_strlen($query); $i < $length; $i++) {
+                       $char = mb_substr($query, $i, 1);
+                       
+                       if ($inQuotes) {
+                               if ($char == '"') {
+                                       $inQuotes = false;
+                               }
+                       }
+                       else {
+                               if ($char == '"') {
+                                       $inQuotes = true;
+                               }
+                               else {
+                                       if ($char == ' ' && !$controlCharacterOrSpace) {
+                                               $controlCharacterOrSpace = true;
+                                               $tmp .= '*';
+                                       }
+                                       else if (in_array($char, $chars)) {
+                                               $controlCharacterOrSpace = true;
+                                       }
+                                       else {
+                                               $controlCharacterOrSpace = false;
+                                       }
+                               }
+                       }
+                       
+                       /*
+                        * prepend a plus sign (logical AND) if ALL these conditions are given:
+                        * 
+                        * 1) previous character:
+                        *   - is empty (start of string)
+                        *   - is a space (MySQL uses spaces to separate words)
+                        * 
+                        * 2) not within quotation marks
+                        * 
+                        * 3) current char:
+                        *   - is NOT +, - or *
+                        */
+                       if (($previousChar == '' || $previousChar == ' ') && !$inQuotes && !in_array($char, $chars)) {
+                               // check if the term is shorter than the minimum fulltext word length
+                               if ($i + $ftMinWordLen <= $length) {
+                                       $term = '';// $char;
+                                       for ($j = $i, $innerLength = $ftMinWordLen + $i; $j < $innerLength; $j++) {
+                                               $currentChar = mb_substr($query, $j, 1);
+                                               if ($currentChar == '"' || $currentChar == ' ' || in_array($currentChar, $chars)) {
+                                                       break;
+                                               }
+                                               
+                                               $term .= $currentChar;
+                                       }
+                                       
+                                       if (mb_strlen($term) == $ftMinWordLen) {
+                                               $tmp .= '+';
+                                       }
+                               }
+                       }
+                       
+                       $tmp .= $char;
+                       $previousChar = $char;
+               }
+               
+               // handle last char
+               if (!$inQuotes && !$controlCharacterOrSpace) {
+                       $tmp .= '*';
+               }
+               
+               return $tmp;
+       }
+       
        /**
         * @inheritDoc
         */