mode = $mode; } /** * @param mixed $mode */ public function setMode($mode) { $this->mode = $mode; } /** * @return mixed */ public function getMode() { return $this->mode; } /** * @param string $text * @param string $language * Eg LANG_SQL_LIKE, LANG_SQL_FTS, LANG_SOLR. * @throws CRM_Core_Exception * @return string */ public function format($text, $language) { $text = trim($text); switch ($language) { case self::LANG_SOLR: case self::LANG_SQL_FTS: $text = $this->_formatFts($text, $this->mode); break; case self::LANG_SQL_FTSBOOL: $text = $this->_formatFtsBool($text, $this->mode); break; case self::LANG_SQL_LIKE: $text = $this->_formatLike($text, $this->mode); break; default: $text = NULL; } if ($text === NULL) { throw new CRM_Core_Exception("Unrecognized combination: language=[{$language}] mode=[{$this->mode}]"); } return $text; } protected function _formatFts($text, $mode) { $result = NULL; // normalize user-inputted wildcards $text = str_replace('%', '*', $text); if (empty($text)) { $result = '*'; } elseif (strpos($text, '*') !== FALSE) { // if user supplies their own wildcards, then don't do any sophisticated changes $result = $text; } else { switch ($mode) { case self::MODE_NONE: $result = $text; break; case self::MODE_PHRASE: $result = '"' . $text . '"'; break; case self::MODE_WILDPHRASE: $result = '"*' . $text . '*"'; break; case self::MODE_WILDWORDS: $result = $this->mapWords($text, '*word*'); break; case self::MODE_WILDWORDS_SUFFIX: $result = $this->mapWords($text, 'word*'); break; default: $result = NULL; } } return $this->dedupeWildcards($result, '%'); } protected function _formatFtsBool($text, $mode) { $result = NULL; // normalize user-inputted wildcards $text = str_replace('%', '*', $text); if (empty($text)) { $result = '*'; } elseif (strpos($text, '+') !== FALSE || strpos($text, '-') !== FALSE) { // if user supplies their own include/exclude operators, use text as is (with trailing wildcard) $result = $this->mapWords($text, 'word*'); } elseif (strpos($text, '*') !== FALSE) { // if user supplies their own wildcards, then don't do any sophisticated changes $result = $this->mapWords($text, '+word'); } elseif (preg_match('/^(["\']).*\1$/m', $text)) { // if surrounded by quotes, use term as is $result = $text; } else { switch ($mode) { case self::MODE_NONE: $result = $this->mapWords($text, '+word'); break; case self::MODE_PHRASE: $result = '+"' . $text . '"'; break; case self::MODE_WILDPHRASE: $result = '+"*' . $text . '*"'; break; case self::MODE_WILDWORDS: $result = $this->mapWords($text, '+*word*'); break; case self::MODE_WILDWORDS_SUFFIX: $result = $this->mapWords($text, '+word*'); break; default: $result = NULL; } } return $this->dedupeWildcards($result, '%'); } protected function _formatLike($text, $mode) { $result = NULL; if (empty($text)) { $result = '%'; } elseif (strpos($text, '%') !== FALSE) { // if user supplies their own wildcards, then don't do any sophisticated changes $result = $text; } else { switch ($mode) { case self::MODE_NONE: case self::MODE_PHRASE: case self::MODE_WILDPHRASE: $result = "%" . $text . "%"; break; case self::MODE_WILDWORDS: case self::MODE_WILDWORDS_SUFFIX: $result = "%" . preg_replace('/[ \r\n]+/', '%', $text) . '%'; break; default: $result = NULL; } } return $this->dedupeWildcards($result, '%'); } /** * @param string $text * User-supplied query string. * @param string $template * A prototypical description of each word, eg "word%" or "word*" or "*word*". * @return string */ protected function mapWords($text, $template) { $result = array(); foreach ($this->parseWords($text) as $word) { $result[] = str_replace('word', $word, $template); } return implode(' ', $result); } /** * @param $text * @return array */ protected function parseWords($text) { return explode(' ', preg_replace('/[ \r\n\t]+/', ' ', trim($text))); } /** * @param $text * @param $wildcard * @return mixed */ protected function dedupeWildcards($text, $wildcard) { if ($text === NULL) { return NULL; } // don't use preg_replace because $wildcard might be special char while (strpos($text, "{$wildcard}{$wildcard}") !== FALSE) { $text = str_replace("{$wildcard}{$wildcard}", "{$wildcard}", $text); } return $text; } public static function getModes() { return array( self::MODE_NONE, self::MODE_PHRASE, self::MODE_WILDPHRASE, self::MODE_WILDWORDS, self::MODE_WILDWORDS_SUFFIX, ); } public static function getLanguages() { return array( self::LANG_SOLR, self::LANG_SQL_FTS, self::LANG_SQL_FTSBOOL, self::LANG_SQL_LIKE, ); } /** * @param $text * * Ex: drush eval 'civicrm_initialize(); CRM_Utils_QueryFormatter::dumpExampleTable("firstword secondword");' */ public static function dumpExampleTable($text) { $width = strlen($text) + 8; $buf = ''; $buf .= sprintf("%-{$width}s", 'mode'); foreach (self::getLanguages() as $lang) { $buf .= sprintf("%-{$width}s", $lang); } $buf .= "\n"; foreach (self::getModes() as $mode) { $formatter = new CRM_Utils_QueryFormatter($mode); $buf .= sprintf("%-{$width}s", $mode); foreach (self::getLanguages() as $lang) { $buf .= sprintf("%-{$width}s", $formatter->format($text, $lang)); } $buf .= "\n"; } echo $buf; } }