2 namespace wcf\system\search
;
3 use wcf\system\database\util\PreparedStatementConditionBuilder
;
4 use wcf\system\SingletonFactory
;
5 use wcf\util\StringUtil
;
8 * Default implementation for search engines, this class should be extended by
9 * all search engines to preserve compatibility in case of interface changes.
11 * @author Alexander Ebert
12 * @copyright 2001-2019 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\System\Search
16 abstract class AbstractSearchEngine
extends SingletonFactory
implements ISearchEngine
{
18 * class name for preferred condition builder
21 protected $conditionBuilderClassName = PreparedStatementConditionBuilder
::class;
24 * list of engine-specific special characters
27 protected $specialCharacters = [];
32 public function getConditionBuilderClassName() {
33 return $this->conditionBuilderClassName
;
37 * Manipulates the search term (< and > used as quotation marks):
39 * - <test foo> becomes <+test* +foo*>
40 * - <test -foo bar> becomes <+test* -foo* +bar*>
41 * - <test "foo bar"> becomes <+test* +"foo bar">
43 * @see http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
45 * @param string $query
48 protected function parseSearchQuery($query) {
49 $query = StringUtil
::trim($query);
51 // expand search terms with a * unless they're encapsulated with quotes
53 $previousChar = $tmp = '';
54 $controlCharacterOrSpace = false;
55 $chars = ['+', '-', '*'];
56 $ftMinWordLen = $this->getFulltextMinimumWordLength();
57 for ($i = 0, $length = mb_strlen($query); $i < $length; $i++
) {
58 $char = mb_substr($query, $i, 1);
70 if ($char == ' ' && !$controlCharacterOrSpace) {
71 $controlCharacterOrSpace = true;
74 else if (in_array($char, $chars)) {
75 $controlCharacterOrSpace = true;
78 $controlCharacterOrSpace = false;
84 * prepend a plus sign (logical AND) if ALL these conditions are given:
86 * 1) previous character:
87 * - is empty (start of string)
88 * - is a space (MySQL uses spaces to separate words)
90 * 2) not within quotation marks
95 if (($previousChar == '' ||
$previousChar == ' ') && !$inQuotes && !in_array($char, $chars)) {
96 // check if the term is shorter than the minimum fulltext word length
97 if ($i +
$ftMinWordLen <= $length) {
99 for ($j = $i, $innerLength = $ftMinWordLen +
$i; $j < $innerLength; $j++
) {
100 $currentChar = mb_substr($query, $j, 1);
101 if ($currentChar == '"' ||
$currentChar == ' ' ||
in_array($currentChar, $chars)) {
105 $term .= $currentChar;
108 if (mb_strlen($term) == $ftMinWordLen) {
115 $previousChar = $char;
119 if (!$inQuotes && !$controlCharacterOrSpace) {
127 * Returns minimum word length for fulltext indices.
131 abstract protected function getFulltextMinimumWordLength();
136 public function removeSpecialCharacters($string) {
137 if (!empty($this->specialCharacters
)) {
138 return str_replace($this->specialCharacters
, '', $string);