Documentation TYPO3 par Ameos

class.t3lib_sqlparser.php

00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2004-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 ***************************************************************/
00107 class t3lib_sqlparser {
00108 
00109                 // Parser:
00110         var $parse_error = '';                                          // Parsing error string
00111         var $lastStopKeyWord = '';                                      // Last stop keyword used.
00112 
00113 
00114 
00115 
00116         /*************************************
00117          *
00118          * SQL Parsing, full queries
00119          *
00120          **************************************/
00121 
00129         function parseSQL($parseString) {
00130                         // Prepare variables:
00131                 $parseString = $this->trimSQL($parseString);
00132                 $this->parse_error = '';
00133                 $result = array();
00134 
00135                         // Finding starting keyword of string:
00136                 $_parseString = $parseString;   // Protecting original string...
00137                 $keyword = $this->nextPart($_parseString, '^(SELECT|UPDATE|INSERT[[:space:]]+INTO|DELETE[[:space:]]+FROM|EXPLAIN|DROP[[:space:]]+TABLE|CREATE[[:space:]]+TABLE|CREATE[[:space:]]+DATABASE|ALTER[[:space:]]+TABLE)[[:space:]]+');
00138                 $keyword = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$keyword));
00139 
00140                 switch($keyword)        {
00141                         case 'SELECT':
00142                                         // Parsing SELECT query:
00143                                 $result = $this->parseSELECT($parseString);
00144                         break;
00145                         case 'UPDATE':
00146                                         // Parsing UPDATE query:
00147                                 $result = $this->parseUPDATE($parseString);
00148                         break;
00149                         case 'INSERTINTO':
00150                                         // Parsing INSERT query:
00151                                 $result = $this->parseINSERT($parseString);
00152                         break;
00153                         case 'DELETEFROM':
00154                                         // Parsing DELETE query:
00155                                 $result = $this->parseDELETE($parseString);
00156                         break;
00157                         case 'EXPLAIN':
00158                                         // Parsing EXPLAIN SELECT query:
00159                                 $result = $this->parseEXPLAIN($parseString);
00160                         break;
00161                         case 'DROPTABLE':
00162                                         // Parsing DROP TABLE query:
00163                                 $result = $this->parseDROPTABLE($parseString);
00164                         break;
00165                         case 'ALTERTABLE':
00166                                         // Parsing ALTER TABLE query:
00167                                 $result = $this->parseALTERTABLE($parseString);
00168                         break;
00169                         case 'CREATETABLE':
00170                                         // Parsing CREATE TABLE query:
00171                                 $result = $this->parseCREATETABLE($parseString);
00172                         break;
00173                         case 'CREATEDATABASE':
00174                                         // Parsing CREATE DATABASE query:
00175                                 $result = $this->parseCREATEDATABASE($parseString);
00176                         break;
00177                         default:
00178                                 return $this->parseError('"'.$keyword.'" is not a keyword',$parseString);
00179                         break;
00180                 }
00181 
00182                 return $result;
00183         }
00184 
00192         function parseSELECT($parseString)      {
00193 
00194                         // Removing SELECT:
00195                 $parseString = $this->trimSQL($parseString);
00196                 $parseString = ltrim(substr($parseString,6)); // REMOVE eregi_replace('^SELECT[[:space:]]+','',$parseString);
00197 
00198                         // Init output variable:
00199                 $result = array();
00200                 $result['type'] = 'SELECT';
00201 
00202                         // Looking for STRAIGHT_JOIN keyword:
00203                 $result['STRAIGHT_JOIN'] = $this->nextPart($parseString, '^(STRAIGHT_JOIN)[[:space:]]+');
00204 
00205                         // Select fields:
00206                 $result['SELECT'] = $this->parseFieldList($parseString, '^(FROM)[[:space:]]+');
00207                 if ($this->parse_error) { return $this->parse_error; }
00208 
00209                         // Continue if string is not ended:
00210                 if ($parseString)       {
00211 
00212                                 // Get table list:
00213                         $result['FROM'] = $this->parseFromTables($parseString, '^(WHERE)[[:space:]]+');
00214                         if ($this->parse_error) { return $this->parse_error; }
00215 
00216                                 // If there are more than just the tables (a WHERE clause that would be...)
00217                         if ($parseString)       {
00218 
00219                                         // Get WHERE clause:
00220                                 $result['WHERE'] = $this->parseWhereClause($parseString, '^(GROUP[[:space:]]+BY|ORDER[[:space:]]+BY|LIMIT)[[:space:]]+');
00221                                 if ($this->parse_error) { return $this->parse_error; }
00222 
00223                                         // If the WHERE clause parsing was stopped by GROUP BY, ORDER BY or LIMIT, then proceed with parsing:
00224                                 if ($this->lastStopKeyWord)     {
00225 
00226                                                 // GROUP BY parsing:
00227                                         if ($this->lastStopKeyWord == 'GROUPBY')        {
00228                                                 $result['GROUPBY'] = $this->parseFieldList($parseString, '^(ORDER[[:space:]]+BY|LIMIT)[[:space:]]+');
00229                                                 if ($this->parse_error) { return $this->parse_error; }
00230                                         }
00231 
00232                                                 // ORDER BY parsing:
00233                                         if ($this->lastStopKeyWord == 'ORDERBY')        {
00234                                                 $result['ORDERBY'] = $this->parseFieldList($parseString, '^(LIMIT)[[:space:]]+');
00235                                                 if ($this->parse_error) { return $this->parse_error; }
00236                                         }
00237 
00238                                                 // LIMIT parsing:
00239                                         if ($this->lastStopKeyWord == 'LIMIT')  {
00240                                                 if (preg_match('/^([0-9]+|[0-9]+[[:space:]]*,[[:space:]]*[0-9]+)$/',trim($parseString)))        {
00241                                                         $result['LIMIT'] = $parseString;
00242                                                 } else {
00243                                                         return $this->parseError('No value for limit!',$parseString);
00244                                                 }
00245                                         }
00246                                 }
00247                         }
00248                 } else return $this->parseError('No table to select from!',$parseString);
00249 
00250                         // Return result:
00251                 return $result;
00252         }
00253 
00261         function parseUPDATE($parseString)      {
00262 
00263                         // Removing UPDATE
00264                 $parseString = $this->trimSQL($parseString);
00265                 $parseString = ltrim(substr($parseString,6)); // REMOVE eregi_replace('^UPDATE[[:space:]]+','',$parseString);
00266 
00267                         // Init output variable:
00268                 $result = array();
00269                 $result['type'] = 'UPDATE';
00270 
00271                         // Get table:
00272                 $result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
00273 
00274                         // Continue if string is not ended:
00275                 if ($result['TABLE'])   {
00276                         if ($parseString && $this->nextPart($parseString, '^(SET)[[:space:]]+'))        {
00277 
00278                                 $comma = TRUE;
00279 
00280                                         // Get field/value pairs:
00281                                 while($comma)   {
00282                                         if ($fieldName = $this->nextPart($parseString,'^([[:alnum:]_]+)[[:space:]]*=')) {
00283                                                 $this->nextPart($parseString,'^(=)');   // Strip of "=" sign.
00284                                                 $value = $this->getValue($parseString);
00285                                                 $result['FIELDS'][$fieldName] = $value;
00286                                         } else return $this->parseError('No fieldname found',$parseString);
00287 
00288                                         $comma = $this->nextPart($parseString,'^(,)');
00289                                 }
00290 
00291                                         // WHERE
00292                                 if ($this->nextPart($parseString,'^(WHERE)'))   {
00293                                         $result['WHERE'] = $this->parseWhereClause($parseString);
00294                                         if ($this->parse_error) { return $this->parse_error; }
00295                                 }
00296                         } else return $this->parseError('Query missing SET...',$parseString);
00297                 } else return $this->parseError('No table found!',$parseString);
00298 
00299                         // Should be no more content now:
00300                 if ($parseString)       {
00301                         return $this->parseError('Still content in clause after parsing!',$parseString);
00302                 }
00303 
00304                         // Return result:
00305                 return $result;
00306         }
00307 
00315         function parseINSERT($parseString)      {
00316 
00317                         // Removing INSERT
00318                 $parseString = $this->trimSQL($parseString);
00319                 $parseString = ltrim(substr(ltrim(substr($parseString,6)),4)); // REMOVE eregi_replace('^INSERT[[:space:]]+INTO[[:space:]]+','',$parseString);
00320 
00321                         // Init output variable:
00322                 $result = array();
00323                 $result['type'] = 'INSERT';
00324 
00325                         // Get table:
00326                 $result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\()');
00327 
00328                 if ($result['TABLE'])   {
00329 
00330                         if ($this->nextPart($parseString,'^(VALUES)[[:space:]]+'))      {       // In this case there are no field names mentioned in the SQL!
00331                                         // Get values/fieldnames (depending...)
00332                                 $result['VALUES_ONLY'] = $this->getValue($parseString,'IN');
00333                                 if ($this->parse_error) { return $this->parse_error; }
00334                         } else {        // There are apparently fieldnames listed:
00335                                 $fieldNames = $this->getValue($parseString,'_LIST');
00336                                 if ($this->parse_error) { return $this->parse_error; }
00337 
00338                                 if ($this->nextPart($parseString,'^(VALUES)[[:space:]]+'))      {       // "VALUES" keyword binds the fieldnames to values:
00339 
00340                                         $values = $this->getValue($parseString,'IN');   // Using the "getValue" function to get the field list...
00341                                         if ($this->parse_error) { return $this->parse_error; }
00342 
00343                                         foreach($fieldNames as $k => $fN)       {
00344                                                 if (preg_match('/^[[:alnum:]_]+$/',$fN))        {
00345                                                         if (isset($values[$k])) {
00346                                                                 if (!isset($result['FIELDS'][$fN]))     {
00347                                                                         $result['FIELDS'][$fN] = $values[$k];
00348                                                                 } else return $this->parseError('Fieldname ("'.$fN.'") already found in list!',$parseString);
00349                                                         } else return $this->parseError('No value set!',$parseString);
00350                                                 } else return $this->parseError('Invalid fieldname ("'.$fN.'")',$parseString);
00351                                         }
00352                                         if (isset($values[$k+1]))       {
00353                                                 return $this->parseError('Too many values in list!',$parseString);
00354                                         }
00355                                 } else return $this->parseError('VALUES keyword expected',$parseString);
00356                         }
00357                 }  else return $this->parseError('No table found!',$parseString);
00358 
00359                         // Should be no more content now:
00360                 if ($parseString)       {
00361                         return $this->parseError('Still content after parsing!',$parseString);
00362                 }
00363 
00364                         // Return result
00365                 return $result;
00366         }
00367 
00375         function parseDELETE($parseString)      {
00376 
00377                         // Removing DELETE
00378                 $parseString = $this->trimSQL($parseString);
00379                 $parseString = ltrim(substr(ltrim(substr($parseString,6)),4)); // REMOVE eregi_replace('^DELETE[[:space:]]+FROM[[:space:]]+','',$parseString);
00380 
00381                         // Init output variable:
00382                 $result = array();
00383                 $result['type'] = 'DELETE';
00384 
00385                         // Get table:
00386                 $result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
00387 
00388                 if ($result['TABLE'])   {
00389 
00390                                 // WHERE
00391                         if ($this->nextPart($parseString,'^(WHERE)'))   {
00392                                 $result['WHERE'] = $this->parseWhereClause($parseString);
00393                                 if ($this->parse_error) { return $this->parse_error; }
00394                         }
00395                 } else return $this->parseError('No table found!',$parseString);
00396 
00397                         // Should be no more content now:
00398                 if ($parseString)       {
00399                         return $this->parseError('Still content in clause after parsing!',$parseString);
00400                 }
00401 
00402                         // Return result:
00403                 return $result;
00404         }
00405 
00413         function parseEXPLAIN($parseString)     {
00414 
00415                         // Removing EXPLAIN
00416                 $parseString = $this->trimSQL($parseString);
00417                 $parseString = ltrim(substr($parseString,6)); // REMOVE eregi_replace('^EXPLAIN[[:space:]]+','',$parseString);
00418 
00419                         // Init output variable:
00420                 $result = $this->parseSELECT($parseString);
00421                 if (is_array($result))  {
00422                         $result['type'] = 'EXPLAIN';
00423                 }
00424 
00425                 return $result;
00426         }
00427 
00435         function parseCREATETABLE($parseString) {
00436 
00437                         // Removing CREATE TABLE
00438                 $parseString = $this->trimSQL($parseString);
00439                 $parseString = ltrim(substr(ltrim(substr($parseString,6)),5)); // REMOVE eregi_replace('^CREATE[[:space:]]+TABLE[[:space:]]+','',$parseString);
00440 
00441                         // Init output variable:
00442                 $result = array();
00443                 $result['type'] = 'CREATETABLE';
00444 
00445                         // Get table:
00446                 $result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*\(',TRUE);
00447 
00448                 if ($result['TABLE'])   {
00449 
00450                                 // While the parseString is not yet empty:
00451                         while(strlen($parseString)>0)   {
00452                                 if ($key = $this->nextPart($parseString, '^(KEY|PRIMARY KEY)([[:space:]]+|\()'))        {       // Getting key
00453                                         $key = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$key));
00454 
00455                                         switch($key)    {
00456                                                 case 'PRIMARYKEY':
00457                                                         $result['KEYS'][$key] = $this->getValue($parseString,'_LIST');
00458                                                         if ($this->parse_error) { return $this->parse_error; }
00459                                                 break;
00460                                                 case 'KEY':
00461                                                         if ($keyName = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\()'))      {
00462                                                                 $result['KEYS'][$keyName] = $this->getValue($parseString,'_LIST');
00463                                                                 if ($this->parse_error) { return $this->parse_error; }
00464                                                         } else return $this->parseError('No keyname found',$parseString);
00465                                                 break;
00466                                         }
00467                                 } elseif ($fieldName = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+'))   {       // Getting field:
00468                                         $result['FIELDS'][$fieldName]['definition'] = $this->parseFieldDef($parseString);
00469                                         if ($this->parse_error) { return $this->parse_error; }
00470                                 }
00471 
00472                                         // Finding delimiter:
00473                                 $delim = $this->nextPart($parseString, '^(,|\))');
00474                                 if (!$delim)    {
00475                                         return $this->parseError('No delimiter found',$parseString);
00476                                 } elseif ($delim==')')  {
00477                                         break;
00478                                 }
00479                         }
00480 
00481                                 // Finding what is after the table definition - table type in MySQL
00482                         if ($delim==')')        {
00483                                 if ($this->nextPart($parseString, '^(TYPE[[:space:]]*=)'))      {
00484                                         $result['tableType'] = $parseString;
00485                                         $parseString = '';
00486                                 }
00487                         } else return $this->parseError('No fieldname found!',$parseString);
00488 
00489                                 // Getting table type
00490                 } else return $this->parseError('No table found!',$parseString);
00491 
00492                         // Should be no more content now:
00493                 if ($parseString)       {
00494                         return $this->parseError('Still content in clause after parsing!',$parseString);
00495                 }
00496 
00497                 return $result;
00498         }
00499 
00507         function parseALTERTABLE($parseString)  {
00508 
00509                         // Removing ALTER TABLE
00510                 $parseString = $this->trimSQL($parseString);
00511                 $parseString = ltrim(substr(ltrim(substr($parseString,5)),5)); // REMOVE eregi_replace('^ALTER[[:space:]]+TABLE[[:space:]]+','',$parseString);
00512 
00513                         // Init output variable:
00514                 $result = array();
00515                 $result['type'] = 'ALTERTABLE';
00516 
00517                         // Get table:
00518                 $result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
00519 
00520                 if ($result['TABLE'])   {
00521                         if ($result['action'] = $this->nextPart($parseString, '^(CHANGE|DROP[[:space:]]+KEY|DROP[[:space:]]+PRIMARY[[:space:]]+KEY|ADD[[:space:]]+KEY|ADD[[:space:]]+PRIMARY[[:space:]]+KEY|DROP|ADD|RENAME)([[:space:]]+|\()'))        {
00522                                 $actionKey = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$result['action']));
00523 
00524                                         // Getting field:
00525                                 if (t3lib_div::inList('ADDPRIMARYKEY,DROPPRIMARYKEY',$actionKey) || $fieldKey = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+'))  {
00526 
00527                                         switch($actionKey)      {
00528                                                 case 'ADD':
00529                                                         $result['FIELD'] = $fieldKey;
00530                                                         $result['definition'] = $this->parseFieldDef($parseString);
00531                                                         if ($this->parse_error) { return $this->parse_error; }
00532                                                 break;
00533                                                 case 'DROP':
00534                                                 case 'RENAME':
00535                                                         $result['FIELD'] = $fieldKey;
00536                                                 break;
00537                                                 case 'CHANGE':
00538                                                         $result['FIELD'] = $fieldKey;
00539                                                         if ($result['newField'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+'))        {
00540                                                                 $result['definition'] = $this->parseFieldDef($parseString);
00541                                                                 if ($this->parse_error) { return $this->parse_error; }
00542                                                         } else return $this->parseError('No NEW field name found',$parseString);
00543                                                 break;
00544 
00545                                                 case 'ADDKEY':
00546                                                 case 'ADDPRIMARYKEY':
00547                                                         $result['KEY'] = $fieldKey;
00548                                                         $result['fields'] = $this->getValue($parseString,'_LIST');
00549                                                         if ($this->parse_error) { return $this->parse_error; }
00550                                                 break;
00551                                                 case 'DROPKEY':
00552                                                         $result['KEY'] = $fieldKey;
00553                                                 break;
00554                                                 case 'DROPPRIMARYKEY':
00555                                                         // ??? todo!
00556                                                 break;
00557                                         }
00558                                 } else return $this->parseError('No field name found',$parseString);
00559                         } else return $this->parseError('No action CHANGE, DROP or ADD found!',$parseString);
00560                 } else return $this->parseError('No table found!',$parseString);
00561 
00562                         // Should be no more content now:
00563                 if ($parseString)       {
00564                         return $this->parseError('Still content in clause after parsing!',$parseString);
00565                 }
00566 
00567                 return $result;
00568         }
00569 
00576         function parseDROPTABLE($parseString)   {
00577 
00578                         // Removing DROP TABLE
00579                 $parseString = $this->trimSQL($parseString);
00580                 $parseString = ltrim(substr(ltrim(substr($parseString,4)),5)); // eregi_replace('^DROP[[:space:]]+TABLE[[:space:]]+','',$parseString);
00581 
00582                         // Init output variable:
00583                 $result = array();
00584                 $result['type'] = 'DROPTABLE';
00585 
00586                         // IF EXISTS
00587                 $result['ifExists']     = $this->nextPart($parseString, '^(IF[[:space:]]+EXISTS[[:space:]]+)');
00588 
00589                         // Get table:
00590                 $result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
00591 
00592                 if ($result['TABLE'])   {
00593 
00594                                 // Should be no more content now:
00595                         if ($parseString)       {
00596                                 return $this->parseError('Still content in clause after parsing!',$parseString);
00597                         }
00598 
00599                         return $result;
00600                 } else return $this->parseError('No table found!',$parseString);
00601         }
00602 
00609         function parseCREATEDATABASE($parseString)      {
00610 
00611                         // Removing CREATE DATABASE
00612                 $parseString = $this->trimSQL($parseString);
00613                 $parseString = ltrim(substr(ltrim(substr($parseString,6)),8)); // eregi_replace('^CREATE[[:space:]]+DATABASE[[:space:]]+','',$parseString);
00614 
00615                         // Init output variable:
00616                 $result = array();
00617                 $result['type'] = 'CREATEDATABASE';
00618 
00619                         // Get table:
00620                 $result['DATABASE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
00621 
00622                 if ($result['DATABASE'])        {
00623 
00624                                 // Should be no more content now:
00625                         if ($parseString)       {
00626                                 return $this->parseError('Still content in clause after parsing!',$parseString);
00627                         }
00628 
00629                         return $result;
00630                 } else return $this->parseError('No database found!',$parseString);
00631         }
00632 
00633 
00634 
00635 
00636 
00637 
00638 
00639 
00640 
00641 
00642 
00643 
00644 
00645 
00646 
00647         /**************************************
00648          *
00649          * SQL Parsing, helper functions for parts of queries
00650          *
00651          **************************************/
00652 
00663         function parseFieldList(&$parseString, $stopRegex='')   {
00664 
00665                         // Prepare variables:
00666                 $parseString = $this->trimSQL($parseString);
00667                 $this->lastStopKeyWord = '';
00668                 $this->parse_error = '';
00669 
00670 
00671                 $stack = array();       // Contains the parsed content
00672                 $pnt = 0;                       // Pointer to positions in $stack
00673                 $level = 0;                     // Indicates the parenthesis level we are at.
00674                 $loopExit = 0;          // Recursivity brake.
00675 
00676                         // $parseString is continously shortend by the process and we keep parsing it till it is zero:
00677                 while (strlen($parseString)) {
00678 
00679                                 // Checking if we are inside / outside parenthesis (in case of a function like count(), max(), min() etc...):
00680                         if ($level>0)   {       // Inside parenthesis here (does NOT detect if values in quotes are used, the only token is ")" or "("):
00681 
00682                                         // Accumulate function content until next () parenthesis:
00683                                 $funcContent = $this->nextPart($parseString,'^([^()]*.)');
00684                                 $stack[$pnt]['func_content.'][] = array(
00685                                         'level' => $level,
00686                                         'func_content' => substr($funcContent,0,-1)
00687                                 );
00688                                 $stack[$pnt]['func_content'].= $funcContent;
00689 
00690                                         // Detecting ( or )
00691                                 switch(substr($stack[$pnt]['func_content'],-1)) {
00692                                         case '(':
00693                                                 $level++;
00694                                         break;
00695                                         case ')':
00696                                                 $level--;
00697                                                 if (!$level)    {       // If this was the last parenthesis:
00698                                                         $stack[$pnt]['func_content'] = substr($stack[$pnt]['func_content'],0,-1);
00699                                                         $parseString = ltrim($parseString);     // Remove any whitespace after the parenthesis.
00700                                                 }
00701                                         break;
00702                                 }
00703                         } else {        // Outside parenthesis, looking for next field:
00704 
00705                                         // Looking for a known function (only known functions supported)
00706                                 $func = $this->nextPart($parseString,'^(count|max|min|floor|sum|avg)[[:space:]]*\(');
00707                                 if ($func)      {
00708                                         $parseString = trim(substr($parseString,1));    // Strip of "("
00709                                         $stack[$pnt]['type'] = 'function';
00710                                         $stack[$pnt]['function'] = $func;
00711                                         $level++;       // increse parenthesis level counter.
00712                                 } else {
00713                                                 // Otherwise, look for regular fieldname:
00714                                         if ($fieldName = $this->nextPart($parseString,'^([[:alnum:]\*._]+)(,|[[:space:]]+)'))   {
00715                                                 $stack[$pnt]['type'] = 'field';
00716 
00717                                                         // Explode fieldname into field and table:
00718                                                 $tableField = explode('.',$fieldName,2);
00719                                                 if (count($tableField)==2)      {
00720                                                         $stack[$pnt]['table'] = $tableField[0];
00721                                                         $stack[$pnt]['field'] = $tableField[1];
00722                                                 } else {
00723                                                         $stack[$pnt]['table'] = '';
00724                                                         $stack[$pnt]['field'] = $tableField[0];
00725                                                 }
00726                                         } else {
00727                                                 return $this->parseError('No field name found as expected',$parseString);
00728                                         }
00729                                 }
00730                         }
00731 
00732                                 // After a function or field we look for "AS" alias and a comma to separate to the next field in the list:
00733                         if (!$level)    {
00734 
00735                                         // Looking for "AS" alias:
00736                                 if ($as = $this->nextPart($parseString,'^(AS)[[:space:]]+'))    {
00737                                         $stack[$pnt]['as'] = $this->nextPart($parseString,'^([[:alnum:]_]+)(,|[[:space:]]+)');
00738                                         $stack[$pnt]['as_keyword'] = $as;
00739                                 }
00740 
00741                                         // Looking for "ASC" or "DESC" keywords (for ORDER BY)
00742                                 if ($sDir = $this->nextPart($parseString,'^(ASC|DESC)([[:space:]]+|,)'))        {
00743                                         $stack[$pnt]['sortDir'] = $sDir;
00744                                 }
00745 
00746                                         // Looking for stop-keywords:
00747                                 if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))   {
00748                                         $this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$this->lastStopKeyWord));
00749                                         return $stack;
00750                                 }
00751 
00752                                         // Looking for comma (since the stop-keyword did not trigger a return...)
00753                                 if (strlen($parseString) && !$this->nextPart($parseString,'^(,)'))      {
00754                                         return $this->parseError('No comma found as expected',$parseString);
00755                                 }
00756 
00757                                         // Increasing pointer:
00758                                 $pnt++;
00759                         }
00760 
00761                                 // Check recursivity brake:
00762                         $loopExit++;
00763                         if ($loopExit>500)      {
00764                                 return $this->parseError('More than 500 loops, exiting prematurely...',$parseString);
00765                         }
00766                 }
00767 
00768                         // Return result array:
00769                 return $stack;
00770         }
00771 
00781         function parseFromTables(&$parseString, $stopRegex='')  {
00782 
00783                         // Prepare variables:
00784                 $parseString = $this->trimSQL($parseString);
00785                 $this->lastStopKeyWord = '';
00786                 $this->parse_error = '';
00787 
00788                 $stack = array();       // Contains the parsed content
00789                 $pnt = 0;                       // Pointer to positions in $stack
00790                 $loopExit = 0;          // Recursivity brake.
00791 
00792                         // $parseString is continously shortend by the process and we keep parsing it till it is zero:
00793                 while (strlen($parseString)) {
00794                                 // Looking for the table:
00795                         if ($stack[$pnt]['table'] = $this->nextPart($parseString,'^([[:alnum:]_]+)(,|[[:space:]]+)'))   {
00796                             $stack[$pnt]['as'] = $this->nextPart($parseString,'^([[:alnum:]_]+)[[:space:]]*');
00797                         } else return $this->parseError('No table name found as expected!',$parseString);
00798 
00799                                 // Looking for JOIN
00800                         if ($join = $this->nextPart($parseString,'^(JOIN|LEFT[[:space:]]+JOIN)[[:space:]]+'))   {
00801                                 $stack[$pnt]['JOIN']['type'] = $join;
00802                                 if ($stack[$pnt]['JOIN']['withTable'] = $this->nextPart($parseString,'^([[:alnum:]_]+)[[:space:]]+ON[[:space:]]+',1))   {
00803                                         $field1 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]*=[[:space:]]*',1);
00804                                         $field2 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]+');
00805                                         if ($field1 && $field2) {
00806                                                 $stack[$pnt]['JOIN']['ON'] = array($field1,$field2);
00807                                         } else return $this->parseError('No join fields found!',$parseString);
00808                                 } else  return $this->parseError('No join table found!',$parseString);
00809                         }
00810 
00811                                 // Looking for stop-keywords:
00812                         if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))   {
00813                                 $this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$this->lastStopKeyWord));
00814                                 return $stack;
00815                         }
00816 
00817                                 // Looking for comma:
00818                         if (strlen($parseString) && !$this->nextPart($parseString,'^(,)'))      {
00819                                 return $this->parseError('No comma found as expected',$parseString);
00820                         }
00821 
00822                                 // Increasing pointer:
00823                         $pnt++;
00824 
00825                                 // Check recursivity brake:
00826                         $loopExit++;
00827                         if ($loopExit>500)      {
00828                                 return $this->parseError('More than 500 loops, exiting prematurely...',$parseString);
00829                         }
00830                 }
00831 
00832                         // Return result array:
00833                 return $stack;
00834         }
00835 
00844         function parseWhereClause(&$parseString, $stopRegex='') {
00845 
00846                         // Prepare variables:
00847                 $parseString = $this->trimSQL($parseString);
00848                 $this->lastStopKeyWord = '';
00849                 $this->parse_error = '';
00850 
00851                 $stack = array(0 => array());   // Contains the parsed content
00852                 $pnt = array(0 => 0);                   // Pointer to positions in $stack
00853                 $level = 0;                                             // Determines parenthesis level
00854                 $loopExit = 0;                                  // Recursivity brake.
00855 
00856                         // $parseString is continously shortend by the process and we keep parsing it till it is zero:
00857                 while (strlen($parseString)) {
00858 
00859                                 // Look for next parenthesis level:
00860                         $newLevel = $this->nextPart($parseString,'^([(])');
00861                         if ($newLevel=='(')     {                       // If new level is started, manage stack/pointers:
00862                                 $level++;                                       // Increase level
00863                                 $pnt[$level] = 0;                       // Reset pointer for this level
00864                                 $stack[$level] = array();       // Reset stack for this level
00865                         } else {        // If no new level is started, just parse the current level:
00866 
00867                                         // Find "modifyer", eg. "NOT or !"
00868                                 $stack[$level][$pnt[$level]]['modifier'] = trim($this->nextPart($parseString,'^(!|NOT[[:space:]]+)'));
00869 
00870                                         // Fieldname:
00871                                 if ($fieldName = $this->nextPart($parseString,'^([[:alnum:]._]+)([[:space:]]+|&|<=|>=|<|>|=|!=|IS)'))   {
00872 
00873                                                 // Parse field name into field and table:
00874                                         $tableField = explode('.',$fieldName,2);
00875                                         if (count($tableField)==2)      {
00876                                                 $stack[$level][$pnt[$level]]['table'] = $tableField[0];
00877                                                 $stack[$level][$pnt[$level]]['field'] = $tableField[1];
00878                                         } else {
00879                                                 $stack[$level][$pnt[$level]]['table'] = '';
00880                                                 $stack[$level][$pnt[$level]]['field'] = $tableField[0];
00881                                         }
00882                                 } else {
00883                                         return $this->parseError('No field name found as expected',$parseString);
00884                                 }
00885 
00886                                         // See if the value is calculated. Support only for "&" (boolean AND) at the moment:
00887                                 $stack[$level][$pnt[$level]]['calc'] = $this->nextPart($parseString,'^(&)');
00888                                 if (strlen($stack[$level][$pnt[$level]]['calc']))       {
00889                                                 // Finding value for calculation:
00890                                         $stack[$level][$pnt[$level]]['calc_value'] = $this->getValue($parseString);
00891                                 }
00892 
00893                                         // Find "comparator":
00894                                 $stack[$level][$pnt[$level]]['comparator'] = $this->nextPart($parseString,'^(<=|>=|<|>|=|!=|NOT[[:space:]]+IN|IN|NOT[[:space:]]+LIKE|LIKE|IS)');
00895                                 if (strlen($stack[$level][$pnt[$level]]['comparator'])) {
00896                                                 // Finding value for comparator:
00897                                         $stack[$level][$pnt[$level]]['value'] = $this->getValue($parseString,$stack[$level][$pnt[$level]]['comparator']);
00898                                         if ($this->parse_error) { return $this->parse_error; }
00899                                 }
00900 
00901                                         // Finished, increase pointer:
00902                                 $pnt[$level]++;
00903 
00904                                         // Checking if the current level is ended, in that case do stack management:
00905                                 while ($this->nextPart($parseString,'^([)])'))  {
00906                                         $level--;               // Decrease level:
00907                                         $stack[$level][$pnt[$level]]['sub'] = $stack[$level+1];         // Copy stack
00908                                         $pnt[$level]++; // Increase pointer of the new level
00909 
00910                                                 // Make recursivity check:
00911                                         $loopExit++;
00912                                         if ($loopExit>500)      {
00913                                                 return $this->parseError('More than 500 loops (in search for exit parenthesis), exiting prematurely...',$parseString);
00914                                         }
00915                                 }
00916 
00917                                         // Detecting the operator for the next level; support for AND, OR and &&):
00918                                 $op = $this->nextPart($parseString,'^(AND|OR|AND[[:space:]]+NOT)(\(|[[:space:]]+)');
00919                                 if ($op)        {
00920                                         $stack[$level][$pnt[$level]]['operator'] = $op;
00921                                 } elseif (strlen($parseString)) {
00922 
00923                                                 // Looking for stop-keywords:
00924                                         if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))   {
00925                                                 $this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$this->lastStopKeyWord));
00926                                                 return $stack[0];
00927                                         } else {
00928                                                 return $this->parseError('No operator, but parsing not finished.',$parseString);
00929                                         }
00930                                 }
00931                         }
00932 
00933                                 // Make recursivity check:
00934                         $loopExit++;
00935                         if ($loopExit>500)      {
00936                                 return $this->parseError('More than 500 loops, exiting prematurely...',$parseString);
00937                         }
00938                 }
00939 
00940                         // Return the stacks lowest level:
00941                 return $stack[0];
00942         }
00943 
00952         function parseFieldDef(&$parseString, $stopRegex='')    {
00953                         // Prepare variables:
00954                 $parseString = $this->trimSQL($parseString);
00955                 $this->lastStopKeyWord = '';
00956                 $this->parse_error = '';
00957 
00958                 $result = array();
00959 
00960                         // Field type:
00961                 if ($result['fieldType'] =  $this->nextPart($parseString,'^(int|smallint|tinyint|mediumint|bigint|double|numeric|decimal|varchar|char|text|tinytext|mediumtext|longtext|blob|tinyblob|mediumblob|longblob)([[:space:]]+|\()'))  {
00962 
00963                                 // Looking for value:
00964                         if (substr($parseString,0,1)=='(')      {
00965                                 $parseString = substr($parseString,1);
00966                                 if ($result['value'] =  $this->nextPart($parseString,'^([^)]*)'))       {
00967                                         $parseString = ltrim(substr($parseString,1));
00968                                 } else return $this->parseError('No end-parenthesis for value found!',$parseString);
00969                         }
00970 
00971                                 // Looking for keywords
00972                         while($keyword = $this->nextPart($parseString,'^(DEFAULT|NOT[[:space:]]+NULL|AUTO_INCREMENT|UNSIGNED)([[:space:]]+|,|\))'))     {
00973                                 $keywordCmp = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$keyword));
00974 
00975                                 $result['featureIndex'][$keywordCmp]['keyword'] = $keyword;
00976 
00977                                 switch($keywordCmp)     {
00978                                         case 'DEFAULT':
00979                                                 $result['featureIndex'][$keywordCmp]['value'] = $this->getValue($parseString);
00980                                         break;
00981                                 }
00982                         }
00983                 } else return $this->parseError('Field type unknown!',$parseString);
00984 
00985                 return $result;
00986         }
00987 
00988 
00989 
00990 
00991 
00992 
00993 
00994 
00995 
00996 
00997 
00998         /************************************
00999          *
01000          * Parsing: Helper functions
01001          *
01002          ************************************/
01003 
01013         function nextPart(&$parseString,$regex,$trimAll=FALSE)  {
01014                 //if (eregi($regex,$parseString.' ', $reg))     {       // Adding space char because [[:space:]]+ is often a requirement in regex's
01015                 if (preg_match('/'.$regex.'/i',$parseString.' ', $reg)) {       // Adding space char because [[:space:]]+ is often a requirement in regex's
01016                         $parseString = ltrim(substr($parseString,strlen($reg[$trimAll?0:1])));
01017                         return $reg[1];
01018                 }
01019         }
01020 
01028         function getValue(&$parseString,$comparator='') {
01029                 //if (t3lib_div::inList('NOTIN,IN,_LIST',strtoupper(ereg_replace('[[:space:]]','',$comparator))))       {       // List of values:
01030                 if (t3lib_div::inList('NOTIN,IN,_LIST',strtoupper(str_replace(array(' ',"\n","\r","\t"),'',$comparator))))      {       // List of values:
01031                         if ($this->nextPart($parseString,'^([(])'))     {
01032                                 $listValues = array();
01033                                 $comma=',';
01034 
01035                                 while($comma==',')      {
01036                                         $listValues[] = $this->getValue($parseString);
01037                                         $comma = $this->nextPart($parseString,'^([,])');
01038                                 }
01039 
01040                                 $out = $this->nextPart($parseString,'^([)])');
01041                                 if ($out)       {
01042                                         if ($comparator=='_LIST')       {
01043                                                 $kVals = array();
01044                                                 foreach ($listValues as $vArr)  {
01045                                                         $kVals[] = $vArr[0];
01046                                                 }
01047                                                 return $kVals;
01048                                         } else {
01049                                                 return $listValues;
01050                                         }
01051                                 } else return array($this->parseError('No ) parenthesis in list',$parseString));
01052                         } else return array($this->parseError('No ( parenthesis starting the list',$parseString));
01053 
01054                 } else {        // Just plain string value, in quotes or not:
01055 
01056                                 // Quote?
01057                         $firstChar = substr($parseString,0,1);
01058 
01059                         switch($firstChar)      {
01060                                 case '"':
01061                                         return array($this->getValueInQuotes($parseString,'"'),'"');
01062                                 break;
01063                                 case "'":
01064                                         return array($this->getValueInQuotes($parseString,"'"),"'");
01065                                 break;
01066                                 default:
01067                                         if (preg_match('/^([[:alnum:]._-]+)/i',$parseString, $reg))     {
01068                                                 $parseString = ltrim(substr($parseString,strlen($reg[0])));
01069                                                 return array($reg[1]);
01070                                         }
01071                                 break;
01072                         }
01073                 }
01074         }
01075 
01084         function getValueInQuotes(&$parseString,$quote) {
01085 
01086                 $parts = explode($quote,substr($parseString,1));
01087                 $buffer = '';
01088                 foreach($parts as $k => $v)     {
01089                         $buffer.=$v;
01090 
01091                         unset($reg);
01092                         //preg_match('/[\]*$/',$v,$reg); // does not work. what is the *exact* meaning of the next line?
01093                         ereg('[\]*$',$v,$reg);
01094                         if (strlen($reg[0])%2)  {
01095                                 $buffer.=$quote;
01096                         } else {
01097                                 $parseString = ltrim(substr($parseString,strlen($buffer)+2));
01098                                 return $this->parseStripslashes($buffer);
01099                         }
01100                 }
01101         }
01102 
01110         function parseStripslashes($str)        {
01111                 $search = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
01112                 $replace = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
01113 
01114                 return str_replace($search, $replace, $str);
01115         }
01116 
01124         function compileAddslashes($str)        {
01125 return $str;
01126                 $search = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
01127                 $replace = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
01128 
01129                 return str_replace($search, $replace, $str);
01130         }
01131 
01139         function parseError($msg,$restQuery)    {
01140                 $this->parse_error = 'SQL engine parse ERROR: '.$msg.': near "'.substr($restQuery,0,50).'"';
01141                 return $this->parse_error;
01142         }
01143 
01153         function trimSQL($str)  {
01154                 return trim(rtrim($str, "; \r\n\t")).' ';
01155                 //return trim(ereg_replace('[[:space:];]*$','',$str)).' ';
01156         }
01157 
01158 
01159 
01160 
01161 
01162 
01163 
01164 
01165 
01166 
01167 
01168 
01169         /*************************
01170          *
01171          * Compiling queries
01172          *
01173          *************************/
01174 
01182         function compileSQL($components)        {
01183                 switch($components['type'])     {
01184                         case 'SELECT':
01185                                 $query = $this->compileSELECT($components);
01186                         break;
01187                         case 'UPDATE':
01188                                 $query = $this->compileUPDATE($components);
01189                         break;
01190                         case 'INSERT':
01191                                 $query = $this->compileINSERT($components);
01192                         break;
01193                         case 'DELETE':
01194                                 $query = $this->compileDELETE($components);
01195                         break;
01196                         case 'EXPLAIN':
01197                                 $query = 'EXPLAIN '.$this->compileSELECT($components);
01198                         break;
01199                         case 'DROPTABLE':
01200                                 $query = 'DROP TABLE'.($components['ifExists']?' IF EXISTS':'').' '.$components['TABLE'];
01201                         break;
01202                         case 'CREATETABLE':
01203                                 $query = $this->compileCREATETABLE($components);
01204                         break;
01205                         case 'ALTERTABLE':
01206                                 $query = $this->compileALTERTABLE($components);
01207                         break;
01208                 }
01209 
01210                 return $query;
01211         }
01212 
01220         function compileSELECT($components)     {
01221 
01222                         // Initialize:
01223                 $where = $this->compileWhereClause($components['WHERE']);
01224                 $groupBy = $this->compileFieldList($components['GROUPBY']);
01225                 $orderBy = $this->compileFieldList($components['ORDERBY']);
01226                 $limit = $components['LIMIT'];
01227 
01228                         // Make query:
01229                 $query = 'SELECT '.($components['STRAIGHT_JOIN'] ? $components['STRAIGHT_JOIN'].'' : '').'
01230                                 '.$this->compileFieldList($components['SELECT']).'
01231                                 FROM '.$this->compileFromTables($components['FROM']).
01232                                         (strlen($where)?'
01233                                 WHERE '.$where : '').
01234                                         (strlen($groupBy)?'
01235                                 GROUP BY '.$groupBy : '').
01236                                         (strlen($orderBy)?'
01237                                 ORDER BY '.$orderBy : '').
01238                                         (strlen($limit)?'
01239                                 LIMIT '.$limit : '');
01240 
01241                 return $query;
01242         }
01243 
01251         function compileUPDATE($components)     {
01252 
01253                         // Where clause:
01254                 $where = $this->compileWhereClause($components['WHERE']);
01255 
01256                         // Fields
01257                 $fields = array();
01258                 foreach($components['FIELDS'] as $fN => $fV)    {
01259                         $fields[]=$fN.'='.$fV[1].$this->compileAddslashes($fV[0]).$fV[1];
01260                 }
01261 
01262                         // Make query:
01263                 $query = 'UPDATE '.$components['TABLE'].' SET
01264                                 '.implode(',
01265                                 ',$fields).'
01266                                 '.(strlen($where)?'
01267                                 WHERE '.$where : '');
01268 
01269                 return $query;
01270         }
01271 
01279         function compileINSERT($components)     {
01280 
01281                 if ($components['VALUES_ONLY']) {
01282                                 // Initialize:
01283                         $fields = array();
01284                         foreach($components['VALUES_ONLY'] as $fV)      {
01285                                 $fields[]=$fV[1].$this->compileAddslashes($fV[0]).$fV[1];
01286                         }
01287 
01288                                 // Make query:
01289                         $query = 'INSERT INTO '.$components['TABLE'].'
01290                                         VALUES
01291                                         ('.implode(',
01292                                         ',$fields).')';
01293                 } else {
01294                                 // Initialize:
01295                         $fields = array();
01296                         foreach($components['FIELDS'] as $fN => $fV)    {
01297                                 $fields[$fN]=$fV[1].$this->compileAddslashes($fV[0]).$fV[1];
01298                         }
01299 
01300                                 // Make query:
01301                         $query = 'INSERT INTO '.$components['TABLE'].'
01302                                         ('.implode(',
01303                                         ',array_keys($fields)).')
01304                                         VALUES
01305                                         ('.implode(',
01306                                         ',$fields).')';
01307                 }
01308 
01309                 return $query;
01310         }
01311 
01319         function compileDELETE($components)     {
01320 
01321                         // Where clause:
01322                 $where = $this->compileWhereClause($components['WHERE']);
01323 
01324                         // Make query:
01325                 $query = 'DELETE FROM '.$components['TABLE'].
01326                                 (strlen($where)?'
01327                                 WHERE '.$where : '');
01328 
01329                 return $query;
01330         }
01331 
01339         function compileCREATETABLE($components)        {
01340 
01341                         // Create fields and keys:
01342                 $fieldsKeys = array();
01343                 foreach($components['FIELDS'] as $fN => $fCfg)  {
01344                         $fieldsKeys[]=$fN.' '.$this->compileFieldCfg($fCfg['definition']);
01345                 }
01346                 foreach($components['KEYS'] as $kN => $kCfg)    {
01347                         if ($kN == 'PRIMARYKEY')        {
01348                                 $fieldsKeys[]='PRIMARY KEY ('.implode(',', $kCfg).')';
01349                         } else {
01350                                 $fieldsKeys[]='KEY '.$kN.' ('.implode(',', $kCfg).')';
01351                         }
01352                 }
01353 
01354                         // Make query:
01355                 $query = 'CREATE TABLE '.$components['TABLE'].' (
01356                         '.implode(',
01357                         ', $fieldsKeys).'
01358                         )'.($components['tableType'] ? ' TYPE='.$components['tableType'] : '');
01359 
01360                 return $query;
01361         }
01362 
01370         function compileALTERTABLE($components) {
01371 
01372                         // Make query:
01373                 $query = 'ALTER TABLE '.$components['TABLE'].' '.$components['action'].' '.($components['FIELD']?$components['FIELD']:$components['KEY']);
01374 
01375                         // Based on action, add the final part:
01376                 switch(strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$components['action'])))     {
01377                         case 'ADD':
01378                                 $query.=' '.$this->compileFieldCfg($components['definition']);
01379                         break;
01380                         case 'CHANGE':
01381                                 $query.=' '.$components['newField'].' '.$this->compileFieldCfg($components['definition']);
01382                         break;
01383                         case 'DROP':
01384                         case 'DROPKEY':
01385                         break;
01386                         case 'ADDKEY':
01387                         case 'ADDPRIMARYKEY':
01388                                 $query.=' ('.implode(',',$components['fields']).')';
01389                         break;
01390                 }
01391 
01392                         // Return query
01393                 return $query;
01394         }
01395 
01396 
01397 
01398 
01399 
01400 
01401 
01402 
01403 
01404 
01405 
01406 
01407 
01408 
01409         /**************************************
01410          *
01411          * Compiling queries, helper functions for parts of queries
01412          *
01413          **************************************/
01414 
01423         function compileFieldList($selectFields)        {
01424 
01425                         // Prepare buffer variable:
01426                 $outputParts = array();
01427 
01428                         // Traverse the selectFields if any:
01429                 if (is_array($selectFields))    {
01430                         foreach($selectFields as $k => $v)      {
01431 
01432                                         // Detecting type:
01433                                 switch($v['type'])      {
01434                                         case 'function':
01435                                                 $outputParts[$k] = $v['function'].'('.$v['func_content'].')';
01436                                         break;
01437                                         case 'field':
01438                                                 $outputParts[$k] = ($v['table']?$v['table'].'.':'').$v['field'];
01439                                         break;
01440                                 }
01441 
01442                                         // Alias:
01443                                 if ($v['as'])   {
01444                                         $outputParts[$k].= ' '.$v['as_keyword'].' '.$v['as'];
01445                                 }
01446 
01447                                         // Specifically for ORDER BY and GROUP BY field lists:
01448                                 if ($v['sortDir'])      {
01449                                         $outputParts[$k].= ' '.$v['sortDir'];
01450                                 }
01451                         }
01452                 }
01453 
01454                         // Return imploded buffer:
01455                 return implode(', ',$outputParts);
01456         }
01457 
01465         function compileFromTables($tablesArray)        {
01466 
01467                         // Prepare buffer variable:
01468                 $outputParts = array();
01469 
01470                         // Traverse the table names:
01471                 if (is_array($tablesArray))     {
01472                         foreach($tablesArray as $k => $v)       {
01473 
01474                                         // Set table name:
01475                                 $outputParts[$k] = $v['table'];
01476 
01477                                         // Add alias AS if there:
01478                                 if ($v['as'])   {
01479                                         $outputParts[$k].= ' '.$v['as_keyword'].' '.$v['as'];
01480                                 }
01481 
01482                                 if (is_array($v['JOIN']))       {
01483                                         $outputParts[$k].= ' '.$v['JOIN']['type'].' '.$v['JOIN']['withTable'].' ON '.implode('=',$v['JOIN']['ON']);
01484                                 }
01485 
01486                         }
01487                 }
01488 
01489                         // Return imploded buffer:
01490                 return implode(', ',$outputParts);
01491         }
01492 
01502         function compileWhereClause($clauseArray)       {
01503 
01504                         // Prepare buffer variable:
01505                 $output='';
01506 
01507                         // Traverse clause array:
01508                 if (is_array($clauseArray))     {
01509                         foreach($clauseArray as $k => $v)       {
01510 
01511                                         // Set operator:
01512                                 $output.=$v['operator'] ? ' '.$v['operator'] : '';
01513 
01514                                         // Look for sublevel:
01515                                 if (is_array($v['sub']))        {
01516                                         $output.=' ('.trim($this->compileWhereClause($v['sub'])).')';
01517                                 } else {
01518 
01519                                                 // Set field/table with modifying prefix if any:
01520                                         $output.=' '.trim($v['modifier'].' '.($v['table']?$v['table'].'.':'').$v['field']);
01521 
01522                                                 // Set calculation, if any:
01523                                         if ($v['calc']) {
01524                                                 $output.=$v['calc'].$v['calc_value'][1].$this->compileAddslashes($v['calc_value'][0]).$v['calc_value'][1];
01525                                         }
01526 
01527                                                 // Set comparator:
01528                                         if ($v['comparator'])   {
01529                                                 $output.=' '.$v['comparator'];
01530 
01531                                                         // Detecting value type; list or plain:
01532                                                 if (t3lib_div::inList('NOTIN,IN',strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$v['comparator']))))       {
01533                                                         $valueBuffer = array();
01534                                                         foreach($v['value'] as $realValue)      {
01535                                                                 $valueBuffer[]=$realValue[1].$this->compileAddslashes($realValue[0]).$realValue[1];
01536                                                         }
01537                                                         $output.=' ('.trim(implode(',',$valueBuffer)).')';
01538                                                 } else {
01539                                                         $output.=' '.$v['value'][1].$this->compileAddslashes($v['value'][0]).$v['value'][1];
01540                                                 }
01541                                         }
01542                                 }
01543                         }
01544                 }
01545 
01546                         // Return output buffer:
01547                 return $output;
01548         }
01549 
01556         function compileFieldCfg($fieldCfg)     {
01557 
01558                         // Set type:
01559                 $cfg = $fieldCfg['fieldType'];
01560 
01561                         // Add value, if any:
01562                 if (strlen($fieldCfg['value'])) {
01563                         $cfg.='('.$fieldCfg['value'].')';
01564                 }
01565 
01566                         // Add additional features:
01567                 if (is_array($fieldCfg['featureIndex']))        {
01568                         foreach($fieldCfg['featureIndex'] as $featureDef)       {
01569                                 $cfg.=' '.$featureDef['keyword'];
01570 
01571                                         // Add value if found:
01572                                 if (is_array($featureDef['value']))     {
01573                                         $cfg.=' '.$featureDef['value'][1].$this->compileAddslashes($featureDef['value'][0]).$featureDef['value'][1];
01574                                 }
01575                         }
01576                 }
01577 
01578                         // Return field definition string:
01579                 return $cfg;
01580         }
01581 
01582 
01583 
01584 
01585 
01586 
01587 
01588 
01589 
01590 
01591 
01592         /*************************
01593          *
01594          * Debugging
01595          *
01596          *************************/
01597 
01605         function debug_parseSQLpart($part,$str) {
01606                 switch($part)   {
01607                         case 'SELECT':
01608                                 return $this->debug_parseSQLpartCompare($str,$this->compileFieldList($this->parseFieldList($str)));
01609                         break;
01610                         case 'FROM':
01611                                 return $this->debug_parseSQLpartCompare($str,$this->compileFromTables($this->parseFromTables($str)));
01612                         break;
01613                         case 'WHERE':
01614                                 return $this->debug_parseSQLpartCompare($str,$this->compileWhereClause($this->parseWhereClause($str)));
01615                         break;
01616                 }
01617         }
01618 
01627         function debug_parseSQLpartCompare($str,$newStr,$caseInsensitive=FALSE) {
01628                 if ($caseInsensitive)   {
01629                         $str1 = strtoupper($str);
01630                         $str2 = strtoupper($newStr);
01631                 } else {
01632                         $str1 = $str;
01633                         $str2 = $newStr;
01634                 }
01635 
01636                         // Fixing escaped chars:
01637                 $search = array('\0', '\n', '\r', '\Z');
01638                 $replace = array("\x00", "\x0a", "\x0d", "\x1a");
01639                 $str1 = str_replace($search, $replace, $str1);
01640                 $str2 = str_replace($search, $replace, $str2);
01641 
01642                         # Normally, commented out since they are needed only in tricky cases...
01643 #               $str1 = stripslashes($str1);
01644 #               $str2 = stripslashes($str2);
01645 
01646                 if (strcmp(str_replace(array(' ',"\t","\r","\n"),'',$this->trimSQL($str1)),str_replace(array(' ',"\t","\r","\n"),'',$this->trimSQL($str2))))    {
01647                         return array(
01648                                         str_replace(array(' ',"\t","\r","\n"),' ',$str),
01649                                         str_replace(array(' ',"\t","\r","\n"),' ',$newStr),
01650                                 );
01651                 }
01652         }
01653 
01660         function debug_testSQL($SQLquery)       {
01661 #               return $SQLquery;
01662 #debug(array($SQLquery));
01663 
01664                         // Getting result array:
01665                 $parseResult = $this->parseSQL($SQLquery);
01666 
01667                         // If result array was returned, proceed. Otherwise show error and exit.
01668                 if (is_array($parseResult))     {
01669 
01670                                 // Re-compile query:
01671                         $newQuery = $this->compileSQL($parseResult);
01672 
01673                                 // TEST the new query:
01674                         $testResult = $this->debug_parseSQLpartCompare($SQLquery, $newQuery);
01675 
01676                                 // Return new query if OK, otherwise show error and exit:
01677                         if (!is_array($testResult))     {
01678                                 return $newQuery;
01679                         } else {
01680                                 debug(array('ERROR MESSAGE'=>'Input query did not match the parsed and recompiled query exactly (not observing whitespace)', 'TEST result' => $testResult),'SQL parsing failed:');
01681                                 exit;
01682                         }
01683                 } else {
01684                         debug(array('query' => $SQLquery, 'ERROR MESSAGE'=>$parseResult),'SQL parsing failed:');
01685                         exit;
01686                 }
01687         }
01688 }
01689 
01690 
01691 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_sqlparser.php']) {
01692         include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_sqlparser.php']);
01693 }
01694 ?>


Généré par Le spécialiste TYPO3 avec  doxygen 1.4.6