|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2011 Kasper Skårhøj (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 ***************************************************************/ 00027 /** 00028 * Searching in database tables, typ. "pages" and "tt_content" 00029 * Used to generate search queries for TypoScript. 00030 * The class is included from "class.tslib_pagegen.php" based on whether there has been detected content in the GPvar "sword" 00031 * 00032 * $Id: class.tslib_search.php 10120 2011-01-18 20:03:36Z ohader $ 00033 * Revised for TYPO3 3.6 June/2003 by Kasper Skårhøj 00034 * 00035 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00036 * @author René Fritz <r.fritz@colorcube.de> 00037 */ 00038 /** 00039 * [CLASS/FUNCTION INDEX of SCRIPT] 00040 * 00041 * 00042 * 00043 * 88: class tslib_search 00044 * 127: function register_tables_and_columns($requestedCols,$allowedCols) 00045 * 168: function explodeCols($in) 00046 * 193: function register_and_explode_search_string($sword) 00047 * 226: function split($origSword, $specchars='+-', $delchars='+.,-') 00048 * 269: function quotemeta($str) 00049 * 285: function build_search_query($endClause) 00050 * 371: function build_search_query_for_searchwords() 00051 * 413: function get_operator($operator) 00052 * 436: function count_query() 00053 * 449: function execute_query() 00054 * 462: function get_searchwords() 00055 * 477: function get_searchwordsArray() 00056 * 00057 * TOTAL FUNCTIONS: 12 00058 * (This index is automatically created/updated by the extension "extdeveval") 00059 * 00060 */ 00061 00062 00063 00064 00065 00066 00067 00068 00069 00070 00071 00072 00073 00074 00075 00076 00077 00078 00079 00080 /** 00081 * Search class used for the content object SEARCHRESULT 00082 * 00083 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00084 * @package TYPO3 00085 * @subpackage tslib 00086 * @see tslib_cObj::SEARCHRESULT() 00087 */ 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 00119 /** 00120 * Creates the $this->tables-array. 00121 * The 'pages'-table is ALWAYS included as the search is page-based. Apart from this there may be one and only one table, joined with the pages-table. This table is the first table mentioned in the requested-list. If any more tables are set here, they are ignored. 00122 * 00123 * @param string is a list (-) of columns that we want to search. This could be input from the search-form (see TypoScript documentation) 00124 * @param string $allowedCols: is the list of columns, that MAY be searched. All allowed cols are set as result-fields. All requested cols MUST be in the allowed-fields list. 00125 * @return void 00126 */ 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 00161 /** 00162 * Function that can convert the syntax for entering which tables/fields the search should be conducted in. 00163 * 00164 * @param string This is the code-line defining the tables/fields to search. Syntax: '[table1].[field1]-[field2]-[field3] : [table2].[field1]-[field2]' 00165 * @return array An array where the values is "[table].[field]" strings to search 00166 * @see register_tables_and_columns() 00167 */ 00168 function explodeCols($in) { 00169 $theArray = explode(':',$in); 00170 $out = Array(); 00171 foreach ($theArray as $val) { 00172 $val=trim($val); 00173 $parts = explode('.',$val); 00174 if ($parts[0] && $parts[1]) { 00175 $subparts = explode('-',$parts[1]); 00176 foreach ($subparts as $piece) { 00177 $piece=trim($piece); 00178 if ($piece) $out[]=$parts[0].'.'.$piece; 00179 } 00180 } 00181 } 00182 return $out; 00183 } 00184 00185 /** 00186 * Takes a search-string (WITHOUT SLASHES or else it'll be a little sppooky , NOW REMEMBER to unslash!!) 00187 * Sets up $this->sword_array op with operators. 00188 * This function uses $this->operator_translate_table as well as $this->default_operator 00189 * 00190 * @param string The input search-word string. 00191 * @return void 00192 */ 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 foreach ($components as $key => $val) { 00202 $operator=$this->get_operator($val); 00203 if ($operator) { 00204 $lastoper = $operator; 00205 } elseif (strlen($val)>1) { // A searchword MUST be at least two characters long! 00206 $this->sword_array[$i]['sword'] = $val; 00207 $this->sword_array[$i]['oper'] = ($lastoper) ? $lastoper : $this->default_operator; 00208 $lastoper = ''; 00209 $i++; 00210 } 00211 } 00212 } 00213 } 00214 } 00215 00216 /** 00217 * Used to split a search-word line up into elements to search for. This function will detect boolean words like AND and OR, + and -, and even find sentences encapsulated in "" 00218 * This function could be re-written to be more clean and effective - yet it's not that important. 00219 * 00220 * @param string The raw sword string from outside 00221 * @param string Special chars which are used as operators (+- is default) 00222 * @param string Special chars which are deleted if the append the searchword (+-., is default) 00223 * @return mixed Returns an ARRAY if there were search words, othervise the return value may be unset. 00224 */ 00225 function split($origSword, $specchars='+-', $delchars='+.,-') { 00226 $sword = $origSword; 00227 $specs = '['.$this->quotemeta($specchars).']'; 00228 00229 // As long as $sword is true (that means $sword MUST be reduced little by little until its empty inside the loop!) 00230 while ($sword) { 00231 if (preg_match('/^"/',$sword)) { // There was a double-quote and we will then look for the ending quote. 00232 $sword = preg_replace('/^"/','',$sword); // Removes first double-quote 00233 preg_match('/^[^"]*/',$sword,$reg); // Removes everything till next double-quote 00234 $value[] = $reg[0]; // reg[0] is the value, should not be trimmed 00235 $sword = preg_replace('/^'.$this->quotemeta($reg[0]).'/','',$sword); 00236 $sword = trim(preg_replace('/^"/','',$sword)); // Removes last double-quote 00237 } elseif (preg_match('/^'.$specs.'/',$sword,$reg)) { 00238 $value[] = $reg[0]; 00239 $sword = trim(preg_replace('/^'.$specs.'/','',$sword)); // Removes = sign 00240 } elseif (preg_match('/[\+\-]/',$sword)) { // Check if $sword contains + or - 00241 // + and - shall only be interpreted as $specchars when there's whitespace before it 00242 // otherwise it's included in the searchword (e.g. "know-how") 00243 $a_sword = explode(' ',$sword); // explode $sword to single words 00244 $word = array_shift($a_sword); // get first word 00245 $word = rtrim($word, $delchars); // Delete $delchars at end of string 00246 $value[] = $word; // add searchword to values 00247 $sword = implode(' ',$a_sword); // re-build $sword 00248 } else { 00249 // There are no double-quotes around the value. Looking for next (space) or special char. 00250 preg_match('/^[^ '.$this->quotemeta($specchars).']*/',$sword,$reg); 00251 $word = rtrim(trim($reg[0]), $delchars); // Delete $delchars at end of string 00252 $value[] = $word; 00253 $sword = trim(preg_replace('/^'.$this->quotemeta($reg[0]).'/','',$sword)); 00254 } 00255 } 00256 00257 return $value; 00258 } 00259 00260 /** 00261 * Local version of quotemeta. This is the same as the PHP function 00262 * but the vertical line, |, and minus, -, is also escaped with a slash. 00263 * 00264 * @param string String to pass through quotemeta() 00265 * @return string Return value 00266 */ 00267 function quotemeta($str) { 00268 $str = str_replace('|','\|',quotemeta($str)); 00269 #$str = str_replace('-','\-',$str); // Breaks "-" which should NOT have a slash before it inside of [ ] in a regex. 00270 return $str; 00271 } 00272 00273 /** 00274 * This creates the search-query. 00275 * In TypoScript this is used for searching only records not hidden, start/endtimed and fe_grouped! (enable-fields, see tt_content) 00276 * Sets $this->queryParts 00277 * 00278 * @param string $endClause is some extra conditions that the search must match. 00279 * @return boolean Returns true no matter what - sweet isn't it! 00280 * @access private 00281 * @see tslib_cObj::SEARCHRESULT() 00282 */ 00283 function build_search_query($endClause) { 00284 00285 if (is_array($this->tables)) { 00286 $tables = $this->tables; 00287 $primary_table = ''; 00288 00289 // Primary key table is found. 00290 foreach($tables as $key => $val) { 00291 if ($tables[$key]['primary_key']) {$primary_table = $key;} 00292 } 00293 00294 if ($primary_table) { 00295 00296 // Initialize query parts: 00297 $this->queryParts = array( 00298 'SELECT' => '', 00299 'FROM' => '', 00300 'WHERE' => '', 00301 'GROUPBY' => '', 00302 'ORDERBY' => '', 00303 'LIMIT' => '', 00304 ); 00305 00306 // Find tables / field names to select: 00307 $fieldArray = array(); 00308 $tableArray = array(); 00309 foreach($tables as $key => $val) { 00310 $tableArray[] = $key; 00311 $resultfields = $tables[$key]['resultfields']; 00312 if (is_array($resultfields)) { 00313 foreach($resultfields as $key2 => $val2) { 00314 $fieldArray[] = $key.'.'.$val2; 00315 } 00316 } 00317 } 00318 $this->queryParts['SELECT'] = implode(',',$fieldArray); 00319 $this->queryParts['FROM'] = implode(',',$tableArray); 00320 00321 // Set join WHERE parts: 00322 $whereArray = array(); 00323 00324 $primary_table_and_key = $primary_table.'.'.$tables[$primary_table]['primary_key']; 00325 $primKeys = Array(); 00326 foreach($tables as $key => $val) { 00327 $fkey = $tables[$key]['fkey']; 00328 if ($fkey) { 00329 $primKeys[] = $key.'.'.$fkey.'='.$primary_table_and_key; 00330 } 00331 } 00332 if (count($primKeys)) { 00333 $whereArray[] = '('.implode(' OR ',$primKeys).')'; 00334 } 00335 00336 // Additional where clause: 00337 if (trim($endClause)) { 00338 $whereArray[] = trim($endClause); 00339 } 00340 00341 // Add search word where clause: 00342 $query_part = $this->build_search_query_for_searchwords(); 00343 if (!$query_part) { 00344 $query_part = '(0!=0)'; 00345 } 00346 $whereArray[] = '('.$query_part.')'; 00347 00348 // Implode where clauses: 00349 $this->queryParts['WHERE'] = implode(' AND ',$whereArray); 00350 00351 // Group by settings: 00352 if ($this->group_by) { 00353 if ($this->group_by == 'PRIMARY_KEY') { 00354 $this->queryParts['GROUPBY'] = $primary_table_and_key; 00355 } else { 00356 $this->queryParts['GROUPBY'] = $this->group_by; 00357 } 00358 } 00359 } 00360 } 00361 } 00362 00363 /** 00364 * Creates the part of the SQL-sentence, that searches for the search-words ($this->sword_array) 00365 * 00366 * @return string Part of where class limiting result to the those having the search word. 00367 * @access private 00368 */ 00369 function build_search_query_for_searchwords() { 00370 00371 if (is_array($this->sword_array)) { 00372 $main_query_part = array(); 00373 00374 foreach($this->sword_array as $key => $val) { 00375 $s_sword = $this->sword_array[$key]['sword']; 00376 00377 // Get subQueryPart 00378 $sub_query_part = array(); 00379 00380 $this->listOfSearchFields=''; 00381 foreach($this->tables as $key3 => $val3) { 00382 $searchfields = $this->tables[$key3]['searchfields']; 00383 if (is_array($searchfields)) { 00384 foreach($searchfields as $key2 => $val2) { 00385 $this->listOfSearchFields.= $key3.'.'.$val2.','; 00386 $sub_query_part[] = $key3.'.'.$val2.' LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($s_sword, $key3).'%\''; 00387 } 00388 } 00389 } 00390 00391 if (count($sub_query_part)) { 00392 $main_query_part[] = $this->sword_array[$key]['oper']; 00393 $main_query_part[] = '('.implode(' OR ',$sub_query_part).')'; 00394 } 00395 } 00396 00397 if (count($main_query_part)) { 00398 unset($main_query_part[0]); // Remove first part anyways. 00399 return implode(' ',$main_query_part); 00400 } 00401 } 00402 } 00403 00404 /** 00405 * This returns an SQL search-operator (eg. AND, OR, NOT) translated from the current localized set of operators (eg. in danish OG, ELLER, IKKE). 00406 * 00407 * @param string The possible operator to find in the internal operator array. 00408 * @return string If found, the SQL operator for the localized input operator. 00409 * @access private 00410 */ 00411 function get_operator($operator) { 00412 $operator = trim($operator); 00413 $op_array = $this->operator_translate_table; 00414 if ($this->operator_translate_table_caseinsensitive) { 00415 $operator = strtolower($operator); // case-conversion is charset insensitive, but it doesn't spoil anything if input string AND operator table is already converted 00416 } 00417 foreach ($op_array as $key => $val) { 00418 $item = $op_array[$key][0]; 00419 if ($this->operator_translate_table_caseinsensitive) { 00420 $item = strtolower($item); // See note above. 00421 } 00422 if ($operator==$item) { 00423 return $op_array[$key][1]; 00424 } 00425 } 00426 } 00427 00428 /** 00429 * Counts the results and sets the result in $this->res_count 00430 * 00431 * @return boolean True, if $this->query was found 00432 */ 00433 function count_query() { 00434 if (is_array($this->queryParts)) { 00435 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($this->queryParts['SELECT'], $this->queryParts['FROM'], $this->queryParts['WHERE'], $this->queryParts['GROUPBY']); 00436 $this->res_count = $GLOBALS['TYPO3_DB']->sql_num_rows($res); 00437 return TRUE; 00438 } 00439 } 00440 00441 /** 00442 * Executes the search, sets result pointer in $this->result 00443 * 00444 * @return boolean True, if $this->query was set and query performed 00445 */ 00446 function execute_query() { 00447 if (is_array($this->queryParts)) { 00448 $this->result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($this->queryParts); 00449 return TRUE; 00450 } 00451 } 00452 00453 /** 00454 * Returns URL-parameters with the current search words. 00455 * Used when linking to result pages so that search words can be highlighted. 00456 * 00457 * @return string URL-parameters with the searchwords 00458 */ 00459 function get_searchwords() { 00460 $SWORD_PARAMS = ''; 00461 if (is_array($this->sword_array)) { 00462 foreach($this->sword_array as $key => $val) { 00463 $SWORD_PARAMS.= '&sword_list[]='.rawurlencode($val['sword']); 00464 } 00465 } 00466 return $SWORD_PARAMS; 00467 } 00468 00469 /** 00470 * Returns an array with the search words in 00471 * 00472 * @return array IF the internal sword_array contained search words it will return these, otherwise "void" 00473 */ 00474 function get_searchwordsArray() { 00475 if (is_array($this->sword_array)) { 00476 foreach($this->sword_array as $key => $val) { 00477 $swords[] = $val['sword']; 00478 } 00479 } 00480 return $swords; 00481 } 00482 } 00483 00484 00485 00486 00487 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['tslib/class.tslib_search.php'])) { 00488 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['tslib/class.tslib_search.php']); 00489 } 00490 00491 ?>
1.8.0