"TYPO3 4.0.1: typo3_src-4.0.1/typo3/sysext/cms/tslib/class.tslib_search.php Source File", "datetime" => "Sat Dec 2 19:22:30 2006", "date" => "2 Dec 2006", "doxygenversion" => "1.4.6", "projectname" => "TYPO3 4.0.1", "projectnumber" => "4.0.1" ); get_header($doxygen_vars); ?>
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2005 Kasper Skaarhoj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00088 class tslib_search { 00089 var $tables = Array (); 00090 00091 var $group_by = 'PRIMARY_KEY'; // Alternatively 'PRIMARY_KEY'; sorting by primary key 00092 var $default_operator = 'AND'; // Standard SQL-operator between words 00093 var $operator_translate_table_caseinsensitive = TRUE; 00094 var $operator_translate_table = Array ( // case-sensitiv. Defineres the words, which will be operators between words 00095 Array ('+' , 'AND'), 00096 Array ('|' , 'AND'), 00097 Array ('-' , 'AND NOT'), 00098 // english 00099 Array ('and' , 'AND'), 00100 Array ('or' , 'OR'), 00101 Array ('not' , 'AND NOT'), 00102 ); 00103 00104 // Internal 00105 var $sword_array; // Contains the search-words and operators 00106 var $queryParts; // Contains the query parts after processing. 00107 00108 var $other_where_clauses; // Addition to the whereclause. This could be used to limit search to a certain page or alike in the system. 00109 var $fTable; // This is set with the foreign table that 'pages' are connected to. 00110 00111 var $res_offset = 0; // How many rows to offset from the beginning 00112 var $res_shows = 20; // How many results to show (0 = no limit) 00113 var $res_count; // Intern: How many results, there was last time (with the exact same searchstring. 00114 00115 var $pageIdList=''; // List of pageIds. 00116 00117 var $listOfSearchFields =''; 00118 00127 function register_tables_and_columns($requestedCols,$allowedCols) { 00128 $rCols=$this->explodeCols($requestedCols); 00129 $aCols=$this->explodeCols($allowedCols); 00130 00131 foreach ($rCols as $k => $v) { 00132 $rCols[$k]=trim($v); 00133 if (in_array($rCols[$k], $aCols)) { 00134 $parts = explode('.',$rCols[$k]); 00135 $this->tables[$parts[0]]['searchfields'][] = $parts[1]; 00136 } 00137 } 00138 $this->tables['pages']['primary_key'] = 'uid'; 00139 $this->tables['pages']['resultfields'][] = 'uid'; 00140 unset($this->tables['pages']['fkey']); 00141 00142 foreach ($aCols as $k => $v) { 00143 $aCols[$k]=trim($v); 00144 $parts = explode('.',$aCols[$k]); 00145 $this->tables[$parts[0]]['resultfields'][] = $parts[1].' AS '.str_replace('.','_',$aCols[$k]); 00146 $this->tables[$parts[0]]['fkey']='pid'; 00147 } 00148 00149 $this->fTable=''; 00150 foreach ($this->tables as $t => $v) { 00151 if ($t!='pages') { 00152 if (!$this->fTable) { 00153 $this->fTable = $t; 00154 } else { 00155 unset($this->tables[$t]); 00156 } 00157 } 00158 } 00159 } 00160 00168 function explodeCols($in) { 00169 $theArray = explode(':',$in); 00170 $out = Array(); 00171 while(list(,$val)=each($theArray)) { 00172 $val=trim($val); 00173 $parts = explode('.',$val); 00174 if ($parts[0] && $parts[1]) { 00175 $subparts = explode('-',$parts[1]); 00176 while(list(,$piece)=each($subparts)) { 00177 $piece=trim($piece); 00178 if ($piece) $out[]=$parts[0].'.'.$piece; 00179 } 00180 } 00181 } 00182 return $out; 00183 } 00184 00193 function register_and_explode_search_string($sword) { 00194 $sword = trim($sword); 00195 if ($sword) { 00196 $components = $this->split($sword); 00197 $s_sword = ''; // the searchword is stored here during the loop 00198 if (is_array($components)) { 00199 $i=0; 00200 $lastoper = ''; 00201 reset($components); 00202 while (list($key,$val) = each ($components)) { 00203 $operator=$this->get_operator($val); 00204 if ($operator) { 00205 $lastoper = $operator; 00206 } elseif (strlen($val)>1) { // A searchword MUST be at least two characters long! 00207 $this->sword_array[$i]['sword'] = $val; 00208 $this->sword_array[$i]['oper'] = ($lastoper) ? $lastoper : $this->default_operator; 00209 $lastoper = ''; 00210 $i++; 00211 } 00212 } 00213 } 00214 } 00215 } 00216 00226 function split($origSword, $specchars='+-', $delchars='+.,-') { 00227 $sword = $origSword; 00228 $specs = '['.$this->quotemeta($specchars).']'; 00229 $delchars = '['.$this->quotemeta($delchars).']'; 00230 00231 // As long as $sword is true (that means $sword MUST be reduced little by little until its empty inside the loop!) 00232 while ($sword) { 00233 if (ereg('^"',$sword)) { // There was a double-quote and we will then look for the ending quote. 00234 $sword = ereg_replace('^"','',$sword); // Removes first double-quote 00235 ereg('^[^"]*',$sword,$reg); // Removes everything till next double-quote 00236 $value[] = $reg[0]; // reg[0] is the value, should not be trimmed 00237 $sword = ereg_replace('^'.$this->quotemeta($reg[0]),'',$sword); 00238 $sword = trim(ereg_replace('^"','',$sword)); // Removes last double-quote 00239 } elseif (ereg('^'.$specs,$sword,$reg)) { 00240 $value[] = $reg[0]; 00241 $sword = trim(ereg_replace('^'.$specs,'',$sword)); // Removes = sign 00242 } elseif (ereg('[\+\-]',$sword)) { // Check if $sword contains + or - 00243 // + and - shall only be interpreted as $specchars when there's whitespace before it 00244 // otherwise it's included in the searchword (e.g. "know-how") 00245 $a_sword = explode(' ',$sword); // explode $sword to single words 00246 $word = array_shift($a_sword); // get first word 00247 $word = ereg_replace($delchars.'$','',$word); // Delete $delchars at end of string 00248 $value[] = $word; // add searchword to values 00249 $sword = implode(' ',$a_sword); // re-build $sword 00250 } else { 00251 // There are no double-quotes around the value. Looking for next (space) or special char. 00252 ereg('^[^ '.$this->quotemeta($specchars).']*',$sword,$reg); 00253 $word = ereg_replace($delchars.'$','',trim($reg[0])); // Delete $delchars at end of string 00254 $value[] = $word; 00255 $sword = trim(ereg_replace('^'.$this->quotemeta($reg[0]),'',$sword)); 00256 } 00257 } 00258 00259 return $value; 00260 } 00261 00269 function quotemeta($str) { 00270 $str = str_replace('|','\|',quotemeta($str)); 00271 #$str = str_replace('-','\-',$str); // Breaks "-" which should NOT have a slash before it inside of [ ] in a regex. 00272 return $str; 00273 } 00274 00285 function build_search_query($endClause) { 00286 00287 if (is_array($this->tables)) { 00288 $tables = $this->tables; 00289 $primary_table = ''; 00290 00291 // Primary key table is found. 00292 foreach($tables as $key => $val) { 00293 if ($tables[$key]['primary_key']) {$primary_table = $key;} 00294 } 00295 00296 if ($primary_table) { 00297 00298 // Initialize query parts: 00299 $this->queryParts = array( 00300 'SELECT' => '', 00301 'FROM' => '', 00302 'WHERE' => '', 00303 'GROUPBY' => '', 00304 'ORDERBY' => '', 00305 'LIMIT' => '', 00306 ); 00307 00308 // Find tables / field names to select: 00309 $fieldArray = array(); 00310 $tableArray = array(); 00311 foreach($tables as $key => $val) { 00312 $tableArray[] = $key; 00313 $resultfields = $tables[$key]['resultfields']; 00314 if (is_array($resultfields)) { 00315 foreach($resultfields as $key2 => $val2) { 00316 $fieldArray[] = $key.'.'.$val2; 00317 } 00318 } 00319 } 00320 $this->queryParts['SELECT'] = implode(',',$fieldArray); 00321 $this->queryParts['FROM'] = implode(',',$tableArray); 00322 00323 // Set join WHERE parts: 00324 $whereArray = array(); 00325 00326 $primary_table_and_key = $primary_table.'.'.$tables[$primary_table]['primary_key']; 00327 $primKeys = Array(); 00328 foreach($tables as $key => $val) { 00329 $fkey = $tables[$key]['fkey']; 00330 if ($fkey) { 00331 $primKeys[] = $key.'.'.$fkey.'='.$primary_table_and_key; 00332 } 00333 } 00334 if (count($primKeys)) { 00335 $whereArray[] = '('.implode(' OR ',$primKeys).')'; 00336 } 00337 00338 // Additional where clause: 00339 if (trim($endClause)) { 00340 $whereArray[] = trim($endClause); 00341 } 00342 00343 // Add search word where clause: 00344 $query_part = $this->build_search_query_for_searchwords(); 00345 if (!$query_part) { 00346 $query_part = '(0!=0)'; 00347 } 00348 $whereArray[] = '('.$query_part.')'; 00349 00350 // Implode where clauses: 00351 $this->queryParts['WHERE'] = implode(' AND ',$whereArray); 00352 00353 // Group by settings: 00354 if ($this->group_by) { 00355 if ($this->group_by == 'PRIMARY_KEY') { 00356 $this->queryParts['GROUPBY'] = $primary_table_and_key; 00357 } else { 00358 $this->queryParts['GROUPBY'] = $this->group_by; 00359 } 00360 } 00361 } 00362 } 00363 } 00364 00371 function build_search_query_for_searchwords() { 00372 00373 if (is_array($this->sword_array)) { 00374 $main_query_part = array(); 00375 00376 foreach($this->sword_array as $key => $val) { 00377 $s_sword = $this->sword_array[$key]['sword']; 00378 00379 // Get subQueryPart 00380 $sub_query_part = array(); 00381 00382 $this->listOfSearchFields=''; 00383 foreach($this->tables as $key3 => $val3) { 00384 $searchfields = $this->tables[$key3]['searchfields']; 00385 if (is_array($searchfields)) { 00386 foreach($searchfields as $key2 => $val2) { 00387 $this->listOfSearchFields.= $key3.'.'.$val2.','; 00388 $sub_query_part[] = $key3.'.'.$val2.' LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($s_sword, $key3).'%\''; 00389 } 00390 } 00391 } 00392 00393 if (count($sub_query_part)) { 00394 $main_query_part[] = $this->sword_array[$key]['oper']; 00395 $main_query_part[] = '('.implode(' OR ',$sub_query_part).')'; 00396 } 00397 } 00398 00399 if (count($main_query_part)) { 00400 unset($main_query_part[0]); // Remove first part anyways. 00401 return implode(' ',$main_query_part); 00402 } 00403 } 00404 } 00405 00413 function get_operator($operator) { 00414 $operator = trim($operator); 00415 $op_array = $this->operator_translate_table; 00416 reset ($op_array); 00417 if ($this->operator_translate_table_caseinsensitive) { 00418 $operator = strtolower($operator); // case-conversion is charset insensitive, but it doesn't spoil anything if input string AND operator table is already converted 00419 } 00420 while (list($key,$val) = each($op_array)) { 00421 $item = $op_array[$key][0]; 00422 if ($this->operator_translate_table_caseinsensitive) { 00423 $item = strtolower($item); // See note above. 00424 } 00425 if ($operator==$item) { 00426 return $op_array[$key][1]; 00427 } 00428 } 00429 } 00430 00436 function count_query() { 00437 if (is_array($this->queryParts)) { 00438 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($this->queryParts['SELECT'], $this->queryParts['FROM'], $this->queryParts['WHERE'], $this->queryParts['GROUPBY']); 00439 $this->res_count = $GLOBALS['TYPO3_DB']->sql_num_rows($res); 00440 return TRUE; 00441 } 00442 } 00443 00449 function execute_query() { 00450 if (is_array($this->queryParts)) { 00451 $this->result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($this->queryParts); 00452 return TRUE; 00453 } 00454 } 00455 00462 function get_searchwords() { 00463 $SWORD_PARAMS = ''; 00464 if (is_array($this->sword_array)) { 00465 foreach($this->sword_array as $key => $val) { 00466 $SWORD_PARAMS.= '&sword_list[]='.rawurlencode($val['sword']); 00467 } 00468 } 00469 return $SWORD_PARAMS; 00470 } 00471 00477 function get_searchwordsArray() { 00478 if (is_array($this->sword_array)) { 00479 foreach($this->sword_array as $key => $val) { 00480 $swords[] = $val['sword']; 00481 } 00482 } 00483 return $swords; 00484 } 00485 } 00486 00487 00488 00489 00490 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_search.php']) { 00491 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_search.php']); 00492 } 00493 00494 ?>