1
0

vlibSearchToSQL.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2002 Active Fish Group |
  7. // +----------------------------------------------------------------------+
  8. // | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk> |
  9. // +----------------------------------------------------------------------+
  10. //
  11. // $Id: vlibSearchToSQL.php 1438 2006-10-29 13:26:42Z b4rt $
  12. // check to avoid multiple including of class
  13. if (!defined('vlibSearchToSQLClassLoaded')) {
  14. define('vlibSearchToSQLClassLoaded', 1);
  15. include_once (dirname(__FILE__).'/vlibIni.php');
  16. /**
  17. * vlibSearchToSQL is a class used to turn a boolean text
  18. * string, i.e., "+cats -dogs" or "cats NOT dogs".. into a
  19. * fully qualified SQL where clause, i.e.,
  20. * "field1 LIKE '%cats%' AND field1 NOT LIKE '%dogs%'".
  21. *
  22. * @since 13/02/2003
  23. * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
  24. * @package vLIB
  25. * @access public
  26. * @see vlibSearchToSQL.html
  27. */
  28. class vlibSearchToSQL {
  29. /*-------------------------------------------------------------------------------\
  30. | ATTENTION |
  31. | Do not touch the following variables. vlibSearchToSQL will not work otherwise. |
  32. \-------------------------------------------------------------------------------*/
  33. var $OPTIONS = array(
  34. 'MIN_WORD_LENGTH' => '',
  35. 'ALLOW_WILDCARDS' => '',
  36. 'ENCLOSE_FIELDS_WITH' => '',
  37. 'DEFAULT_SEPERATOR' => '',
  38. 'STOP_WORDS' => ''
  39. );
  40. var $stop_words = '';
  41. var $escapechars = "\0\r\n\t\\";
  42. var $words_stopped = array();
  43. var $words_too_short = array();
  44. /**
  45. * FUNCTION: vlibSearchToSQL
  46. *
  47. * vlibSearchToSQL constructor.
  48. *
  49. * @param array $options see above
  50. * @return boolean true/false
  51. * @access private
  52. */
  53. function vlibSearchToSQL ($options=null) {
  54. if (is_array(vlibIni::vlibSearchToSQL())) {
  55. foreach (vlibIni::vlibSearchToSQL() as $name => $val) {
  56. $this->OPTIONS[$name] = $val;
  57. }
  58. }
  59. if (is_array($options)) {
  60. foreach($options as $key => $val) {
  61. if (strtoupper($key) == 'STOP_WORDS') {
  62. $this->OPTIONS['STOP_WORDS'] .= ' '.$val;
  63. }
  64. else {
  65. $this->OPTIONS[strtoupper($key)] = $val;
  66. }
  67. }
  68. }
  69. $this->stop_words = str_replace(' ', '|', $this->OPTIONS['STOP_WORDS']);
  70. if (!$this->OPTIONS['allow_wildcards']) $this->escapechars .= '%_';
  71. return true;
  72. }
  73. /**
  74. * FUNCTION: convert
  75. *
  76. * converts the search string to it's SQL equivilent.
  77. *
  78. * @param array $fields, all the fields in the database that you want to search
  79. * @return boolean true/false
  80. * @access private
  81. */
  82. function convert ($fields, $str) {
  83. $this->reset(); // reset vars before we start, in case a search was done previous to this.
  84. // run regex against search string
  85. $rgx = "/
  86. \s*
  87. (AND|OR|NOT|\+|\-|\|\||\!)? # any inclusion exclusion identifier
  88. \s*
  89. ([\"\']?) # first quote
  90. \s*
  91. ((?<=[\"\']) # if last one was a quote
  92. [^\"\']+ # search for any non quote
  93. | # else
  94. [^\s]+ # search for a word
  95. ) # endif
  96. \s*
  97. ([\"\']?) # second quote
  98. /ix";
  99. preg_match_all($rgx, $str, $matches, PREG_SET_ORDER);
  100. $words_used = array();
  101. // convert matches to readable SQL
  102. $sql = '';
  103. foreach ($matches as $m) {
  104. if (strlen($m[3]) < $this->OPTIONS['MIN_WORD_LENGTH']) { // word to short
  105. array_push($this->words_too_short, $m[3]);
  106. continue;
  107. }
  108. if (in_array(strtolower($m[3]), $words_used)) { // word duplicated
  109. continue;
  110. }
  111. if (preg_match('/^('.$this->stop_words.')$/i', $m[3]) && $m[1] != '+') { // stop word
  112. array_push($this->words_stopped, $m[3]);
  113. continue;
  114. }
  115. $preclause = null;
  116. $clause = null;
  117. switch (strtoupper($m[1])) {
  118. case 'AND':
  119. case '+':
  120. if ($sql) $preclause = ' AND ';
  121. $clause = ' LIKE ';
  122. break;
  123. case 'OR':
  124. case '||':
  125. if ($sql) $preclause = ' OR ';
  126. $clause = ' LIKE ';
  127. break;
  128. case 'NOT':
  129. case '-':
  130. if ($sql) $preclause = ' AND ';
  131. $clause = ' IS NOT LIKE ';
  132. break;
  133. default:
  134. if ($sql) $preclause = ' '.$this->OPTIONS['DEFAULT_SEPERATOR'].' ';
  135. $clause = ' LIKE ';
  136. break;
  137. }
  138. $sql .= $preclause.'$fieldname$'.$clause.'\'%'.addcslashes($m[3], $this->escapechars).'%\'';
  139. $words_used[] = strtolower($m[3]);
  140. }
  141. // loop through Db fields, creating identical SQL for each
  142. $where_clauses = array();
  143. foreach ($fields as $f) {
  144. if ($this->OPTIONS['ENCLOSE_FIELDS_WITH']) {
  145. $f = $this->OPTIONS['ENCLOSE_FIELDS_WITH'] . $f . $this->OPTIONS['ENCLOSE_FIELDS_WITH'];
  146. }
  147. $where_clauses[] = '('.str_replace('$fieldname$', $f, $sql).')';
  148. }
  149. return '('.implode(' OR ', $where_clauses).')';
  150. }
  151. /**
  152. * FUNCTION: getStoppedWords
  153. *
  154. * get an array of the words that were omitted from the search because
  155. * they were in the stop words list.
  156. *
  157. * @return array
  158. * @access public
  159. */
  160. function getStoppedWords () {
  161. return $this->words_stopped;
  162. }
  163. /**
  164. * FUNCTION: getShortWords
  165. *
  166. * get an array of the words that were omitted from the search because
  167. * they were too short.
  168. *
  169. * @return array
  170. * @access public
  171. */
  172. function getShortWords () {
  173. return $this->words_too_short;
  174. }
  175. /**
  176. * FUNCTION: reset
  177. *
  178. * reset any vars before a convert.
  179. *
  180. * @return true
  181. * @access public
  182. */
  183. function reset () {
  184. $this->words_stopped = array();
  185. $this->words_too_short = array();
  186. }
  187. } // end of class
  188. }// end of if (defined)
  189. ?>