Main Page | Directories | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages | Examples

class.tslib_search.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 1999-2004 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 = '1';
00094    var $operator_translate_table = Array (               // case-sensitiv. Defineres the words, which will be operators between words
00095       Array ('+' , 'AND'),
00096       Array ('-' , 'AND NOT'),
00097          // english
00098       Array ('AND' , 'AND'),
00099       Array ('OR' , 'OR'),
00100       Array ('NOT' , 'AND NOT'),
00101          // danish
00102       Array ('OG' , 'AND'),
00103       Array ('ELLER' , 'OR'),
00104       Array ('UDEN' , 'AND NOT')
00105    );
00106 
00107    // Internal
00108    var $sword_array;    // Contains the search-words and operators
00109    var $queryParts;     // Contains the query parts after processing.
00110 
00111    var $other_where_clauses;  // Addition to the whereclause. This could be used to limit search to a certain page or alike in the system.
00112    var $fTable;      // This is set with the foreign table that 'pages' are connected to.
00113 
00114    var $res_offset = 0; // How many rows to offset from the beginning
00115    var $res_shows = 20; // How many results to show (0 = no limit)
00116    var $res_count;         // Intern: How many results, there was last time (with the exact same searchstring.
00117 
00118    var $pageIdList='';     // List of pageIds.
00119 
00120    var $listOfSearchFields ='';
00121 
00130    function register_tables_and_columns($requestedCols,$allowedCols) {
00131       $rCols=$this->explodeCols($requestedCols);
00132       $aCols=$this->explodeCols($allowedCols);
00133 
00134       foreach ($rCols as $k => $v)  {
00135          $rCols[$k]=trim($v);
00136          if (in_array($rCols[$k], $aCols))   {
00137             $parts = explode('.',$rCols[$k]);
00138             $this->tables[$parts[0]]['searchfields'][] = $parts[1];
00139          }
00140       }
00141       $this->tables['pages']['primary_key'] = 'uid';
00142       $this->tables['pages']['resultfields'][] = 'uid';
00143       unset($this->tables['pages']['fkey']);
00144 
00145       foreach ($aCols as $k => $v)  {
00146          $aCols[$k]=trim($v);
00147          $parts = explode('.',$aCols[$k]);
00148          $this->tables[$parts[0]]['resultfields'][] = $parts[1].' AS '.str_replace('.','_',$aCols[$k]);
00149          $this->tables[$parts[0]]['fkey']='pid';
00150       }
00151 
00152       $this->fTable='';
00153       foreach ($this->tables as $t => $v) {
00154          if ($t!='pages')  {
00155             if (!$this->fTable)  {
00156                $this->fTable = $t;
00157             } else {
00158                unset($this->tables[$t]);
00159             }
00160          }
00161       }
00162    }
00163 
00171    function explodeCols($in)  {
00172       $theArray = explode(':',$in);
00173       $out = Array();
00174       while(list(,$val)=each($theArray))  {
00175          $val=trim($val);
00176          $parts = explode('.',$val);
00177          if ($parts[0] && $parts[1])   {
00178             $subparts = explode('-',$parts[1]);
00179             while(list(,$piece)=each($subparts))   {
00180                $piece=trim($piece);
00181                if ($piece)    $out[]=$parts[0].'.'.$piece;
00182             }
00183          }
00184       }
00185       return $out;
00186    }
00187 
00196    function register_and_explode_search_string($sword)   {
00197       $sword = trim($sword);
00198       if ($sword) {
00199          $components = $this->split($sword);
00200          $s_sword = '';  // the searchword is stored here during the loop
00201          if (is_array($components)) {
00202             $i=0;
00203             $lastoper = '';
00204             reset($components);
00205             while (list($key,$val) = each ($components)) {
00206                $operator=$this->get_operator($val);
00207                if ($operator) {
00208                   $lastoper = $operator;
00209                } elseif (strlen($val)>1) {      // A searchword MUST be at least two characters long!
00210                   $this->sword_array[$i]['sword'] = $val;
00211                   $this->sword_array[$i]['oper'] = ($lastoper) ? $lastoper : $this->default_operator;
00212                   $lastoper = '';
00213                   $i++;
00214                }
00215             }
00216          }
00217       }
00218    }
00219 
00229    function split($origSword, $specchars='+-', $delchars='+.,-')  {
00230       $sword = $origSword;
00231       $specs = '['.$this->quotemeta($specchars).']';
00232       $delchars = '['.$this->quotemeta($delchars).']';
00233 
00234          // As long as $sword is true (that means $sword MUST be reduced little by little until its empty inside the loop!)
00235       while ($sword) {
00236          if (ereg('^"',$sword))  {     // There was a double-quote and we will then look for the ending quote.
00237             $sword = ereg_replace('^"','',$sword);    // Removes first double-quote
00238             ereg('^[^"]*',$sword,$reg);  // Removes everything till next double-quote
00239             $value[] = $reg[0];  // reg[0] is the value, should not be trimmed
00240             $sword = ereg_replace('^'.$this->quotemeta($reg[0]),'',$sword);
00241             $sword = trim(ereg_replace('^"','',$sword));    // Removes last double-quote
00242          } elseif (ereg('^'.$specs,$sword,$reg)) {
00243             $value[] = $reg[0];
00244             $sword = trim(ereg_replace('^'.$specs,'',$sword));    // Removes = sign
00245          } elseif (ereg('[\+\-]',$sword)) {  // Check if $sword contains + or -
00246                // + and - shall only be interpreted as $specchars when there's whitespace before it
00247                // otherwise it's included in the searchword (e.g. "know-how")
00248             $a_sword = explode(' ',$sword);  // explode $sword to single words
00249             $word = array_shift($a_sword);   // get first word
00250             $word = ereg_replace($delchars.'$','',$word);      // Delete $delchars at end of string
00251             $value[] = $word; // add searchword to values
00252             $sword = implode(' ',$a_sword);  // re-build $sword
00253          } else {
00254                // There are no double-quotes around the value. Looking for next (space) or special char.
00255             ereg('^[^ '.$this->quotemeta($specchars).']*',$sword,$reg);
00256             $word = ereg_replace($delchars.'$','',trim($reg[0]));    // Delete $delchars at end of string
00257             $value[] = $word;
00258             $sword = trim(ereg_replace('^'.$this->quotemeta($reg[0]),'',$sword));
00259          }
00260       }
00261 
00262       return $value;
00263    }
00264 
00272    function quotemeta($str)   {
00273       $str = str_replace('|','\|',quotemeta($str));
00274       #$str = str_replace('-','\-',$str);    // Breaks "-" which should NOT have a slash before it inside of [ ] in a regex.
00275       return $str;
00276    }
00277 
00288    function build_search_query($endClause) {
00289 
00290       if (is_array($this->tables))  {
00291          $tables = $this->tables;
00292          $primary_table = '';
00293 
00294             // Primary key table is found.
00295          foreach($tables as $key => $val) {
00296             if ($tables[$key]['primary_key'])   {$primary_table = $key;}
00297          }
00298 
00299          if ($primary_table) {
00300 
00301                // Initialize query parts:
00302             $this->queryParts = array(
00303                'SELECT' => '',
00304                'FROM' => '',
00305                'WHERE' => '',
00306                'GROUPBY' => '',
00307                'ORDERBY' => '',
00308                'LIMIT' => '',
00309             );
00310 
00311                // Find tables / field names to select:
00312             $fieldArray = array();
00313             $tableArray = array();
00314             foreach($tables as $key => $val) {
00315                $tableArray[] = $key;
00316                $resultfields = $tables[$key]['resultfields'];
00317                if (is_array($resultfields))  {
00318                   foreach($resultfields as $key2 => $val2)  {
00319                      $fieldArray[] = $key.'.'.$val2;
00320                   }
00321                }
00322             }
00323             $this->queryParts['SELECT'] = implode(',',$fieldArray);
00324             $this->queryParts['FROM'] = implode(',',$tableArray);
00325 
00326                // Set join WHERE parts:
00327             $whereArray = array();
00328 
00329             $primary_table_and_key = $primary_table.'.'.$tables[$primary_table]['primary_key'];
00330             $primKeys = Array();
00331             foreach($tables as $key => $val) {
00332                $fkey = $tables[$key]['fkey'];
00333                if ($fkey)  {
00334                    $primKeys[] = $key.'.'.$fkey.'='.$primary_table_and_key;
00335                }
00336             }
00337             if (count($primKeys))   {
00338                $whereArray[] = '('.implode(' OR ',$primKeys).')';
00339             }
00340 
00341                // Additional where clause:
00342             if (trim($endClause))   {
00343                $whereArray[] = trim($endClause);
00344             }
00345 
00346                // Add search word where clause:
00347             $query_part = $this->build_search_query_for_searchwords();
00348             if (!$query_part) {
00349                $query_part = '(0!=0)';
00350             }
00351             $whereArray[] = '('.$query_part.')';
00352 
00353                // Implode where clauses:
00354             $this->queryParts['WHERE'] = implode(' AND ',$whereArray);
00355 
00356                // Group by settings:
00357             if ($this->group_by) {
00358                if ($this->group_by == 'PRIMARY_KEY')  {
00359                   $this->queryParts['GROUPBY'] = $primary_table_and_key;
00360                } else {
00361                   $this->queryParts['GROUPBY'] = $this->group_by;
00362                }
00363             }
00364          }
00365       }
00366    }
00367 
00374    function build_search_query_for_searchwords()   {
00375 
00376       if (is_array($this->sword_array))   {
00377          $main_query_part = array();
00378 
00379          foreach($this->sword_array as $key => $val)  {
00380             $s_sword = $this->sword_array[$key]['sword'];
00381 
00382                // Get subQueryPart
00383             $sub_query_part = array();
00384 
00385             $this->listOfSearchFields='';
00386             foreach($this->tables as $key3 => $val3)  {
00387                $searchfields = $this->tables[$key3]['searchfields'];
00388                if (is_array($searchfields))  {
00389                   foreach($searchfields as $key2 => $val2)  {
00390                      $this->listOfSearchFields.= $key3.'.'.$val2.',';
00391                      $sub_query_part[] = $key3.'.'.$val2.' LIKE "%'.$GLOBALS['TYPO3_DB']->quoteStr($s_sword, $key3).'%"';
00392                   }
00393                }
00394             }
00395 
00396             if (count($sub_query_part))   {
00397                $main_query_part[] = $this->sword_array[$key]['oper'];
00398                $main_query_part[] = '('.implode(' OR ',$sub_query_part).')';
00399             }
00400          }
00401 
00402          if (count($main_query_part))  {
00403             unset($main_query_part[0]);   // Remove first part anyways.
00404             return implode(' ',$main_query_part);
00405          }
00406       }
00407    }
00408 
00416    function get_operator($operator) {
00417       $operator = trim($operator);
00418       $op_array = $this->operator_translate_table;
00419       reset ($op_array);
00420       if ($this->operator_translate_table_caseinsensitive)  {
00421          $operator = strtoupper($operator);
00422       }
00423       while (list($key,$val) = each($op_array)) {
00424          $item = $op_array[$key][0];
00425          if ($this->operator_translate_table_caseinsensitive)  {
00426             $item = strtoupper($item);
00427          }
00428          if ($operator==$item)   {
00429             return $op_array[$key][1];
00430          }
00431       }
00432    }
00433 
00439    function count_query() {
00440       if (is_array($this->queryParts)) {
00441          $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($this->queryParts['SELECT'], $this->queryParts['FROM'], $this->queryParts['WHERE'], $this->queryParts['GROUPBY']);
00442           $this->res_count = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
00443          return TRUE;
00444       }
00445    }
00446 
00452    function execute_query() {
00453       if (is_array($this->queryParts)) {
00454            $this->result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($this->queryParts);
00455          return TRUE;
00456       }
00457    }
00458 
00465    function get_searchwords() {
00466       $SWORD_PARAMS='';
00467       if (is_array($this->sword_array))   {
00468          foreach($this->sword_array as $key => $val)  {
00469             $SWORD_PARAMS.='&sword_list[]='.rawurlencode($val['sword']);
00470          }
00471       }
00472       return $SWORD_PARAMS;
00473    }
00474 
00480    function get_searchwordsArray()  {
00481       if (is_array($this->sword_array))   {
00482          foreach($this->sword_array as $key => $val)  {
00483             $swords[]=$val['sword'];
00484          }
00485       }
00486       return $swords;
00487    }
00488 }
00489 
00490 
00491 
00492 
00493 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_search.php'])  {
00494    include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_search.php']);
00495 }
00496 
00497 ?>

Generated on Sun Oct 3 01:05:59 2004 for TYPO3core 3.7.0 dev by  doxygen 1.3.8-20040913