Project

General

Profile

Bug #21432 » dbal_12440.diff

Administrator Admin, 2009-11-02 23:46

View differences:

class.ux_t3lib_sqlparser.php (working copy)
*/
class ux_t3lib_sqlparser extends t3lib_sqlparser {
// START: Methods originally belonging to t3lib_sqlparser but copied and fixed here as they were not used in Core
/**
* Parsing the tablenames in the "FROM [$parseString] WHERE" part of a query into an array.
* The success of this parsing determines if that part of the query is supported by TYPO3.
*
* @param string list of tables, eg. "pages, tt_content" or "pages A, pages B". NOTICE: passed by reference!
* @param string Regular expressing to STOP parsing, eg. '^(WHERE)([[:space:]]*)'
* @return array If successful parsing, returns an array, otherwise an error string.
* @see compileFromTables()
*/
public function parseFromTables(&$parseString, $stopRegex = '') {
// Prepare variables:
$parseString = $this->trimSQL($parseString);
$this->lastStopKeyWord = '';
$this->parse_error = '';
$stack = array(); // Contains the parsed content
$pnt = 0; // Pointer to positions in $stack
$loopExit = 0; // Recursivity brake.
// $parseString is continously shortend by the process and we keep parsing it till it is zero:
while (strlen($parseString)) {
// Looking for the table:
if ($stack[$pnt]['table'] = $this->nextPart($parseString,'^([[:alnum:]_]+)(,|[[:space:]]+)')) {
// Looking for stop-keywords before fetching potential table alias:
if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
$this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"), '', $this->lastStopKeyWord));
return $stack;
}
if (!preg_match('/^(LEFT|JOIN)[[:space:]]+/i',$parseString)) {
$stack[$pnt]['as_keyword'] = $this->nextPart($parseString,'^(AS[[:space:]]+)');
$stack[$pnt]['as'] = $this->nextPart($parseString,'^([[:alnum:]_]+)[[:space:]]*');
}
} else return $this->parseError('No table name found as expected in parseFromTables()!', $parseString);
// Looking for JOIN
$joinCnt = 0;
while ($join = $this->nextPart($parseString,'^(LEFT[[:space:]]+JOIN|LEFT[[:space:]]+OUTER[[:space:]]+JOIN|INNER[[:space:]]+JOIN|JOIN)[[:space:]]+')) {
$stack[$pnt]['JOIN'][$joinCnt]['type'] = $join;
if ($stack[$pnt]['JOIN'][$joinCnt]['withTable'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+', 1)) {
if (!preg_match('/^ON[[:space:]]+/i', $parseString)) {
$stack[$pnt]['JOIN'][$joinCnt]['as_keyword'] = $this->nextPart($parseString, '^(AS[[:space:]]+)');
$stack[$pnt]['JOIN'][$joinCnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
}
if (!$this->nextPart($parseString, '^(ON[[:space:]]+)')) {
return $this->parseError('No join condition found in parseFromTables()!', $parseString);
}
$field1 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]*=[[:space:]]*', 1);
$field2 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]+');
if ($field1 && $field2) {
// Explode fields into field and table:
$tableField = explode('.', $field1, 2);
$field1 = array();
if (count($tableField) != 2) {
$field1['table'] = '';
$field1['field'] = $tableField[0];
} else {
$field1['table'] = $tableField[0];
$field1['field'] = $tableField[1];
}
$tableField = explode('.', $field2, 2);
$field2 = array();
if (count($tableField) != 2) {
$field2['table'] = '';
$field2['field'] = $tableField[0];
} else {
$field2['table'] = $tableField[0];
$field2['field'] = $tableField[1];
}
$stack[$pnt]['JOIN'][$joinCnt]['ON'] = array($field1, $field2);
$joinCnt++;
} else return $this->parseError('No join fields found in parseFromTables()!', $parseString);
} else return $this->parseError('No join table found in parseFromTables()!', $parseString);
}
// Looking for stop-keywords:
if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex)) {
$this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"), '', $this->lastStopKeyWord));
return $stack;
}
// Looking for comma:
if (strlen($parseString) && !$this->nextPart($parseString, '^(,)')) {
return $this->parseError('No comma found as expected in parseFromTables()', $parseString);
}
// Increasing pointer:
$pnt++;
// Check recursivity brake:
$loopExit++;
if ($loopExit > 500) {
return $this->parseError('More than 500 loops, exiting prematurely in parseFromTables()...', $parseString);
}
}
// Return result array:
return $stack;
}
/**
* Parsing the WHERE clause fields in the "WHERE [$parseString] ..." part of a query into a multidimensional array.
* The success of this parsing determines if that part of the query is supported by TYPO3.
*
* @param string WHERE clause to parse. NOTICE: passed by reference!
* @param string Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
* @return mixed If successful parsing, returns an array, otherwise an error string.
*/
public function parseWhereClause(&$parseString, $stopRegex = '') {
// Prepare variables:
$parseString = $this->trimSQL($parseString);
$this->lastStopKeyWord = '';
$this->parse_error = '';
$stack = array(0 => array()); // Contains the parsed content
$pnt = array(0 => 0); // Pointer to positions in $stack
$level = 0; // Determines parenthesis level
$loopExit = 0; // Recursivity brake.
// $parseString is continously shortend by the process and we keep parsing it till it is zero:
while (strlen($parseString)) {
// Look for next parenthesis level:
$newLevel = $this->nextPart($parseString,'^([(])');
if ($newLevel == '(') { // If new level is started, manage stack/pointers:
$level++; // Increase level
$pnt[$level] = 0; // Reset pointer for this level
$stack[$level] = array(); // Reset stack for this level
} else { // If no new level is started, just parse the current level:
// Find "modifyer", eg. "NOT or !"
$stack[$level][$pnt[$level]]['modifier'] = trim($this->nextPart($parseString, '^(!|NOT[[:space:]]+)'));
// Support calculated value only for:
// - "&" (boolean AND)
// - "+" (addition)
// - "-" (substraction)
// - "*" (multiplication)
// - "/" (division)
// - "%" (modulo)
$calcOperators = '&|\+|-|\*|\/|%';
// Fieldname:
if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)([[:space:]]+|' . $calcOperators . '|<=|>=|<|>|=|!=|IS)')) {
// Parse field name into field and table:
$tableField = explode('.', $fieldName, 2);
if (count($tableField) == 2) {
$stack[$level][$pnt[$level]]['table'] = $tableField[0];
$stack[$level][$pnt[$level]]['field'] = $tableField[1];
} else {
$stack[$level][$pnt[$level]]['table'] = '';
$stack[$level][$pnt[$level]]['field'] = $tableField[0];
}
} else {
return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
}
// See if the value is calculated:
$stack[$level][$pnt[$level]]['calc'] = $this->nextPart($parseString, '^(' . $calcOperators . ')');
if (strlen($stack[$level][$pnt[$level]]['calc'])) {
// Finding value for calculation:
$stack[$level][$pnt[$level]]['calc_value'] = $this->getValue($parseString);
}
// Find "comparator":
$stack[$level][$pnt[$level]]['comparator'] = $this->nextPart($parseString, '^(<=|>=|<|>|=|!=|NOT[[:space:]]+IN|IN|NOT[[:space:]]+LIKE|LIKE|IS[[:space:]]+NOT|IS)');
if (strlen($stack[$level][$pnt[$level]]['comparator'])) {
// Finding value for comparator:
$stack[$level][$pnt[$level]]['value'] = $this->getValue($parseString, $stack[$level][$pnt[$level]]['comparator']);
if ($this->parse_error) {
return $this->parse_error;
}
}
// Finished, increase pointer:
$pnt[$level]++;
// Checking if the current level is ended, in that case do stack management:
while ($this->nextPart($parseString,'^([)])')) {
$level--; // Decrease level:
$stack[$level][$pnt[$level]]['sub'] = $stack[$level+1]; // Copy stack
$pnt[$level]++; // Increase pointer of the new level
// Make recursivity check:
$loopExit++;
if ($loopExit > 500) {
return $this->parseError('More than 500 loops (in search for exit parenthesis), exiting prematurely in parseWhereClause()...', $parseString);
}
}
// Detecting the operator for the next level:
$op = $this->nextPart($parseString, '^(AND[[:space:]]+NOT|&&[[:space:]]+NOT|OR[[:space:]]+NOT|OR[[:space:]]+NOT|\|\|[[:space:]]+NOT|AND|&&|OR|\|\|)(\(|[[:space:]]+)');
if ($op) {
// Normalize boolean operator
$op = str_replace(array('&&', '||'), array('AND', 'OR'), $op);
$stack[$level][$pnt[$level]]['operator'] = $op;
} elseif (strlen($parseString)) {
// Looking for stop-keywords:
if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex)) {
$this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"), '', $this->lastStopKeyWord));
return $stack[0];
} else {
return $this->parseError('No operator, but parsing not finished in parseWhereClause().', $parseString);
}
}
}
// Make recursivity check:
$loopExit++;
if ($loopExit > 500) {
return $this->parseError('More than 500 loops, exiting prematurely in parseWhereClause()...', $parseString);
}
}
// Return the stacks lowest level:
return $stack[0];
}
/**
* Compiles a "FROM [output] WHERE..:" table list based on input array (made with ->parseFromTables())
*
* @param array Array of table names, (made with ->parseFromTables())
* @return string Table name string
* @see parseFromTables()
*/
public function compileFromTables($tablesArray) {
// Prepare buffer variable:
$outputParts = array();
// Traverse the table names:
if (is_array($tablesArray)) {
foreach ($tablesArray as $k => $v) {
// Set table name:
$outputParts[$k] = $v['table'];
// Add alias AS if there:
if ($v['as']) {
$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
}
if (is_array($v['JOIN'])) {
foreach ($v['JOIN'] as $join) {
$outputParts[$k] .= ' ' . $join['type'] . ' ' . $join['withTable'];
// Add alias AS if there:
if (isset($join['as']) && $join['as']) {
$outputParts[$k] .= ' ' . $join['as_keyword'] . ' ' . $join['as'];
}
$outputParts[$k] .= ' ON ';
$outputParts[$k] .= ($join['ON'][0]['table']) ? $join['ON'][0]['table'] . '.' : '';
$outputParts[$k] .= $join['ON'][0]['field'];
$outputParts[$k] .= '=';
$outputParts[$k] .= ($join['ON'][1]['table']) ? $join['ON'][1]['table'] . '.' : '';
$outputParts[$k] .= $join['ON'][1]['field'];
}
}
}
}
// Return imploded buffer:
return implode(', ', $outputParts);
}
/**
* Implodes an array of WHERE clause configuration into a WHERE clause.
* NOTICE: MIGHT BY A TEMPORARY FUNCTION. Use for debugging only!
* BUT IT IS NEEDED FOR DBAL - MAKE IT PERMANENT?!?!
*
* @param array WHERE clause configuration
* @return string WHERE clause as string.
* @see explodeWhereClause()
*/
protected function nativeCompileWhereClause($clauseArray) {
// Prepare buffer variable:
$output = '';
// Traverse clause array:
if (is_array($clauseArray)) {
foreach ($clauseArray as $k => $v) {
// Set operator:
$output .= $v['operator'] ? ' ' . $v['operator'] : '';
// Look for sublevel:
if (is_array($v['sub'])) {
$output .= ' (' . trim($this->nativeCompileWhereClause($v['sub'])) . ')';
} else {
// Set field/table with modifying prefix if any:
$output .= ' ' . trim($v['modifier'] . ' ' . ($v['table'] ? $v['table'] . '.' : '') . $v['field']);
// Set calculation, if any:
if ($v['calc']) {
$output .= $v['calc'] . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
}
// Set comparator:
if ($v['comparator']) {
$output .= ' ' . $v['comparator'];
// Detecting value type; list or plain:
if (t3lib_div::inList('NOTIN,IN', strtoupper(str_replace(array(' ', "\t", "\r", "\n"), '', $v['comparator'])))) {
$valueBuffer = array();
foreach ($v['value'] as $realValue) {
$valueBuffer[] = $realValue[1] . $this->compileAddslashes($realValue[0]) . $realValue[1];
}
$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
} else {
$output .= ' ' . $v['value'][1] . $this->compileAddslashes($v['value'][0]) . $v['value'][1];
}
}
}
}
}
// Return output buffer:
return $output;
}
// END: Methods originally belonging to t3lib_sqlparser but copied and fixed here as they were not used in Core
/*************************
*
* Compiling queries
......
* @return string WHERE clause as string.
* @see t3lib_sqlparser::parseWhereClause()
*/
function compileWhereClause($clauseArray, $functionMapping = TRUE) {
public function compileWhereClause($clauseArray, $functionMapping = TRUE) {
switch ((string)$GLOBALS['TYPO3_DB']->handlerCfg[$GLOBALS['TYPO3_DB']->lastHandlerKey]['type']) {
case 'native':
$output = parent::compileWhereClause($clauseArray);
$output = $this->nativeCompileWhereClause($clauseArray);
break;
case 'adodb':
// Prepare buffer variable:
(2-2/2)