"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); ?>

class.tslib_search.php

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 ?>