chocolatdemariage/www/tools/parser_sql/processors/SelectExpressionProcessor.php
Michael RICOIS f46be40623 Init
2017-06-07 16:31:24 +02:00

170 lines
6.2 KiB
PHP

<?php
/**
* SelectExpressionProcessor.php
*
* This file implements the processor for SELECT expressions.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
require_once(dirname(__FILE__) . '/AbstractProcessor.php');
require_once(dirname(__FILE__) . '/ExpressionListProcessor.php');
require_once(dirname(__FILE__) . '/../utils/ExpressionType.php');
/**
*
* This class processes the SELECT expressions.
*
* @author arothe
*
*/
class SelectExpressionProcessor extends AbstractProcessor {
private $expressionListProcessor;
public function __construct() {
$this->expressionListProcessor = new ExpressionListProcessor();
}
/**
* This fuction processes each SELECT clause.
* We determine what (if any) alias
* is provided, and we set the type of expression.
*/
public function process($expression) {
$tokens = $this->splitSQLIntoTokens($expression);
$token_count = count($tokens);
if ($token_count === 0) {
return null;
}
/*
* Determine if there is an explicit alias after the AS clause.
* If AS is found, then the next non-whitespace token is captured as the alias.
* The tokens after (and including) the AS are removed.
*/
$base_expr = "";
$stripped = array();
$capture = false;
$alias = false;
$processed = false;
for ($i = 0; $i < $token_count; ++$i) {
$token = $tokens[$i];
$upper = strtoupper($token);
if ($upper === 'AS') {
$alias = array('as' => true, "name" => "", "base_expr" => $token);
$tokens[$i] = "";
$capture = true;
continue;
}
if (!$this->isWhitespaceToken($upper)) {
$stripped[] = $token;
}
// we have an explicit AS, next one can be the alias
// but also a comment!
if ($capture) {
if (!$this->isWhitespaceToken($upper) && !$this->isCommentToken($upper)) {
$alias['name'] .= $token;
array_pop($stripped);
}
$alias['base_expr'] .= $token;
$tokens[$i] = "";
continue;
}
$base_expr .= $token;
}
$stripped = $this->expressionListProcessor->process($stripped);
// TODO: the last part can also be a comment, don't use array_pop
// we remove the last token, if it is a colref,
// it can be an alias without an AS
$last = array_pop($stripped);
if (!$alias && $this->isColumnReference($last)) {
// TODO: it can be a comment, don't use array_pop
// check the token before the colref
$prev = array_pop($stripped);
if ($this->isReserved($prev) || $this->isConstant($prev) || $this->isAggregateFunction($prev)
|| $this->isFunction($prev) || $this->isExpression($prev) || $this->isSubQuery($prev)
|| $this->isColumnReference($prev) || $this->isBracketExpression($prev)) {
$alias = array('as' => false, 'name' => trim($last['base_expr']),
'no_quotes' => $this->revokeQuotation($last['base_expr']),
'base_expr' => trim($last['base_expr']));
// remove the last token
array_pop($tokens);
$base_expr = join("", $tokens);
}
}
if (!$alias) {
$base_expr = join("", $tokens);
} else {
/* remove escape from the alias */
$alias['no_quotes'] = $this->revokeQuotation($alias['name']);
$alias['name'] = trim($alias['name']);
$alias['base_expr'] = trim($alias['base_expr']);
}
// TODO: this is always done with $stripped, how we do it twice?
$processed = $this->expressionListProcessor->process($tokens);
// if there is only one part, we copy the expr_type
// in all other cases we use "expression" as global type
$type = ExpressionType::EXPRESSION;
$no_quotes = $this->revokeQuotation(trim($base_expr));
if (count($processed) === 1) {
if (!$this->isSubQuery($processed[0])) {
$type = $processed[0]['expr_type'];
$base_expr = $processed[0]['base_expr'];
$no_quotes = isset($processed[0]['no_quotes']) ? $processed[0]['no_quotes'] : null;
$processed = $processed[0]['sub_tree']; // it can be FALSE
}
}
$result = array();
$result['expr_type'] = $type;
$result['alias'] = $alias;
$result['base_expr'] = trim($base_expr);
if (!empty($no_quotes)) {
$result['no_quotes'] = $no_quotes;
}
$result['sub_tree'] = $processed;
return $result;
}
}
?>