Documentation TYPO3 par Ameos |
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 ?>