public static function searchFor($strKeywords, $blnOrSearch=false, $arrPid=array(), $intRows=0, $intOffset=0, $blnFuzzy=false)
	{
		// Clean the keywords
		$strKeywords = StringUtil::decodeEntities($strKeywords);
		$strKeywords = Utf8::strtolower($strKeywords);

		// Check keyword string
		if (!\strlen($strKeywords))
		{
			throw new \Exception('Empty keyword string');
		}

		// Split keywords
		$arrChunks = array();
		preg_match_all('/"[^"]+"|[\+\-]?[^ ]+\*?/', $strKeywords, $arrChunks);

		$arrPhrases = array();
		$arrKeywords = array();
		$arrWildcards = array();
		$arrIncluded = array();
		$arrExcluded = array();

		foreach (array_unique($arrChunks[0]) as $strKeyword)
		{
			if (substr($strKeyword, -1) == '*' && \strlen($strKeyword) > 1)
			{
				$arrWildcards[] = str_replace('*', '%', $strKeyword);
				continue;
			}

			switch (substr($strKeyword, 0, 1))
			{
				// Phrases
				case '"':
					if ($strKeyword = trim(substr($strKeyword, 1, -1)))
					{
						$arrPhrases[] = str_replace(' ', '[^[:alnum:]]+', preg_quote($strKeyword));
					}
					break;

				// Included keywords
				case '+':
					if ($strKeyword = trim(substr($strKeyword, 1)))
					{
						foreach (self::splitIntoWords($strKeyword, $GLOBALS['TL_LANGUAGE']) as $strWord)
						{
							$arrIncluded[] = $strWord;
						}
					}
					break;

				// Excluded keywords
				case '-':
					if ($strKeyword = trim(substr($strKeyword, 1)))
					{
						foreach (self::splitIntoWords($strKeyword, $GLOBALS['TL_LANGUAGE']) as $strWord)
						{
							$arrExcluded[] = $strWord;
						}
					}
					break;

				// Wildcards
				case '*':
					if (\strlen($strKeyword) > 1)
					{
						$arrWildcards[] = str_replace('*', '%', $strKeyword);
					}
					break;

				// Normal keywords
				default:
					foreach (self::splitIntoWords($strKeyword, $GLOBALS['TL_LANGUAGE']) as $strWord)
					{
						$arrKeywords[] = $strWord;
					}
					break;
			}
		}

		// Fuzzy search
		if ($blnFuzzy)
		{
			foreach ($arrKeywords as $strKeyword)
			{
				$arrWildcards[] = '%' . $strKeyword . '%';
			}

			$arrKeywords = array();
		}

		// Count keywords
		$intPhrases = \count($arrPhrases);
		$intWildcards = \count($arrWildcards);
		$intIncluded = \count($arrIncluded);
		$intExcluded = \count($arrExcluded);

		$intKeywords = 0;
		$arrValues = array();

		// Remember found words so we can highlight them later
		$strQuery = "SELECT * FROM (SELECT tl_search_index.pid AS sid, GROUP_CONCAT(word) AS matches";

		// Get the number of wildcard matches
		if (!$blnOrSearch && $intWildcards)
		{
			$strQuery .= ", (SELECT COUNT(*) FROM tl_search_index WHERE (" . implode(' OR ', array_fill(0, $intWildcards, 'word LIKE ?')) . ") AND pid=sid) AS wildcards";
			$arrValues = array_merge($arrValues, $arrWildcards);
		}

		// Count the number of matches
		$strQuery .= ", COUNT(*) AS count";

		// Get the relevance
		$strQuery .= ", SUM(relevance) AS relevance";

		// Prepare keywords array
		$arrAllKeywords = array();

		// Get keywords
		if (!empty($arrKeywords))
		{
			$arrAllKeywords[] = implode(' OR ', array_fill(0, \count($arrKeywords), 'word=?'));
			$arrValues = array_merge($arrValues, $arrKeywords);
			$intKeywords += \count($arrKeywords);
		}

		// Get included keywords
		if ($intIncluded)
		{
			$arrAllKeywords[] = implode(' OR ', array_fill(0, $intIncluded, 'word=?'));
			$arrValues = array_merge($arrValues, $arrIncluded);
			$intKeywords += $intIncluded;
		}

		// Get keywords from phrases
		if ($intPhrases)
		{
			foreach ($arrPhrases as $strPhrase)
			{
				$arrWords = self::splitIntoWords(str_replace('[^[:alnum:]]+', ' ', $strPhrase), $GLOBALS['TL_LANGUAGE']);
				$arrAllKeywords[] = implode(' OR ', array_fill(0, \count($arrWords), 'word=?'));
				$arrValues = array_merge($arrValues, $arrWords);
				$intKeywords += \count($arrWords);
			}
		}

		// Get wildcards
		if ($intWildcards)
		{
			$arrAllKeywords[] = implode(' OR ', array_fill(0, $intWildcards, 'word LIKE ?'));
			$arrValues = array_merge($arrValues, $arrWildcards);
		}

		$strQuery .= " FROM tl_search_index WHERE (" . implode(' OR ', $arrAllKeywords) . ")";

		// Get phrases
		if ($intPhrases)
		{
			$strQuery .= " AND (" . implode(($blnOrSearch ? ' OR ' : ' AND '), array_fill(0, $intPhrases, 'tl_search_index.pid IN(SELECT id FROM tl_search WHERE text REGEXP ?)')) . ")";
			$arrValues = array_merge($arrValues, $arrPhrases);
		}

		// Include keywords
		if ($intIncluded)
		{
			$strQuery .= " AND tl_search_index.pid IN(SELECT pid FROM tl_search_index WHERE " . implode(' OR ', array_fill(0, $intIncluded, 'word=?')) . ")";
			$arrValues = array_merge($arrValues, $arrIncluded);
		}

		// Exclude keywords
		if ($intExcluded)
		{
			$strQuery .= " AND tl_search_index.pid NOT IN(SELECT pid FROM tl_search_index WHERE " . implode(' OR ', array_fill(0, $intExcluded, 'word=?')) . ")";
			$arrValues = array_merge($arrValues, $arrExcluded);
		}

		// Limit results to a particular set of pages
		if (!empty($arrPid) && \is_array($arrPid))
		{
			$strQuery .= " AND tl_search_index.pid IN(SELECT id FROM tl_search WHERE pid IN(" . implode(',', array_map('\intval', $arrPid)) . "))";
		}

		$strQuery .= " GROUP BY tl_search_index.pid";

		// Sort by relevance
		$strQuery .= " ORDER BY relevance DESC) matches LEFT JOIN tl_search ON(matches.sid=tl_search.id)";

		// Make sure to find all words
		if (!$blnOrSearch)
		{
			// Number of keywords without wildcards
			$strQuery .= " WHERE matches.count >= " . $intKeywords;

			// Dynamically add the number of wildcard matches
			if ($intWildcards)
			{
				$strQuery .= " + IF(matches.wildcards>" . $intWildcards . ", matches.wildcards, " . $intWildcards . ")";
			}
		}

		// Return result
		$objResultStmt = Database::getInstance()->prepare($strQuery);

		if ($intRows > 0)
		{
			$objResultStmt->limit($intRows, $intOffset);
		}

		return $objResultStmt->execute($arrValues);
	}
public static function searchFor($strKeywords, $blnOrSearch=false, $arrPid=array(), $intRows=0, $intOffset=0, $blnFuzzy=false)
	{
		
		$strKeywords = StringUtil::decodeEntities($strKeywords);
		$strKeywords = Utf8::strtolower($strKeywords);

		
		if (!\strlen($strKeywords))
		{
			throw new \Exception('Empty keyword string');
		}

		
		$arrChunks = array();
		preg_match_all('/"[^"]+"|[\+\-]?[^ ]+\*?/', $strKeywords, $arrChunks);

		$arrPhrases = array();
		$arrKeywords = array();
		$arrWildcards = array();
		$arrIncluded = array();
		$arrExcluded = array();

		foreach (array_unique($arrChunks[0]) as $strKeyword)
		{
			if (substr($strKeyword, -1) == '*' && \strlen($strKeyword) > 1)
			{
				$arrWildcards[] = str_replace('*', '%', $strKeyword);
				continue;
			}

			switch (substr($strKeyword, 0, 1))
			{
				
				case '"':
					if ($strKeyword = trim(substr($strKeyword, 1, -1)))
					{
						$arrPhrases[] = str_replace(' ', '[^[:alnum:]]+', preg_quote($strKeyword));
					}
					break;

				
				case '+':
					if ($strKeyword = trim(substr($strKeyword, 1)))
					{
						foreach (self::splitIntoWords($strKeyword, $GLOBALS['TL_LANGUAGE']) as $strWord)
						{
							$arrIncluded[] = $strWord;
						}
					}
					break;

				
				case '-':
					if ($strKeyword = trim(substr($strKeyword, 1)))
					{
						foreach (self::splitIntoWords($strKeyword, $GLOBALS['TL_LANGUAGE']) as $strWord)
						{
							$arrExcluded[] = $strWord;
						}
					}
					break;

				
				case '*':
					if (\strlen($strKeyword) > 1)
					{
						$arrWildcards[] = str_replace('*', '%', $strKeyword);
					}
					break;

				
				default:
					foreach (self::splitIntoWords($strKeyword, $GLOBALS['TL_LANGUAGE']) as $strWord)
					{
						$arrKeywords[] = $strWord;
					}
					break;
			}
		}

		
		if ($blnFuzzy)
		{
			foreach ($arrKeywords as $strKeyword)
			{
				$arrWildcards[] = '%' . $strKeyword . '%';
			}

			$arrKeywords = array();
		}

		
		$intPhrases = \count($arrPhrases);
		$intWildcards = \count($arrWildcards);
		$intIncluded = \count($arrIncluded);
		$intExcluded = \count($arrExcluded);

		$intKeywords = 0;
		$arrValues = array();

		
		$strQuery = "SELECT * FROM (SELECT tl_search_index.pid AS sid, GROUP_CONCAT(word) AS matches";

		
		if (!$blnOrSearch && $intWildcards)
		{
			$strQuery .= ", (SELECT COUNT(*) FROM tl_search_index WHERE (" . implode(' OR ', array_fill(0, $intWildcards, 'word LIKE ?')) . ") AND pid=sid) AS wildcards";
			$arrValues = array_merge($arrValues, $arrWildcards);
		}

		
		$strQuery .= ", COUNT(*) AS count";

		
		$strQuery .= ", SUM(relevance) AS relevance";

		
		$arrAllKeywords = array();

		
		if (!empty($arrKeywords))
		{
			$arrAllKeywords[] = implode(' OR ', array_fill(0, \count($arrKeywords), 'word=?'));
			$arrValues = array_merge($arrValues, $arrKeywords);
			$intKeywords += \count($arrKeywords);
		}

		
		if ($intIncluded)
		{
			$arrAllKeywords[] = implode(' OR ', array_fill(0, $intIncluded, 'word=?'));
			$arrValues = array_merge($arrValues, $arrIncluded);
			$intKeywords += $intIncluded;
		}

		
		if ($intPhrases)
		{
			foreach ($arrPhrases as $strPhrase)
			{
				$arrWords = self::splitIntoWords(str_replace('[^[:alnum:]]+', ' ', $strPhrase), $GLOBALS['TL_LANGUAGE']);
				$arrAllKeywords[] = implode(' OR ', array_fill(0, \count($arrWords), 'word=?'));
				$arrValues = array_merge($arrValues, $arrWords);
				$intKeywords += \count($arrWords);
			}
		}

		
		if ($intWildcards)
		{
			$arrAllKeywords[] = implode(' OR ', array_fill(0, $intWildcards, 'word LIKE ?'));
			$arrValues = array_merge($arrValues, $arrWildcards);
		}

		$strQuery .= " FROM tl_search_index WHERE (" . implode(' OR ', $arrAllKeywords) . ")";

		
		if ($intPhrases)
		{
			$strQuery .= " AND (" . implode(($blnOrSearch ? ' OR ' : ' AND '), array_fill(0, $intPhrases, 'tl_search_index.pid IN(SELECT id FROM tl_search WHERE text REGEXP ?)')) . ")";
			$arrValues = array_merge($arrValues, $arrPhrases);
		}

		
		if ($intIncluded)
		{
			$strQuery .= " AND tl_search_index.pid IN(SELECT pid FROM tl_search_index WHERE " . implode(' OR ', array_fill(0, $intIncluded, 'word=?')) . ")";
			$arrValues = array_merge($arrValues, $arrIncluded);
		}

		
		if ($intExcluded)
		{
			$strQuery .= " AND tl_search_index.pid NOT IN(SELECT pid FROM tl_search_index WHERE " . implode(' OR ', array_fill(0, $intExcluded, 'word=?')) . ")";
			$arrValues = array_merge($arrValues, $arrExcluded);
		}

		
		if (!empty($arrPid) && \is_array($arrPid))
		{
			$strQuery .= " AND tl_search_index.pid IN(SELECT id FROM tl_search WHERE pid IN(" . implode(',', array_map('\intval', $arrPid)) . "))";
		}

		$strQuery .= " GROUP BY tl_search_index.pid";

		
		$strQuery .= " ORDER BY relevance DESC) matches LEFT JOIN tl_search ON(matches.sid=tl_search.id)";

		
		if (!$blnOrSearch)
		{
			
			$strQuery .= " WHERE matches.count >= " . $intKeywords;

			
			if ($intWildcards)
			{
				$strQuery .= " + IF(matches.wildcards>" . $intWildcards . ", matches.wildcards, " . $intWildcards . ")";
			}
		}

		
		$objResultStmt = Database::getInstance()->prepare($strQuery);

		if ($intRows > 0)
		{
			$objResultStmt->limit($intRows, $intOffset);
		}

		return $objResultStmt->execute($arrValues);
	}
public static function searchFor ( $ strKeywords , $ blnOrSearch = false , $ arrPid = array ( ) , $ intRows = 0 , $ intOffset = 0 , $ blnFuzzy = false ) { // Clean the keywords $ strKeywords = StringUtil :: decodeEntities ( $ strKeywords ) ; $ strKeywords = Utf8 :: strtolower ( $ strKeywords ) ; // Check keyword string if ( ! \ strlen ( $ strKeywords ) ) { throw new \ Exception ( 'Empty keyword string' ) ; } // Split keywords $ arrChunks = array ( ) ; preg_match_all ( '/"[^"]+"|[\+\-]?[^ ]+\*?/' , $ strKeywords , $ arrChunks ) ; $ arrPhrases = array ( ) ; $ arrKeywords = array ( ) ; $ arrWildcards = array ( ) ; $ arrIncluded = array ( ) ; $ arrExcluded = array ( ) ; foreach ( array_unique ( $ arrChunks [ 0 ] ) as $ strKeyword ) { if ( substr ( $ strKeyword , - 1 ) == '*' && \ strlen ( $ strKeyword ) > 1 ) { $ arrWildcards [ ] = str_replace ( '*' , '%' , $ strKeyword ) ; continue ; } switch ( substr ( $ strKeyword , 0 , 1 ) ) { // Phrases case '"' : if ( $ strKeyword = trim ( substr ( $ strKeyword , 1 , - 1 ) ) ) { $ arrPhrases [ ] = str_replace ( ' ' , '[^[:alnum:]]+' , preg_quote ( $ strKeyword ) ) ; } break ; // Included keywords case '+' : if ( $ strKeyword = trim ( substr ( $ strKeyword , 1 ) ) ) { foreach ( self :: splitIntoWords ( $ strKeyword , $ GLOBALS [ 'TL_LANGUAGE' ] ) as $ strWord ) { $ arrIncluded [ ] = $ strWord ; } } break ; // Excluded keywords case '-' : if ( $ strKeyword = trim ( substr ( $ strKeyword , 1 ) ) ) { foreach ( self :: splitIntoWords ( $ strKeyword , $ GLOBALS [ 'TL_LANGUAGE' ] ) as $ strWord ) { $ arrExcluded [ ] = $ strWord ; } } break ; // Wildcards case '*' : if ( \ strlen ( $ strKeyword ) > 1 ) { $ arrWildcards [ ] = str_replace ( '*' , '%' , $ strKeyword ) ; } break ; // Normal keywords default : foreach ( self :: splitIntoWords ( $ strKeyword , $ GLOBALS [ 'TL_LANGUAGE' ] ) as $ strWord ) { $ arrKeywords [ ] = $ strWord ; } break ; } } // Fuzzy search if ( $ blnFuzzy ) { foreach ( $ arrKeywords as $ strKeyword ) { $ arrWildcards [ ] = '%' . $ strKeyword . '%' ; } $ arrKeywords = array ( ) ; } // Count keywords $ intPhrases = \ count ( $ arrPhrases ) ; $ intWildcards = \ count ( $ arrWildcards ) ; $ intIncluded = \ count ( $ arrIncluded ) ; $ intExcluded = \ count ( $ arrExcluded ) ; $ intKeywords = 0 ; $ arrValues = array ( ) ; // Remember found words so we can highlight them later $ strQuery = "SELECT * FROM (SELECT tl_search_index.pid AS sid, GROUP_CONCAT(word) AS matches" ; // Get the number of wildcard matches if ( ! $ blnOrSearch && $ intWildcards ) { $ strQuery .= ", (SELECT COUNT(*) FROM tl_search_index WHERE (" . implode ( ' OR ' , array_fill ( 0 , $ intWildcards , 'word LIKE ?' ) ) . ") AND pid=sid) AS wildcards" ; $ arrValues = array_merge ( $ arrValues , $ arrWildcards ) ; } // Count the number of matches $ strQuery .= ", COUNT(*) AS count" ; // Get the relevance $ strQuery .= ", SUM(relevance) AS relevance" ; // Prepare keywords array $ arrAllKeywords = array ( ) ; // Get keywords if ( ! empty ( $ arrKeywords ) ) { $ arrAllKeywords [ ] = implode ( ' OR ' , array_fill ( 0 , \ count ( $ arrKeywords ) , 'word=?' ) ) ; $ arrValues = array_merge ( $ arrValues , $ arrKeywords ) ; $ intKeywords += \ count ( $ arrKeywords ) ; } // Get included keywords if ( $ intIncluded ) { $ arrAllKeywords [ ] = implode ( ' OR ' , array_fill ( 0 , $ intIncluded , 'word=?' ) ) ; $ arrValues = array_merge ( $ arrValues , $ arrIncluded ) ; $ intKeywords += $ intIncluded ; } // Get keywords from phrases if ( $ intPhrases ) { foreach ( $ arrPhrases as $ strPhrase ) { $ arrWords = self :: splitIntoWords ( str_replace ( '[^[:alnum:]]+' , ' ' , $ strPhrase ) , $ GLOBALS [ 'TL_LANGUAGE' ] ) ; $ arrAllKeywords [ ] = implode ( ' OR ' , array_fill ( 0 , \ count ( $ arrWords ) , 'word=?' ) ) ; $ arrValues = array_merge ( $ arrValues , $ arrWords ) ; $ intKeywords += \ count ( $ arrWords ) ; } } // Get wildcards if ( $ intWildcards ) { $ arrAllKeywords [ ] = implode ( ' OR ' , array_fill ( 0 , $ intWildcards , 'word LIKE ?' ) ) ; $ arrValues = array_merge ( $ arrValues , $ arrWildcards ) ; } $ strQuery .= " FROM tl_search_index WHERE (" . implode ( ' OR ' , $ arrAllKeywords ) . ")" ; // Get phrases if ( $ intPhrases ) { $ strQuery .= " AND (" . implode ( ( $ blnOrSearch ? ' OR ' : ' AND ' ) , array_fill ( 0 , $ intPhrases , 'tl_search_index.pid IN(SELECT id FROM tl_search WHERE text REGEXP ?)' ) ) . ")" ; $ arrValues = array_merge ( $ arrValues , $ arrPhrases ) ; } // Include keywords if ( $ intIncluded ) { $ strQuery .= " AND tl_search_index.pid IN(SELECT pid FROM tl_search_index WHERE " . implode ( ' OR ' , array_fill ( 0 , $ intIncluded , 'word=?' ) ) . ")" ; $ arrValues = array_merge ( $ arrValues , $ arrIncluded ) ; } // Exclude keywords if ( $ intExcluded ) { $ strQuery .= " AND tl_search_index.pid NOT IN(SELECT pid FROM tl_search_index WHERE " . implode ( ' OR ' , array_fill ( 0 , $ intExcluded , 'word=?' ) ) . ")" ; $ arrValues = array_merge ( $ arrValues , $ arrExcluded ) ; } // Limit results to a particular set of pages if ( ! empty ( $ arrPid ) && \ is_array ( $ arrPid ) ) { $ strQuery .= " AND tl_search_index.pid IN(SELECT id FROM tl_search WHERE pid IN(" . implode ( ',' , array_map ( '\intval' , $ arrPid ) ) . "))" ; } $ strQuery .= " GROUP BY tl_search_index.pid" ; // Sort by relevance $ strQuery .= " ORDER BY relevance DESC) matches LEFT JOIN tl_search ON(matches.sid=tl_search.id)" ; // Make sure to find all words if ( ! $ blnOrSearch ) { // Number of keywords without wildcards $ strQuery .= " WHERE matches.count >= " . $ intKeywords ; // Dynamically add the number of wildcard matches if ( $ intWildcards ) { $ strQuery .= " + IF(matches.wildcards>" . $ intWildcards . ", matches.wildcards, " . $ intWildcards . ")" ; } } // Return result $ objResultStmt = Database :: getInstance ( ) -> prepare ( $ strQuery ) ; if ( $ intRows > 0 ) { $ objResultStmt -> limit ( $ intRows , $ intOffset ) ; } return $ objResultStmt -> execute ( $ arrValues ) ; }
Search the index and return the result object

@param string  $strKeywords The keyword string
@param boolean $blnOrSearch If true, the result can contain any keyword
@param array   $arrPid      An optional array of page IDs to limit the result to
@param integer $intRows     An optional maximum number of result rows
@param integer $intOffset   An optional result offset
@param boolean $blnFuzzy    If true, the search will be fuzzy

@return Result The database result object

@throws \Exception If the cleaned keyword string is empty
Search the index and return the result object