From 5b73f1bd826858dcee43dacdd6bcdc8ee53f7639 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 30 Nov 2011 09:57:54 -0500 Subject: [PATCH] Improved code readability. Improved performance. --- .../ORM/Internal/CommitOrderCalculator.php | 12 +- .../ORM/Internal/Hydration/ObjectHydrator.php | 5 - lib/Doctrine/ORM/PersistentCollection.php | 14 +- lib/Doctrine/ORM/Query/Lexer.php | 96 ++--- lib/Doctrine/ORM/Query/Parser.php | 337 ++++++++++-------- 5 files changed, 250 insertions(+), 214 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php b/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php index 8997b1ea5..aad34e993 100644 --- a/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php +++ b/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php @@ -23,8 +23,9 @@ namespace Doctrine\ORM\Internal; * The CommitOrderCalculator is used by the UnitOfWork to sort out the * correct order in which changes to entities need to be persisted. * - * @since 2.0 - * @author Roman Borschel + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco */ class CommitOrderCalculator { @@ -60,10 +61,9 @@ class CommitOrderCalculator { // Check whether we need to do anything. 0 or 1 node is easy. $nodeCount = count($this->_classes); - if ($nodeCount == 0) { - return array(); - } else if ($nodeCount == 1) { - return array_values($this->_classes); + + if ($nodeCount <= 1) { + return ($nodeCount == 1) ? array_values($this->_classes) : array(); } // Init diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index ced83b126..36dbfa46a 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -36,11 +36,6 @@ use PDO, * @author Guilherme Blanco * * @internal Highly performance-sensitive code. - * - * @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable. - * Example: SELECT u AS user FROM User u - * The result should contains an array where each array index is an array: array('user' => [User object]) - * Problem must be solved somehow by removing the isMixed in ResultSetMapping */ class ObjectHydrator extends AbstractHydrator { diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 53bff7066..d8a53f2f1 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -164,7 +164,7 @@ final class PersistentCollection implements Collection // If _backRefFieldName is set and its a one-to-many association, // we need to set the back reference. - if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) { + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { // Set back reference to owner $this->typeClass->reflFields[$this->backRefFieldName]->setValue( $element, $this->owner @@ -189,7 +189,7 @@ final class PersistentCollection implements Collection // If _backRefFieldName is set, then the association is bidirectional // and we need to set the back reference. - if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) { + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { // Set back reference to owner $this->typeClass->reflFields[$this->backRefFieldName]->setValue( $element, $this->owner @@ -304,7 +304,7 @@ final class PersistentCollection implements Collection if ($this->association !== null && $this->association['isOwningSide'] && - $this->association['type'] == ClassMetadata::MANY_TO_MANY && + $this->association['type'] === ClassMetadata::MANY_TO_MANY && $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) { $this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner); } @@ -425,7 +425,7 @@ final class PersistentCollection implements Collection $this->changed(); if ($this->association !== null && - $this->association['type'] == ClassMetadata::ONE_TO_MANY && + $this->association['type'] === ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) { $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); } @@ -448,7 +448,7 @@ final class PersistentCollection implements Collection */ public function contains($element) { - if ( ! $this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); return $this->coll->contains($element) || $persister->contains($this, $element); @@ -514,7 +514,7 @@ final class PersistentCollection implements Collection */ public function count() { - if ( ! $this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); return $persister->count($this) + ($this->isDirty ? $this->coll->count() : 0); @@ -630,7 +630,7 @@ final class PersistentCollection implements Collection $uow = $this->em->getUnitOfWork(); - if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) { + if ($this->association['type'] === ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) { // we need to initialize here, as orphan removal acts like implicit cascadeRemove, // hence for event listeners we need the objects in memory. $this->initialize(); diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index 00fcffd8c..1eef6afeb 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -49,7 +49,7 @@ class Lexer extends \Doctrine\Common\Lexer const T_PLUS = 17; const T_OPEN_CURLY_BRACE = 18; const T_CLOSE_CURLY_BRACE = 19; - + // All tokens that are also identifiers should be >= 100 const T_IDENTIFIER = 100; const T_ALL = 101; @@ -133,7 +133,7 @@ class Lexer extends \Doctrine\Common\Lexer '\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}' ); } - + /** * @inheritdoc */ @@ -149,50 +149,58 @@ class Lexer extends \Doctrine\Common\Lexer { $type = self::T_NONE; - // Recognizing numeric values - if (is_numeric($value)) { - return (strpos($value, '.') !== false || stripos($value, 'e') !== false) - ? self::T_FLOAT : self::T_INTEGER; - } - - // Differentiate between quoted names, identifiers, input parameters and symbols - if ($value[0] === "'") { - $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); - return self::T_STRING; - } else if (ctype_alpha($value[0]) || $value[0] === '_') { - $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value); - - if (defined($name)) { - $type = constant($name); - - if ($type > 100) { - return $type; + switch (true) { + // Recognize numeric values + case (is_numeric($value)): + if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { + return self::T_FLOAT; } - } - return self::T_IDENTIFIER; - } else if ($value[0] === '?' || $value[0] === ':') { - return self::T_INPUT_PARAMETER; - } else { - switch ($value) { - case '.': return self::T_DOT; - case ',': return self::T_COMMA; - case '(': return self::T_OPEN_PARENTHESIS; - case ')': return self::T_CLOSE_PARENTHESIS; - case '=': return self::T_EQUALS; - case '>': return self::T_GREATER_THAN; - case '<': return self::T_LOWER_THAN; - case '+': return self::T_PLUS; - case '-': return self::T_MINUS; - case '*': return self::T_MULTIPLY; - case '/': return self::T_DIVIDE; - case '!': return self::T_NEGATE; - case '{': return self::T_OPEN_CURLY_BRACE; - case '}': return self::T_CLOSE_CURLY_BRACE; - default: - // Do nothing - break; - } + return self::T_INTEGER; + + // Recognize quoted strings + case ($value[0] === "'"): + $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + + // Recognize identifiers + case (ctype_alpha($value[0]) || $value[0] === '_'): + $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value); + + if (defined($name)) { + $type = constant($name); + + if ($type > 100) { + return $type; + } + } + + return self::T_IDENTIFIER; + + // Recognize input parameters + case ($value[0] === '?' || $value[0] === ':'): + return self::T_INPUT_PARAMETER; + + // Recognize symbols + case ($value === '.'): return self::T_DOT; + case ($value === ','): return self::T_COMMA; + case ($value === '('): return self::T_OPEN_PARENTHESIS; + case ($value === ')'): return self::T_CLOSE_PARENTHESIS; + case ($value === '='): return self::T_EQUALS; + case ($value === '>'): return self::T_GREATER_THAN; + case ($value === '<'): return self::T_LOWER_THAN; + case ($value === '+'): return self::T_PLUS; + case ($value === '-'): return self::T_MINUS; + case ($value === '*'): return self::T_MULTIPLY; + case ($value === '/'): return self::T_DIVIDE; + case ($value === '!'): return self::T_NEGATE; + case ($value === '{'): return self::T_OPEN_CURLY_BRACE; + case ($value === '}'): return self::T_CLOSE_CURLY_BRACE; + + // Default + default: + // Do nothing } return $type; diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 1a34963f2..a3725eb8d 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -677,13 +677,10 @@ class Parser } // Build the error message - $semanticalError = 'Invalid PathExpression. '; - - if (count($expectedStringTypes) == 1) { - $semanticalError .= 'Must be a ' . $expectedStringTypes[0] . '.'; - } else { - $semanticalError .= implode(' or ', $expectedStringTypes) . ' expected.'; - } + $semanticalError = 'Invalid PathExpression. '; + $semanticalError .= (count($expectedStringTypes) == 1) + ? 'Must be a ' . $expectedStringTypes[0] . '.' + : implode(' or ', $expectedStringTypes) . ' expected.'; $this->semanticalError($semanticalError, $deferredItem['token']); } @@ -1866,53 +1863,78 @@ class Parser $expression = null; $identVariable = null; $peek = $this->_lexer->glimpse(); + $lookaheadType = $this->_lexer->lookahead['type']; - if ($this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT) { + switch (true) { // ScalarExpression (u.name) - $expression = $this->ScalarExpression(); - } else if ($this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS) { - // IdentificationVariable (u) - $expression = $identVariable = $this->IdentificationVariable(); - } else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) { - // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...)) - $expression = $this->CaseExpression(); - } else if ($this->_isFunction()) { - // DQL Function (SUM(u.value) or SUM(u.value) + 1) - $this->_lexer->peek(); // "(" - - $lookaheadType = $this->_lexer->lookahead['type']; - $beyond = $this->_peekBeyondClosingParenthesis(); - - if ($this->_isMathOperator($beyond)) { - // SUM(u.id) + COUNT(u.id) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT): $expression = $this->ScalarExpression(); - } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { - // COUNT(u.id) - $expression = $this->AggregateExpression(); - } else { - // SUM(u.id) - $expression = $this->FunctionDeclaration(); - } - } else if ($this->_lexer->lookahead['type'] === Lexer::T_PARTIAL) { + break; + + // IdentificationVariable (u) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $identVariable = $this->IdentificationVariable(); + break; + + // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...)) + case ($lookaheadType === Lexer::T_CASE): + case ($lookaheadType === Lexer::T_COALESCE): + case ($lookaheadType === Lexer::T_NULLIF): + $expression = $this->CaseExpression(); + break; + + // DQL Function (SUM(u.value) or SUM(u.value) + 1) + case ($this->_isFunction()): + $this->_lexer->peek(); // "(" + + switch (true) { + case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + $expression = $this->ScalarExpression(); + break; + + case ($this->_isAggregateFunction($lookaheadType)): + // COUNT(u.id) + $expression = $this->AggregateExpression(); + break; + + default: + // IDENTITY(u) + $expression = $this->FunctionDeclaration(); + break; + } + + break; + // PartialObjectExpression (PARTIAL u.{id, name}) - $expression = $this->PartialObjectExpression(); - $identVariable = $expression->identificationVariable; - } else if ($this->_lexer->lookahead['type'] === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT) { + case ($lookaheadType === Lexer::T_PARTIAL): + $expression = $this->PartialObjectExpression(); + $identVariable = $expression->identificationVariable; + break; + // Subselect - $this->match(Lexer::T_OPEN_PARENTHESIS); - $expression = $this->Subselect(); - $this->match(Lexer::T_CLOSE_PARENTHESIS); - } else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_OPEN_PARENTHESIS, Lexer::T_INTEGER, Lexer::T_FLOAT, Lexer::T_STRING))) { + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT): + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + break; + // Shortcut: ScalarExpression => SimpleArithmeticExpression - $expression = $this->SimpleArithmeticExpression(); - } else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_PLUS, Lexer::T_MINUS))) { - // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) - $expression = $this->SimpleArithmeticExpression(); - } else { - $this->syntaxError( - 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', - $this->_lexer->lookahead - ); + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS): + case ($lookaheadType === Lexer::T_INTEGER): + case ($lookaheadType === Lexer::T_STRING): + case ($lookaheadType === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) + case ($lookaheadType === Lexer::T_MINUS): + case ($lookaheadType === Lexer::T_PLUS): + $expression = $this->SimpleArithmeticExpression(); + break; + + default: + $this->syntaxError( + 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', + $this->_lexer->lookahead + ); } // [["AS"] ["HIDDEN"] AliasResultVariable] @@ -1965,25 +1987,41 @@ class Parser { $peek = $this->_lexer->glimpse(); - if ($peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { - // SingleValuedPathExpression | IdentificationVariable - $expression = ($peek['value'] == '.') - ? $this->StateFieldPathExpression() - : $this->IdentificationVariable(); + switch ($this->_lexer->lookahead['type']) { + case Lexer::T_IDENTIFIER: + switch (true) { + case ($peek['type'] === Lexer::T_DOT): + $expression = $this->StateFieldPathExpression(); + + return new AST\SimpleSelectExpression($expression); + + case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $this->IdentificationVariable(); + + return new AST\SimpleSelectExpression($expression); + + default: + // Do nothing + } + break; + + case Lexer::T_OPEN_PARENTHESIS: + if ($peek['type'] !== Lexer::T_SELECT) { + // Shortcut: ScalarExpression => SimpleArithmeticExpression + $expression = $this->SimpleArithmeticExpression(); + + return new AST\SimpleSelectExpression($expression); + } - return new AST\SimpleSelectExpression($expression); - } else if ($this->_lexer->lookahead['value'] == '(') { - if ($peek['type'] == Lexer::T_SELECT) { // Subselect $this->match(Lexer::T_OPEN_PARENTHESIS); $expression = $this->Subselect(); $this->match(Lexer::T_CLOSE_PARENTHESIS); - } else { - // Shortcut: ScalarExpression => SimpleArithmeticExpression - $expression = $this->SimpleArithmeticExpression(); - } - return new AST\SimpleSelectExpression($expression); + return new AST\SimpleSelectExpression($expression); + + default: + // Do nothing } $this->_lexer->peek(); @@ -2099,27 +2137,26 @@ class Parser { $condPrimary = new AST\ConditionalPrimary; - if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { - // Peek beyond the matching closing paranthesis ')' - $peek = $this->_peekBeyondClosingParenthesis(); - - if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || - $peek['type'] === Lexer::T_NOT || - $peek['type'] === Lexer::T_BETWEEN || - $peek['type'] === Lexer::T_LIKE || - $peek['type'] === Lexer::T_IN || - $peek['type'] === Lexer::T_IS || - $peek['type'] === Lexer::T_EXISTS) { - $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); - } else { - $this->match(Lexer::T_OPEN_PARENTHESIS); - $condPrimary->conditionalExpression = $this->ConditionalExpression(); - $this->match(Lexer::T_CLOSE_PARENTHESIS); - } - } else { + if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; } + // Peek beyond the matching closing paranthesis ')' + $peek = $this->_peekBeyondClosingParenthesis(); + + if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || + in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS))) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + $condPrimary->conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + return $condPrimary; } @@ -2132,10 +2169,10 @@ class Parser */ public function SimpleConditionalExpression() { + $token = $this->_lexer->lookahead; + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { $token = $this->_lexer->glimpse(); - } else { - $token = $this->_lexer->lookahead; } if ($token['type'] === Lexer::T_EXISTS) { @@ -2464,9 +2501,9 @@ class Parser } return $this->FunctionDeclaration(); - } else { - return $this->Literal(); } + + return $this->Literal(); } } @@ -2498,30 +2535,46 @@ class Parser */ public function StringPrimary() { - if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { - $peek = $this->_lexer->glimpse(); + $lookaheadType = $this->_lexer->lookahead['type']; + + switch ($lookaheadType) { + case Lexer::T_IDENTIFIER: + $peek = $this->_lexer->glimpse(); + + if ($peek['value'] == '.') { + return $this->StateFieldPathExpression(); + } + + if ($peek['value'] == '(') { + // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions. + return $this->FunctionDeclaration(); + } - if ($peek['value'] == '.') { - return $this->StateFieldPathExpression(); - } else if ($peek['value'] == '(') { - // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions. - return $this->FunctionDeclaration(); - } else { $this->syntaxError("'.' or '('"); - } - } else if ($this->_lexer->isNextToken(Lexer::T_STRING)) { - $this->match(Lexer::T_STRING); + break; - return $this->_lexer->token['value']; - } else if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { - return $this->InputParameter(); - } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { - return $this->AggregateExpression(); - } else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) { - return $this->CaseExpression(); + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + + return $this->_lexer->token['value']; + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + case Lexer::T_CASE: + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + return $this->CaseExpression(); + + default: + if ($this->_isAggregateFunction($lookaheadType)) { + return $this->AggregateExpression(); + } } - $this->syntaxError('StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'); + $this->syntaxError( + 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression' + ); } /** @@ -2564,40 +2617,28 @@ class Parser */ public function AggregateExpression() { + $lookaheadType = $this->_lexer->lookahead['type']; $isDistinct = false; - $functionName = ''; - if ($this->_lexer->isNextToken(Lexer::T_COUNT)) { - $this->match(Lexer::T_COUNT); - $functionName = $this->_lexer->token['value']; - $this->match(Lexer::T_OPEN_PARENTHESIS); - - if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { - $this->match(Lexer::T_DISTINCT); - $isDistinct = true; - } - - $pathExp = $this->SingleValuedPathExpression(); - $this->match(Lexer::T_CLOSE_PARENTHESIS); - } else { - if ($this->_lexer->isNextToken(Lexer::T_AVG)) { - $this->match(Lexer::T_AVG); - } else if ($this->_lexer->isNextToken(Lexer::T_MAX)) { - $this->match(Lexer::T_MAX); - } else if ($this->_lexer->isNextToken(Lexer::T_MIN)) { - $this->match(Lexer::T_MIN); - } else if ($this->_lexer->isNextToken(Lexer::T_SUM)) { - $this->match(Lexer::T_SUM); - } else { - $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT'); - } - - $functionName = $this->_lexer->token['value']; - $this->match(Lexer::T_OPEN_PARENTHESIS); - $pathExp = $this->SimpleArithmeticExpression(); - $this->match(Lexer::T_CLOSE_PARENTHESIS); + if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) { + $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT'); } + $this->match($lookaheadType); + $functionName = $this->_lexer->token['value']; + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + $isDistinct = true; + } + + $pathExp = ($lookaheadType === Lexer::T_COUNT) + ? $this->SingleValuedPathExpression() + : $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); } @@ -2608,24 +2649,19 @@ class Parser */ public function QuantifiedExpression() { - $type = ''; + $lookaheadType = $this->_lexer->lookahead['type']; + $value = $this->_lexer->lookahead['value']; - if ($this->_lexer->isNextToken(Lexer::T_ALL)) { - $this->match(Lexer::T_ALL); - $type = 'ALL'; - } else if ($this->_lexer->isNextToken(Lexer::T_ANY)) { - $this->match(Lexer::T_ANY); - $type = 'ANY'; - } else if ($this->_lexer->isNextToken(Lexer::T_SOME)) { - $this->match(Lexer::T_SOME); - $type = 'SOME'; - } else { + if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) { $this->syntaxError('ALL, ANY or SOME'); } + $this->match($lookaheadType); $this->match(Lexer::T_OPEN_PARENTHESIS); + $qExpr = new AST\QuantifiedExpression($this->Subselect()); - $qExpr->type = $type; + $qExpr->type = $value; + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $qExpr; @@ -2666,14 +2702,11 @@ class Parser { $peek = $this->_lexer->glimpse(); - $leftExpr = $this->ArithmeticExpression(); - $operator = $this->ComparisonOperator(); - - if ($this->_isNextAllAnySome()) { - $rightExpr = $this->QuantifiedExpression(); - } else { - $rightExpr = $this->ArithmeticExpression(); - } + $leftExpr = $this->ArithmeticExpression(); + $operator = $this->ComparisonOperator(); + $rightExpr = ($this->_isNextAllAnySome()) + ? $this->QuantifiedExpression() + : $this->ArithmeticExpression(); return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr); }