From ee7b5da64aec642f101dff00492b00dca2e3b9db Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 1 Jan 2012 15:02:28 -0200 Subject: [PATCH 01/22] start work --- .../ORM/Query/AST/NewObjectExpression.php | 60 +++++++++++++++++++ lib/Doctrine/ORM/Query/Lexer.php | 1 + lib/Doctrine/ORM/Query/Parser.php | 35 +++++++++++ .../Doctrine/Tests/Models/CMS/CmsUserDTO.php | 17 ++++++ .../ORM/Query/SelectSqlGenerationTest.php | 12 ++++ 5 files changed, 125 insertions(+) create mode 100644 lib/Doctrine/ORM/Query/AST/NewObjectExpression.php create mode 100644 tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php diff --git a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php new file mode 100644 index 000000000..1642b6bfe --- /dev/null +++ b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" SimpleSelectExpression {"," SimpleSelectExpression}* ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class NewObjectExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $fieldSet; + + /** + * @param type $identificationVariable + * @param array $fieldSet + */ + public function __construct($identificationVariable, array $fieldSet) + { + $this->identificationVariable = $identificationVariable; + $this->fieldSet = $fieldSet; + } + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * @return string + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNewObject($this); + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index beafa7dee..4692b6ae8 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -108,6 +108,7 @@ class Lexer extends \Doctrine\Common\Lexer const T_WHERE = 154; const T_WITH = 155; const T_PARTIAL = 156; + const T_NEW = 157; /** * Creates a new query scanner object. diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index ac6ad6f3e..14d6c34f0 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1629,6 +1629,35 @@ class Parser return $partialObjectExpression; } + /** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" SimpleSelectExpression {"," SimpleSelectExpression}* ")" + * @return \Doctrine\ORM\Query\AST\NewObjectExpression + */ + public function NewObjectExpression() + { + $this->match(Lexer::T_NEW); + $this->match(Lexer::T_IDENTIFIER); + + $identificationVariable = $this->_lexer->token['value']; + + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $fieldSet[] = $this->SimpleSelectExpression(); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $fieldSet[] = $this->SimpleSelectExpression(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $expression = new AST\NewObjectExpression($identificationVariable, $fieldSet); + + // @TODO : Defer NewObjectExpression validation + throw new \BadMethodCallException("Not complete yet !"); + return $expression; + } + /** * IndexBy ::= "INDEX" "BY" StateFieldPathExpression * @@ -1953,6 +1982,12 @@ class Parser $expression = $this->SimpleArithmeticExpression(); break; + // NewObjectExpression (New ClassName(id, name)) + case ($lookaheadType === Lexer::T_NEW): + $expression = $this->NewObjectExpression(); + //$identVariable = $expression->identificationVariable; + break; + default: $this->syntaxError( 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php new file mode 100644 index 000000000..78398f863 --- /dev/null +++ b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php @@ -0,0 +1,17 @@ +name = $name; + $this->email = $email; + $this->city = $city; + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index fa5b06d82..232a86ea7 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1555,6 +1555,18 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-1574 + */ + public function testSupportsNewOperator() + { + $this->markTestIncomplete('not complete yet !'); + $this->assertSqlGeneration( + 'SELECT new Doctrine\Tests\Models\CMS\CmsUser(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', + 'SELECT c0_.name AS name0, c1_.email AS email1, c2_.city AS city2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + ); + } + public function testCustomTypeValueSql() { if (DBALType::hasType('negative_to_positive')) { From 0c1a8cd43f48692a1d0a898b7afd002eed95d061 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Tue, 3 Jan 2012 10:31:54 -0200 Subject: [PATCH 02/22] sql generation --- lib/Doctrine/ORM/Query/Parser.php | 7 +++---- lib/Doctrine/ORM/Query/SqlWalker.php | 6 ++++++ tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | 1 - 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 14d6c34f0..657280284 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1642,19 +1642,18 @@ class Parser $this->match(Lexer::T_OPEN_PARENTHESIS); - $fieldSet[] = $this->SimpleSelectExpression(); + $fieldSet[] = $this->SelectExpression(); while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); - $fieldSet[] = $this->SimpleSelectExpression(); + $fieldSet[] = $this->SelectExpression(); } $this->match(Lexer::T_CLOSE_PARENTHESIS); $expression = new AST\NewObjectExpression($identificationVariable, $fieldSet); - // @TODO : Defer NewObjectExpression validation - throw new \BadMethodCallException("Not complete yet !"); + // @TODO : Defer NewObjectExpression validation ? return $expression; } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 13c2b5df0..2bad829f9 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1221,6 +1221,12 @@ class SqlWalker implements TreeWalker } break; + case ($expr instanceof AST\NewObjectExpression): + $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $expr->fieldSet)); + $sql .= implode(', ', $sqlSelectExpressions); + + break; + default: // IdentificationVariable or PartialObjectExpression if ($expr instanceof AST\PartialObjectExpression) { diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 232a86ea7..074fdbd5f 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1560,7 +1560,6 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase */ public function testSupportsNewOperator() { - $this->markTestIncomplete('not complete yet !'); $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUser(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', 'SELECT c0_.name AS name0, c1_.email AS email1, c2_.city AS city2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' From 0e60c50c5e4e661ae3bc33bcab6468282b102607 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 7 Jan 2012 14:42:44 -0200 Subject: [PATCH 03/22] small code refactoring --- .../ORM/Query/AST/NewObjectExpression.php | 16 ++++++------- lib/Doctrine/ORM/Query/Parser.php | 24 ++++++++++++++----- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- .../ORM/Query/SelectSqlGenerationTest.php | 2 +- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php index 1642b6bfe..c92481a62 100644 --- a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php +++ b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -20,7 +20,7 @@ namespace Doctrine\ORM\Query\AST; /** - * NewObjectExpression ::= "NEW" IdentificationVariable "(" SimpleSelectExpression {"," SimpleSelectExpression}* ")" + * NewObjectExpression ::= "NEW" IdentificationVariable "(" SelectExpression {"," SelectExpression}* ")" * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org @@ -32,21 +32,21 @@ class NewObjectExpression extends Node /** * @var string */ - public $identificationVariable; + public $className; /** * @var array */ - public $fieldSet; + public $args; /** - * @param type $identificationVariable - * @param array $fieldSet + * @param type $className + * @param array $args */ - public function __construct($identificationVariable, array $fieldSet) + public function __construct($className, array $args) { - $this->identificationVariable = $identificationVariable; - $this->fieldSet = $fieldSet; + $this->className = $className; + $this->args = $args; } /** diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 657280284..94a435a37 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1630,7 +1630,7 @@ class Parser } /** - * NewObjectExpression ::= "NEW" IdentificationVariable "(" SimpleSelectExpression {"," SimpleSelectExpression}* ")" + * NewObjectExpression ::= "NEW" IdentificationVariable "(" SelectExpression {"," SelectExpression}* ")" * @return \Doctrine\ORM\Query\AST\NewObjectExpression */ public function NewObjectExpression() @@ -1638,20 +1638,33 @@ class Parser $this->match(Lexer::T_NEW); $this->match(Lexer::T_IDENTIFIER); - $identificationVariable = $this->_lexer->token['value']; + $className = $this->_lexer->token['value']; + + if ( ! class_exists($className, true)) { + $this->semanticalError("Class '$className' is not defined.", $this->_lexer->token); + } + + $class = new \ReflectionClass($className); + if($class->getConstructor() === null) { + $this->semanticalError("Class '$className' has not a valid contructor.", $this->_lexer->token); + } $this->match(Lexer::T_OPEN_PARENTHESIS); - $fieldSet[] = $this->SelectExpression(); + $args[] = $this->SelectExpression(); while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); - $fieldSet[] = $this->SelectExpression(); + $args[] = $this->SelectExpression(); } $this->match(Lexer::T_CLOSE_PARENTHESIS); - $expression = new AST\NewObjectExpression($identificationVariable, $fieldSet); + if($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { + $this->semanticalError("Number of arguments does not match definition.", $this->_lexer->token); + } + + $expression = new AST\NewObjectExpression($className, $args); // @TODO : Defer NewObjectExpression validation ? return $expression; @@ -1984,7 +1997,6 @@ class Parser // NewObjectExpression (New ClassName(id, name)) case ($lookaheadType === Lexer::T_NEW): $expression = $this->NewObjectExpression(); - //$identVariable = $expression->identificationVariable; break; default: diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 2bad829f9..15b4a173c 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1222,7 +1222,7 @@ class SqlWalker implements TreeWalker break; case ($expr instanceof AST\NewObjectExpression): - $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $expr->fieldSet)); + $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $expr->args)); $sql .= implode(', ', $sqlSelectExpressions); break; diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 074fdbd5f..1e812f366 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1561,7 +1561,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase public function testSupportsNewOperator() { $this->assertSqlGeneration( - 'SELECT new Doctrine\Tests\Models\CMS\CmsUser(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', + 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', 'SELECT c0_.name AS name0, c1_.email AS email1, c2_.city AS city2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' ); } From ed89695a8cf54f5497ba3c61d0be648b72091f9d Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 8 Jan 2012 16:47:12 -0200 Subject: [PATCH 04/22] collect new object parameters --- lib/Doctrine/ORM/Query/ResultSetMapping.php | 5 ++ lib/Doctrine/ORM/Query/SqlWalker.php | 11 +++- .../Tests/ORM/Functional/QueryTest.php | 63 ++++++++++++++++++- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index b8cbd3225..35505bf5c 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -118,6 +118,11 @@ class ResultSetMapping */ public $isIdentifierColumn = array(); + /** + * @var array Maps column names in the result set to field names for each new object expression. + */ + public $newObjectMappings = array(); + /** * Adds an entity result to this ResultSetMapping. * diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 15b4a173c..be9e59235 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1222,9 +1222,16 @@ class SqlWalker implements TreeWalker break; case ($expr instanceof AST\NewObjectExpression): - $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $expr->args)); - $sql .= implode(', ', $sqlSelectExpressions); + $sqlSelectExpressions = array(); + $this->_rsm->newObjectMappings['className'] = $expr->className; + foreach ($expr->args as $e) { + $resultAliasMap = $this->scalarResultAliasMap; + $sqlSelectExpressions[] = $this->walkSelectExpression($e); + $this->_rsm->newObjectMappings['resultAliasMap'][] = array_diff($this->scalarResultAliasMap, $resultAliasMap); + } + + $sql .= implode(', ', $sqlSelectExpressions); break; default: diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php index 906b5e68f..133b8910e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php @@ -780,4 +780,65 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[2]); $this->assertNull($users[3]); } -} + + /** + * @group DDC-1574 + */ + public function testSupportsNewOperator() + { + $u1 = new CmsUser; + $u2 = new CmsUser; + $u3 = new CmsUser; + + $u1->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); + $u1->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); + + $u2->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); + $u2->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); + + $u3->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); + $u3->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); + + $u1->name = 'Test 1'; + $u1->username = '1test'; + $u1->status = 'developer'; + $u1->email->email = 'email@test1.com'; + $u1->address->zip = '111111111'; + $u1->address->city = 'Some City 1'; + $u1->address->country = 'Some Country 2'; + + $u2->name = 'Test 2'; + $u2->username = '2test'; + $u2->status = 'developer'; + $u2->email->email = 'email@test2.com'; + $u2->address->zip = '111111111'; + $u2->address->city = 'Some City 2'; + $u2->address->country = 'Some Country 2'; + + $u3->name = 'Fabio Silva'; + $u3->username = 'FabioBatSilva'; + $u3->status = 'developer'; + $u3->email->email = 'fabio.bat.silva@gmail.com'; + $u3->address->zip = '33333333'; + $u3->address->city = 'Some City 3'; + $u3->address->country = 'Some Country 3'; + + $this->_em->persist($u1); + $this->_em->persist($u2); + $this->_em->persist($u3); + + $this->_em->flush(); + $this->_em->clear(); + + $query = $this->_em->createQuery("SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name"); + $result = $query->getResult(); + + $this->assertEquals(3, count($result)); + + $this->markTestIncomplete(); + + $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); + $this->assertTrue($result[1] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); + $this->assertTrue($result[2] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); + } +} \ No newline at end of file From 0fbb78e61a32151f471a401f2e15249b608729dc Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 21 Jan 2012 17:39:32 -0200 Subject: [PATCH 05/22] basic support, need some code refactory and improvements --- .../Internal/Hydration/AbstractHydrator.php | 12 ++++++++ .../ORM/Internal/Hydration/ObjectHydrator.php | 29 +++++++++++++++++++ lib/Doctrine/ORM/Query/SqlWalker.php | 22 ++++++++++++-- .../Tests/ORM/Functional/QueryTest.php | 2 +- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index a5eae8b72..f8eb6b820 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -234,6 +234,18 @@ abstract class AbstractHydrator // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping. continue 2; } + + if (isset ($this->_rsm->newObjectMappings[$key])) { + $cache[$key]['isNewObjectParameter'] = true; + } + } + + if (isset ($cache[$key]['isNewObjectParameter'])) { + $argIndex = $this->_rsm->newObjectMappings[$key]['argIndex']; + $objIndex = $this->_rsm->newObjectMappings[$key]['objIndex']; + $className = $this->_rsm->newObjectMappings[$key]['className']; + $rowData['newObjects'][$objIndex]['className'] = $className; + $rowData['newObjects'][$objIndex]['args'][$argIndex] = $cache[$key]['fieldName']; } if (isset($cache[$key]['isScalar'])) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 3f48f8607..7275ffa76 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -330,6 +330,17 @@ class ObjectHydrator extends AbstractHydrator } } + // Extract "new" object constructor arguments. They're appended at the end. + if (isset($rowData['newObjects'])) { + $newObjects = $rowData['newObjects']; + + unset($rowData['newObjects']); + + if (empty($rowData)) { + ++$this->_resultCounter; + } + } + // Hydrate the data chunks foreach ($rowData as $dqlAlias => $data) { $entityName = $this->_rsm->aliasMap[$dqlAlias]; @@ -531,6 +542,24 @@ class ObjectHydrator extends AbstractHydrator $result[$resultKey][$name] = $value; } } + + // Append new object to mixed result sets + if (isset($newObjects)) { + if ( ! isset($resultKey) ) { + $resultKey = $this->_resultCounter - 1; + } + + foreach ($newObjects as $newObject) { + $args = array(); + $className = $newObject['className']; + foreach ($newObject['args'] as $index => $name) { + $args[$index] = $result[$resultKey][$name]; + } + $class = new \ReflectionClass($className); + $result[$resultKey] = $class->newInstanceArgs($args); + } + } + } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index be9e59235..2fff87ff0 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -77,6 +77,13 @@ class SqlWalker implements TreeWalker */ private $sqlParamIndex = 0; + /** + * Counters for generating indexes. + * + * @var integer + */ + private $newObjectCounter; + /** * @var ParserResult */ @@ -1225,10 +1232,19 @@ class SqlWalker implements TreeWalker $sqlSelectExpressions = array(); $this->_rsm->newObjectMappings['className'] = $expr->className; - foreach ($expr->args as $e) { - $resultAliasMap = $this->scalarResultAliasMap; + $sqlSelectExpressions = array(); + $objIndex = $this->newObjectCounter ++; + foreach ($expr->args as $key => $e) { + $resultAliasMap = $this->scalarResultAliasMap; $sqlSelectExpressions[] = $this->walkSelectExpression($e); - $this->_rsm->newObjectMappings['resultAliasMap'][] = array_diff($this->scalarResultAliasMap, $resultAliasMap); + $scalarResultAliasMap = array_diff($this->scalarResultAliasMap, $resultAliasMap); + foreach ($scalarResultAliasMap as $aliasMap) { + $this->_rsm->newObjectMappings[$aliasMap] = array( + 'className' => $expr->className, + 'objIndex' => $objIndex, + 'argIndex' => $key + ); + } } $sql .= implode(', ', $sqlSelectExpressions); diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php index 133b8910e..780c7e8a2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php @@ -833,7 +833,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $query = $this->_em->createQuery("SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name"); $result = $query->getResult(); - $this->assertEquals(3, count($result)); + $this->assertCount(3, $result); $this->markTestIncomplete(); From b29d47a682b300a274b26fdf8a33ddf91216ac31 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 21 Jan 2012 18:16:38 -0200 Subject: [PATCH 06/22] cache new object mappings --- .../ORM/Internal/Hydration/AbstractHydrator.php | 12 +++++++----- .../ORM/Internal/Hydration/ObjectHydrator.php | 5 ++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index f8eb6b820..8631dc297 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -237,15 +237,17 @@ abstract class AbstractHydrator if (isset ($this->_rsm->newObjectMappings[$key])) { $cache[$key]['isNewObjectParameter'] = true; + $cache[$key]['argIndex'] = $this->_rsm->newObjectMappings[$key]['argIndex']; + $cache[$key]['objIndex'] = $this->_rsm->newObjectMappings[$key]['objIndex']; + $cache[$key]['class'] = new \ReflectionClass($this->_rsm->newObjectMappings[$key]['className']); } } if (isset ($cache[$key]['isNewObjectParameter'])) { - $argIndex = $this->_rsm->newObjectMappings[$key]['argIndex']; - $objIndex = $this->_rsm->newObjectMappings[$key]['objIndex']; - $className = $this->_rsm->newObjectMappings[$key]['className']; - $rowData['newObjects'][$objIndex]['className'] = $className; - $rowData['newObjects'][$objIndex]['args'][$argIndex] = $cache[$key]['fieldName']; + $argIndex = $cache[$key]['argIndex']; + $objIndex = $cache[$key]['objIndex']; + $rowData['newObjects'][$objIndex]['class'] = $cache[$key]['class']; + $rowData['newObjects'][$objIndex]['args'][$argIndex] = $cache[$key]['fieldName']; } if (isset($cache[$key]['isScalar'])) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 7275ffa76..7ab624ec0 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -550,12 +550,11 @@ class ObjectHydrator extends AbstractHydrator } foreach ($newObjects as $newObject) { - $args = array(); - $className = $newObject['className']; + $args = array(); + $class = $newObject['class']; foreach ($newObject['args'] as $index => $name) { $args[$index] = $result[$resultKey][$name]; } - $class = new \ReflectionClass($className); $result[$resultKey] = $class->newInstanceArgs($args); } } From 2b403b7dad470d39e23259dc6c533b3a7648ba15 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 30 Jun 2012 18:15:09 -0300 Subject: [PATCH 07/22] basic support refactory --- .../Internal/Hydration/AbstractHydrator.php | 4 +- lib/Doctrine/ORM/Query/Parser.php | 47 +++++- lib/Doctrine/ORM/Query/SqlWalker.php | 33 ++-- .../Tests/Models/CMS/CmsAddressDTO.php | 17 ++ .../Doctrine/Tests/Models/CMS/CmsUserDTO.php | 10 +- .../Tests/ORM/Functional/NewOperatorTest.php | 158 ++++++++++++++++++ .../Tests/ORM/Functional/QueryTest.php | 63 +------ .../ORM/Query/SelectSqlGenerationTest.php | 2 +- 8 files changed, 241 insertions(+), 93 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php create mode 100644 tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 8631dc297..b2c6b9f5e 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -235,7 +235,7 @@ abstract class AbstractHydrator continue 2; } - if (isset ($this->_rsm->newObjectMappings[$key])) { + if (isset($this->_rsm->newObjectMappings[$key])) { $cache[$key]['isNewObjectParameter'] = true; $cache[$key]['argIndex'] = $this->_rsm->newObjectMappings[$key]['argIndex']; $cache[$key]['objIndex'] = $this->_rsm->newObjectMappings[$key]['objIndex']; @@ -243,7 +243,7 @@ abstract class AbstractHydrator } } - if (isset ($cache[$key]['isNewObjectParameter'])) { + if (isset($cache[$key]['isNewObjectParameter'])) { $argIndex = $cache[$key]['argIndex']; $objIndex = $cache[$key]['objIndex']; $rowData['newObjects'][$objIndex]['class'] = $cache[$key]['class']; diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 94a435a37..49cc7f6b5 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1630,7 +1630,7 @@ class Parser } /** - * NewObjectExpression ::= "NEW" IdentificationVariable "(" SelectExpression {"," SelectExpression}* ")" + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" * @return \Doctrine\ORM\Query\AST\NewObjectExpression */ public function NewObjectExpression() @@ -1643,19 +1643,19 @@ class Parser if ( ! class_exists($className, true)) { $this->semanticalError("Class '$className' is not defined.", $this->_lexer->token); } - + $class = new \ReflectionClass($className); if($class->getConstructor() === null) { $this->semanticalError("Class '$className' has not a valid contructor.", $this->_lexer->token); } $this->match(Lexer::T_OPEN_PARENTHESIS); - - $args[] = $this->SelectExpression(); + + $args[] = $this->NewObjectArg(); while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); - $args[] = $this->SelectExpression(); + $args[] = $this->NewObjectArg(); } $this->match(Lexer::T_CLOSE_PARENTHESIS); @@ -1664,10 +1664,41 @@ class Parser $this->semanticalError("Number of arguments does not match definition.", $this->_lexer->token); } - $expression = new AST\NewObjectExpression($className, $args); + return new AST\NewObjectExpression($className, $args);; + } - // @TODO : Defer NewObjectExpression validation ? - return $expression; + /** + * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression + */ + public function NewObjectArg() + { + $peek = $this->_lexer->glimpse(); + + 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); + + case ($this->_isFunction()): + // SUM(u.id) + COUNT(u.id) + if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) { + return new AST\SimpleSelectExpression($this->ScalarExpression()); + } + // COUNT(u.id) + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return new AST\SimpleSelectExpression($this->AggregateExpression()); + } + // IDENTITY(u) + return new AST\SimpleSelectExpression($this->FunctionDeclaration()); + } + + $this->semanticalError("Unsupported expression"); } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 2fff87ff0..5ffdccc16 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1229,25 +1229,28 @@ class SqlWalker implements TreeWalker break; case ($expr instanceof AST\NewObjectExpression): - $sqlSelectExpressions = array(); - $this->_rsm->newObjectMappings['className'] = $expr->className; - $sqlSelectExpressions = array(); - $objIndex = $this->newObjectCounter ++; - foreach ($expr->args as $key => $e) { + $sqlSelectExpressions = array(); + $objIndex = $this->newObjectCounter ++; + foreach ($expr->args as $argIndex => $e) { + + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; $resultAliasMap = $this->scalarResultAliasMap; - $sqlSelectExpressions[] = $this->walkSelectExpression($e); - $scalarResultAliasMap = array_diff($this->scalarResultAliasMap, $resultAliasMap); - foreach ($scalarResultAliasMap as $aliasMap) { - $this->_rsm->newObjectMappings[$aliasMap] = array( - 'className' => $expr->className, - 'objIndex' => $objIndex, - 'argIndex' => $key - ); - } + $sqlSelectExpressions[] = $this->walkSimpleSelectExpression($e) . ' AS ' . $columnAlias; + + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + + $this->rsm->newObjectMappings[$columnAlias] = array( + 'className' => $expr->className, + 'objIndex' => $objIndex, + 'argIndex' => $argIndex + ); } - $sql .= implode(', ', $sqlSelectExpressions); + $sql .= implode(',', $sqlSelectExpressions); break; default: diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php new file mode 100644 index 000000000..2a4f220f8 --- /dev/null +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php @@ -0,0 +1,17 @@ +country = $country; + $this->city = $city; + $this->zip = $zip; + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php index 78398f863..974c69e7d 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php @@ -6,12 +6,12 @@ class CmsUserDTO { public $name; public $email; - public $city; + public $address; - public function __construct($name = null, $email = null, $city = null) + public function __construct($name = null, $email = null, $address = null) { - $this->name = $name; - $this->email = $email; - $this->city = $city; + $this->name = $name; + $this->email = $email; + $this->address = $address; } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php new file mode 100644 index 000000000..6291427c0 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -0,0 +1,158 @@ +useModelSet('cms'); + parent::setUp(); + + $this->loadFixtures(); + } + + private function loadFixtures() + { + $u1 = new CmsUser; + $u2 = new CmsUser; + $u3 = new CmsUser; + + $u1->setEmail(new CmsEmail()); + $u1->setAddress(new CmsAddress()); + + $u2->setEmail(new CmsEmail()); + $u2->setAddress(new CmsAddress()); + + $u3->setEmail(new CmsEmail()); + $u3->setAddress(new CmsAddress()); + + $u1->name = 'Test 1'; + $u1->username = '1test'; + $u1->status = 'developer'; + $u1->email->email = 'email@test1.com'; + $u1->address->zip = '111111111'; + $u1->address->city = 'Some City 1'; + $u1->address->country = 'Some Country 2'; + + $u2->name = 'Test 2'; + $u2->username = '2test'; + $u2->status = 'developer'; + $u2->email->email = 'email@test2.com'; + $u2->address->zip = '222222222'; + $u2->address->city = 'Some City 2'; + $u2->address->country = 'Some Country 2'; + + $u3->name = 'Test 3'; + $u3->username = '3test'; + $u3->status = 'developer'; + $u3->email->email = 'email@test3.com'; + $u3->address->zip = '33333333'; + $u3->address->city = 'Some City 3'; + $u3->address->country = 'Some Country 3'; + + $this->_em->persist($u1); + $this->_em->persist($u2); + $this->_em->persist($u3); + + $this->_em->flush(); + $this->_em->clear(); + + $this->fixtures = array($u1, $u2, $u3); + } + + public function testShouldSupportsBasicUsage() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER + BY u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals($this->fixtures[0]->email->email, $result[0]->email); + $this->assertEquals($this->fixtures[1]->email->email, $result[1]->email); + $this->assertEquals($this->fixtures[2]->email->email, $result[2]->email); + + $this->assertEquals($this->fixtures[0]->address->city, $result[0]->address); + $this->assertEquals($this->fixtures[1]->address->city, $result[1]->address); + $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); + } + + public function testShouldSupportNestedOperators() + { + $this->markTestIncomplete(); + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + new Doctrine\Tests\Models\CMS\CmsUserDTO(a.country, a.city, a.zip) + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER + BY u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + } + + public function testShouldSupportAggregateFunctions() + { + $this->markTestIncomplete(); + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php index 780c7e8a2..906b5e68f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php @@ -780,65 +780,4 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[2]); $this->assertNull($users[3]); } - - /** - * @group DDC-1574 - */ - public function testSupportsNewOperator() - { - $u1 = new CmsUser; - $u2 = new CmsUser; - $u3 = new CmsUser; - - $u1->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); - $u1->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); - - $u2->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); - $u2->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); - - $u3->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); - $u3->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); - - $u1->name = 'Test 1'; - $u1->username = '1test'; - $u1->status = 'developer'; - $u1->email->email = 'email@test1.com'; - $u1->address->zip = '111111111'; - $u1->address->city = 'Some City 1'; - $u1->address->country = 'Some Country 2'; - - $u2->name = 'Test 2'; - $u2->username = '2test'; - $u2->status = 'developer'; - $u2->email->email = 'email@test2.com'; - $u2->address->zip = '111111111'; - $u2->address->city = 'Some City 2'; - $u2->address->country = 'Some Country 2'; - - $u3->name = 'Fabio Silva'; - $u3->username = 'FabioBatSilva'; - $u3->status = 'developer'; - $u3->email->email = 'fabio.bat.silva@gmail.com'; - $u3->address->zip = '33333333'; - $u3->address->city = 'Some City 3'; - $u3->address->country = 'Some Country 3'; - - $this->_em->persist($u1); - $this->_em->persist($u2); - $this->_em->persist($u3); - - $this->_em->flush(); - $this->_em->clear(); - - $query = $this->_em->createQuery("SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name"); - $result = $query->getResult(); - - $this->assertCount(3, $result); - - $this->markTestIncomplete(); - - $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); - $this->assertTrue($result[1] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); - $this->assertTrue($result[2] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); - } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 1e812f366..b9ec036ee 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1562,7 +1562,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', - 'SELECT c0_.name AS name0, c1_.email AS email1, c2_.city AS city2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + 'SELECT c0_.name AS sclr01, c1_.email AS sclr12, c2_.city AS sclr23 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' ); } From 88f04b5ebd62ed7c06dd91da12bae55a4e123a45 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 30 Jun 2012 19:25:54 -0300 Subject: [PATCH 08/22] parse nested new operators --- lib/Doctrine/ORM/Query/Parser.php | 6 +- lib/Doctrine/ORM/Query/SqlWalker.php | 64 ++++++++++++------- .../Tests/ORM/Functional/NewOperatorTest.php | 6 +- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 49cc7f6b5..4b577af96 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1680,10 +1680,8 @@ class Parser return new AST\SimpleSelectExpression($expression); - case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): - $expression = $this->IdentificationVariable(); - - return new AST\SimpleSelectExpression($expression); + case ($this->_lexer->lookahead['type'] === Lexer::T_NEW): + return $this->NewObjectExpression(); case ($this->_isFunction()): // SUM(u.id) + COUNT(u.id) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 5ffdccc16..c6eb8e2cb 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1229,28 +1229,7 @@ class SqlWalker implements TreeWalker break; case ($expr instanceof AST\NewObjectExpression): - - $sqlSelectExpressions = array(); - $objIndex = $this->newObjectCounter ++; - foreach ($expr->args as $argIndex => $e) { - - $resultAlias = $this->scalarResultCounter++; - $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; - $resultAliasMap = $this->scalarResultAliasMap; - $sqlSelectExpressions[] = $this->walkSimpleSelectExpression($e) . ' AS ' . $columnAlias; - - - $this->scalarResultAliasMap[$resultAlias] = $columnAlias; - $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); - - $this->rsm->newObjectMappings[$columnAlias] = array( - 'className' => $expr->className, - 'objIndex' => $objIndex, - 'argIndex' => $argIndex - ); - } - - $sql .= implode(',', $sqlSelectExpressions); + $sql .= $this->walkNewObject($expr); break; default: @@ -1419,6 +1398,47 @@ class SqlWalker implements TreeWalker . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); } + /** + * @param AST\NewObjectExpression + * @return string The SQL. + */ + public function walkNewObject($newObjectExpression) + { + + $sqlSelectExpressions = array(); + $objIndex = $this->newObjectCounter ++; + foreach ($newObjectExpression->args as $argIndex => $e) { + + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; + $resultAliasMap = $this->scalarResultAliasMap; + + switch (true) { + case $e instanceof AST\NewObjectExpression: + + $sqlSelectExpressions[] = $e->dispatch($this); + + break; + + default: + $sqlSelectExpressions[] = $e->dispatch($this) . ' AS ' . $columnAlias; + break; + } + + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + + $this->rsm->newObjectMappings[$columnAlias] = array( + 'className' => $newObjectExpression->className, + 'objIndex' => $objIndex, + 'argIndex' => $argIndex + ); + } + + return implode(',', $sqlSelectExpressions); + } + /** * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. * diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 6291427c0..76fa8656b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -129,7 +129,11 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase new Doctrine\Tests\Models\CMS\CmsUserDTO( u.name, e.email, - new Doctrine\Tests\Models\CMS\CmsUserDTO(a.country, a.city, a.zip) + new Doctrine\Tests\Models\CMS\CmsUserDTO( + a.country, + a.city, + a.zip + ) ) FROM Doctrine\Tests\Models\CMS\CmsUser u From b19e4a6440b0b16a4063752a2b2e5394640596dc Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 9 Jul 2012 18:56:16 -0300 Subject: [PATCH 09/22] support arithmetic expression and aggregate functions --- lib/Doctrine/ORM/Query/Parser.php | 29 ++-- .../Doctrine/Tests/Models/CMS/CmsUserDTO.php | 10 +- .../Tests/ORM/Functional/NewOperatorTest.php | 137 +++++++++++++++++- 3 files changed, 155 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 4b577af96..376e51369 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1683,20 +1683,25 @@ class Parser case ($this->_lexer->lookahead['type'] === Lexer::T_NEW): return $this->NewObjectExpression(); - case ($this->_isFunction()): - // SUM(u.id) + COUNT(u.id) - if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) { - return new AST\SimpleSelectExpression($this->ScalarExpression()); + default: + if ( ! ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead))) { + $this->syntaxError(); } - // COUNT(u.id) - if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { - return new AST\SimpleSelectExpression($this->AggregateExpression()); - } - // IDENTITY(u) - return new AST\SimpleSelectExpression($this->FunctionDeclaration()); - } - $this->semanticalError("Unsupported expression"); + // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) + $this->_lexer->peek(); // "(" + $peek = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } } /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php index 974c69e7d..0a07d26c1 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php @@ -7,11 +7,13 @@ class CmsUserDTO public $name; public $email; public $address; + public $phonenumbers; - public function __construct($name = null, $email = null, $address = null) + public function __construct($name = null, $email = null, $address = null, $phonenumbers = null) { - $this->name = $name; - $this->email = $email; - $this->address = $address; + $this->name = $name; + $this->email = $email; + $this->address = $address; + $this->phonenumbers = $phonenumbers; } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 76fa8656b..3d563cb22 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -40,12 +40,18 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u1->setEmail(new CmsEmail()); $u1->setAddress(new CmsAddress()); + $u1->addPhonenumber(new CmsPhonenumber()); $u2->setEmail(new CmsEmail()); $u2->setAddress(new CmsAddress()); + $u2->addPhonenumber(new CmsPhonenumber()); + $u2->addPhonenumber(new CmsPhonenumber()); $u3->setEmail(new CmsEmail()); $u3->setAddress(new CmsAddress()); + $u3->addPhonenumber(new CmsPhonenumber()); + $u3->addPhonenumber(new CmsPhonenumber()); + $u3->addPhonenumber(new CmsPhonenumber()); $u1->name = 'Test 1'; $u1->username = '1test'; @@ -54,6 +60,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u1->address->zip = '111111111'; $u1->address->city = 'Some City 1'; $u1->address->country = 'Some Country 2'; + $u1->phonenumbers[0]->phonenumber = "(11) 1111-1111"; $u2->name = 'Test 2'; $u2->username = '2test'; @@ -62,6 +69,8 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u2->address->zip = '222222222'; $u2->address->city = 'Some City 2'; $u2->address->country = 'Some Country 2'; + $u2->phonenumbers[0]->phonenumber = "(22) 1111-1111"; + $u2->phonenumbers[1]->phonenumber = "(22) 2222-2222"; $u3->name = 'Test 3'; $u3->username = '3test'; @@ -70,6 +79,9 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u3->address->zip = '33333333'; $u3->address->city = 'Some City 3'; $u3->address->country = 'Some Country 3'; + $u3->phonenumbers[0]->phonenumber = "(33) 1111-1111"; + $u3->phonenumbers[1]->phonenumber = "(33) 2222-2222"; + $u3->phonenumbers[2]->phonenumber = "(33) 3333-3333"; $this->_em->persist($u1); $this->_em->persist($u2); @@ -96,8 +108,8 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase u.email e JOIN u.address a - ORDER - BY u.name"; + ORDER BY + u.name"; $query = $this->_em->createQuery($dql); $result = $query->getResult(); @@ -141,8 +153,8 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase u.email e JOIN u.address a - ORDER - BY u.name"; + ORDER BY + u.name"; $query = $this->_em->createQuery($dql); $result = $query->getResult(); @@ -157,6 +169,121 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testShouldSupportAggregateFunctions() { - $this->markTestIncomplete(); + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city, + COUNT(p) + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals($this->fixtures[0]->email->email, $result[0]->email); + $this->assertEquals($this->fixtures[1]->email->email, $result[1]->email); + $this->assertEquals($this->fixtures[2]->email->email, $result[2]->email); + + $this->assertEquals($this->fixtures[0]->address->city, $result[0]->address); + $this->assertEquals($this->fixtures[1]->address->city, $result[1]->address); + $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); + + $this->assertEquals( + (count($this->fixtures[0]->phonenumbers)), + $result[0]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[1]->phonenumbers)), + $result[1]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[2]->phonenumbers)), + $result[2]->phonenumbers + ); + } + + public function testShouldSupportArithmeticExpression() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city, + COUNT(p) + u.id + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals($this->fixtures[0]->email->email, $result[0]->email); + $this->assertEquals($this->fixtures[1]->email->email, $result[1]->email); + $this->assertEquals($this->fixtures[2]->email->email, $result[2]->email); + + $this->assertEquals($this->fixtures[0]->address->city, $result[0]->address); + $this->assertEquals($this->fixtures[1]->address->city, $result[1]->address); + $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); + + $this->assertEquals( + (count($this->fixtures[0]->phonenumbers) + $this->fixtures[0]->id), + $result[0]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[1]->phonenumbers) + $this->fixtures[1]->id), + $result[1]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[2]->phonenumbers) + $this->fixtures[2]->id), + $result[2]->phonenumbers + ); } } \ No newline at end of file From af2f556fd352025d3f3356d3680c172021017d66 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 21 Jul 2012 18:07:56 -0300 Subject: [PATCH 10/22] small refactory --- lib/Doctrine/ORM/Query/SqlWalker.php | 9 +++------ tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index c6eb8e2cb..9c8f870e6 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -82,7 +82,7 @@ class SqlWalker implements TreeWalker * * @var integer */ - private $newObjectCounter; + private $newObjectCounter = 0; /** * @var ParserResult @@ -1409,15 +1409,12 @@ class SqlWalker implements TreeWalker $objIndex = $this->newObjectCounter ++; foreach ($newObjectExpression->args as $argIndex => $e) { - $resultAlias = $this->scalarResultCounter++; - $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; - $resultAliasMap = $this->scalarResultAliasMap; + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; switch (true) { case $e instanceof AST\NewObjectExpression: - $sqlSelectExpressions[] = $e->dispatch($this); - break; default: diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 3d563cb22..a7668df0d 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -141,7 +141,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase new Doctrine\Tests\Models\CMS\CmsUserDTO( u.name, e.email, - new Doctrine\Tests\Models\CMS\CmsUserDTO( + new Doctrine\Tests\Models\CMS\CmsAddressDTO( a.country, a.city, a.zip @@ -186,7 +186,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase JOIN u.phonenumbers p GROUP BY - u + u, e, a ORDER BY u.name"; From ddb2651691453d8ad2336420b1182d7b6cb7b750 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 10 Aug 2012 21:36:47 -0300 Subject: [PATCH 11/22] fix tests on postgres --- tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index a7668df0d..d1a4f3ad8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -246,7 +246,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase JOIN u.phonenumbers p GROUP BY - u + u, e, a ORDER BY u.name"; From e5e45a3a5c5bb817812011a08c084c98fe4179b7 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 12:56:47 -0300 Subject: [PATCH 12/22] test sql generation --- lib/Doctrine/ORM/Query/Parser.php | 10 ++--- lib/Doctrine/ORM/Query/SqlWalker.php | 9 +++-- .../Tests/ORM/Functional/NewOperatorTest.php | 38 ------------------- .../ORM/Query/SelectSqlGenerationTest.php | 12 +++++- 4 files changed, 20 insertions(+), 49 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 376e51369..d8e982a75 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -31,6 +31,7 @@ use Doctrine\ORM\Mapping\ClassMetadata; * @author Jonathan Wage * @author Roman Borschel * @author Janne Vanhala + * @author Fabio B. Silva */ class Parser { @@ -1641,12 +1642,12 @@ class Parser $className = $this->_lexer->token['value']; if ( ! class_exists($className, true)) { - $this->semanticalError("Class '$className' is not defined.", $this->_lexer->token); + $this->semanticalError("Class \"$className\" is not defined.", $this->_lexer->token); } $class = new \ReflectionClass($className); if($class->getConstructor() === null) { - $this->semanticalError("Class '$className' has not a valid contructor.", $this->_lexer->token); + $this->semanticalError("Class \"$className\" has not a valid contructor.", $this->_lexer->token); } $this->match(Lexer::T_OPEN_PARENTHESIS); @@ -1661,7 +1662,7 @@ class Parser $this->match(Lexer::T_CLOSE_PARENTHESIS); if($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { - $this->semanticalError("Number of arguments does not match definition.", $this->_lexer->token); + $this->semanticalError("Number of arguments does not match.", $this->_lexer->token); } return new AST\NewObjectExpression($className, $args);; @@ -1680,9 +1681,6 @@ class Parser return new AST\SimpleSelectExpression($expression); - case ($this->_lexer->lookahead['type'] === Lexer::T_NEW): - return $this->NewObjectExpression(); - default: if ( ! ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead))) { $this->syntaxError(); diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 9c8f870e6..71b88ec5d 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -34,6 +34,7 @@ use Doctrine\DBAL\LockMode, * @author Roman Borschel * @author Benjamin Eberlei * @author Alexander + * @author Fabio B. Silva * @since 2.0 * @todo Rename: SQLWalker */ @@ -1410,7 +1411,7 @@ class SqlWalker implements TreeWalker foreach ($newObjectExpression->args as $argIndex => $e) { $resultAlias = $this->scalarResultCounter++; - $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; + $columnAlias = $this->getSQLColumnAlias('sclr'); switch (true) { case $e instanceof AST\NewObjectExpression: @@ -1418,13 +1419,13 @@ class SqlWalker implements TreeWalker break; default: - $sqlSelectExpressions[] = $e->dispatch($this) . ' AS ' . $columnAlias; + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; break; } $this->scalarResultAliasMap[$resultAlias] = $columnAlias; - $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + $this->rsm->addScalarResult($columnAlias, $resultAlias); $this->rsm->newObjectMappings[$columnAlias] = array( 'className' => $newObjectExpression->className, @@ -1433,7 +1434,7 @@ class SqlWalker implements TreeWalker ); } - return implode(',', $sqlSelectExpressions); + return implode(', ', $sqlSelectExpressions); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index d1a4f3ad8..7f56bc6c5 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -2,12 +2,8 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\Common\Collections\ArrayCollection; - -use Doctrine\DBAL\Connection; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsEmail; -use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsPhonenumber; @@ -133,40 +129,6 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); } - public function testShouldSupportNestedOperators() - { - $this->markTestIncomplete(); - $dql = " - SELECT - new Doctrine\Tests\Models\CMS\CmsUserDTO( - u.name, - e.email, - new Doctrine\Tests\Models\CMS\CmsAddressDTO( - a.country, - a.city, - a.zip - ) - ) - FROM - Doctrine\Tests\Models\CMS\CmsUser u - JOIN - u.email e - JOIN - u.address a - ORDER BY - u.name"; - - $query = $this->_em->createQuery($dql); - $result = $query->getResult(); - - $this->assertCount(3, $result); - - $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); - $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); - $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); - - } - public function testShouldSupportAggregateFunctions() { $dql = " diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index b9ec036ee..fdedb921a 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1562,7 +1562,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', - 'SELECT c0_.name AS sclr01, c1_.email AS sclr12, c2_.city AS sclr23 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + ); + + $this->assertSqlGeneration( + 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', + 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' + ); + + $this->assertSqlGeneration( + 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p) + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', + 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) + c0_.id AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' ); } From f0403a539474ac7453bdac9cacb12814bad4be96 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 14:01:57 -0300 Subject: [PATCH 13/22] test literal values --- .../ORM/Query/AST/NewObjectExpression.php | 2 +- lib/Doctrine/ORM/Query/Parser.php | 32 +---- lib/Doctrine/ORM/Query/SqlWalker.php | 30 ++++- .../Tests/ORM/Functional/NewOperatorTest.php | 110 ++++++++++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 5 + 5 files changed, 149 insertions(+), 30 deletions(-) diff --git a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php index c92481a62..5ddaf0a5e 100644 --- a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php +++ b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -20,7 +20,7 @@ namespace Doctrine\ORM\Query\AST; /** - * NewObjectExpression ::= "NEW" IdentificationVariable "(" SelectExpression {"," SelectExpression}* ")" + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index d8e982a75..a245afebb 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1669,37 +1669,15 @@ class Parser } /** + * NewObjectArg ::= ScalarExpression + * + * @TODO - Maybe you should support other expressions and nested "new" operator + * * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression */ public function NewObjectArg() { - $peek = $this->_lexer->glimpse(); - - switch (true) { - case ($peek['type'] === Lexer::T_DOT): - $expression = $this->StateFieldPathExpression(); - - return new AST\SimpleSelectExpression($expression); - - default: - if ( ! ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead))) { - $this->syntaxError(); - } - - // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) - $this->_lexer->peek(); // "(" - $peek = $this->_peekBeyondClosingParenthesis(); - - if ($this->_isMathOperator($peek)) { - return $this->SimpleArithmeticExpression(); - } - - if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { - return $this->AggregateExpression(); - } - - return $this->FunctionDeclaration(); - } + return $this->ScalarExpression(); } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 71b88ec5d..a24b92836 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1405,9 +1405,9 @@ class SqlWalker implements TreeWalker */ public function walkNewObject($newObjectExpression) { - $sqlSelectExpressions = array(); $objIndex = $this->newObjectCounter ++; + foreach ($newObjectExpression->args as $argIndex => $e) { $resultAlias = $this->scalarResultCounter++; @@ -1424,8 +1424,34 @@ class SqlWalker implements TreeWalker } + switch (true) { + case ($e instanceof AST\PathExpression): + $fieldName = $e->field; + $dqlAlias = $e->identificationVariable; + $qComp = $this->queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + $fieldType = $class->getTypeOfField($fieldName); + break; + + case ($e instanceof AST\Literal): + switch ($e->type) { + case AST\Literal::BOOLEAN: + $fieldType = 'boolean'; + break; + + case AST\Literal::NUMERIC: + $fieldType = is_float($e->value) ? 'float' : 'integer'; + break; + } + break; + + default: + $fieldType = 'string'; + break; + } + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; - $this->rsm->addScalarResult($columnAlias, $resultAlias); + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); $this->rsm->newObjectMappings[$columnAlias] = array( 'className' => $newObjectExpression->className, diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 7f56bc6c5..7797445b9 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -129,6 +129,116 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); } + public function testShouldSupportLiteral() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + 'fabio.bat.silva@gmail.com', + FALSE, + 123 + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u, e, a + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals('fabio.bat.silva@gmail.com', $result[0]->email); + $this->assertEquals('fabio.bat.silva@gmail.com', $result[1]->email); + $this->assertEquals('fabio.bat.silva@gmail.com', $result[2]->email); + + $this->assertEquals(false, $result[0]->address); + $this->assertEquals(false, $result[1]->address); + $this->assertEquals(false, $result[2]->address); + + $this->assertEquals(123, $result[0]->phonenumbers); + $this->assertEquals(123, $result[1]->phonenumbers); + $this->assertEquals(123, $result[2]->phonenumbers); + } + + public function testShouldSupportSimpleArithmeticExpression() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city, + a.id + u.id + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u, e, a + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals($this->fixtures[0]->email->email, $result[0]->email); + $this->assertEquals($this->fixtures[1]->email->email, $result[1]->email); + $this->assertEquals($this->fixtures[2]->email->email, $result[2]->email); + + $this->assertEquals($this->fixtures[0]->address->city, $result[0]->address); + $this->assertEquals($this->fixtures[1]->address->city, $result[1]->address); + $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); + + $this->assertEquals( + ($this->fixtures[0]->address->id + $this->fixtures[0]->id), + $result[0]->phonenumbers + ); + + $this->assertEquals( + ($this->fixtures[1]->address->id + $this->fixtures[1]->id), + $result[1]->phonenumbers + ); + + $this->assertEquals( + ($this->fixtures[2]->address->id + $this->fixtures[2]->id), + $result[2]->phonenumbers + ); + } + public function testShouldSupportAggregateFunctions() { $dql = " diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index fdedb921a..573621d25 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1565,6 +1565,11 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' ); + $this->assertSqlGeneration( + 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.id + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', + "SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.id + c0_.id AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id" + ); + $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' From 6844116b94e8aa6fac83220397602446554f1b25 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 16:37:09 -0300 Subject: [PATCH 14/22] test case expression --- .../Tests/ORM/Functional/NewOperatorTest.php | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 7797445b9..1b85b485b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -129,7 +129,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); } - public function testShouldSupportLiteral() + public function testShouldSupportLiteralExpression() { $dql = " SELECT @@ -179,6 +179,46 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(123, $result[2]->phonenumbers); } + public function testShouldSupportCaseExpression() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + CASE WHEN (e.email = 'email@test1.com') THEN 'TEST1' ELSE 'OTHER_TEST' END + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u, e, a + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals('TEST1', $result[0]->email); + $this->assertEquals('OTHER_TEST', $result[1]->email); + $this->assertEquals('OTHER_TEST', $result[2]->email); + } + public function testShouldSupportSimpleArithmeticExpression() { $dql = " From 4dca27962eff772dae6048801c7362123d022b21 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 21:22:16 -0300 Subject: [PATCH 15/22] support multiple operators --- .../Internal/Hydration/AbstractHydrator.php | 8 ++- .../ORM/Internal/Hydration/ObjectHydrator.php | 28 ++++++---- .../Tests/ORM/Functional/NewOperatorTest.php | 52 +++++++++++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 20 ++++--- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index b2c6b9f5e..f8b7607ca 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -244,10 +244,14 @@ abstract class AbstractHydrator } if (isset($cache[$key]['isNewObjectParameter'])) { + $class = $cache[$key]['class']; $argIndex = $cache[$key]['argIndex']; $objIndex = $cache[$key]['objIndex']; - $rowData['newObjects'][$objIndex]['class'] = $cache[$key]['class']; - $rowData['newObjects'][$objIndex]['args'][$argIndex] = $cache[$key]['fieldName']; + $value = $cache[$key]['type'] + ->convertToPHPValue($value, $this->_platform); + + $rowData['newObjects'][$objIndex]['class'] = $class; + $rowData['newObjects'][$objIndex]['args'][$argIndex] = $value; } if (isset($cache[$key]['isScalar'])) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 7ab624ec0..e311507db 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -389,8 +389,8 @@ class ObjectHydrator extends AbstractHydrator } $indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); - $index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; - $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; + $argIndex = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; + $indexIsValid = $argIndex !== false ? isset($reflFieldValue[$argIndex]) : false; if ( ! $indexExists || ! $indexIsValid) { if (isset($this->_existingCollections[$collKey])) { @@ -417,7 +417,7 @@ class ObjectHydrator extends AbstractHydrator } } else { // Update result pointer - $this->_resultPointers[$dqlAlias] = $reflFieldValue[$index]; + $this->_resultPointers[$dqlAlias] = $reflFieldValue[$argIndex]; } } else if ( ! $reflFieldValue) { $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); @@ -517,9 +517,9 @@ class ObjectHydrator extends AbstractHydrator } else { // Update result pointer - $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; - $this->_resultPointers[$dqlAlias] = $result[$index]; - $resultKey = $index; + $argIndex = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; + $this->_resultPointers[$dqlAlias] = $result[$argIndex]; + $resultKey = $argIndex; /*if ($this->_rsm->isMixed) { $result[] = $result[$index]; ++$this->_resultCounter; @@ -549,13 +549,19 @@ class ObjectHydrator extends AbstractHydrator $resultKey = $this->_resultCounter - 1; } - foreach ($newObjects as $newObject) { - $args = array(); + $count = count($newObjects); + + foreach ($newObjects as $objIndex => $newObject) { $class = $newObject['class']; - foreach ($newObject['args'] as $index => $name) { - $args[$index] = $result[$resultKey][$name]; + $args = $newObject['args']; + $obj = $class->newInstanceArgs($args); + + if ($count === 1) { + $result[$resultKey] = $obj; + continue; } - $result[$resultKey] = $class->newInstanceArgs($args); + + $result[$resultKey][$objIndex] = $obj; } } diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 1b85b485b..e83130e28 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -398,4 +398,56 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $result[2]->phonenumbers ); } + + public function testShouldSupportMultipleNewOperators() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email + ), + new Doctrine\Tests\Models\CMS\CmsAddressDTO( + a.country, + a.city + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0][0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1][0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2][0]); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddressDTO', $result[0][1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddressDTO', $result[1][1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddressDTO', $result[2][1]); + + $this->assertEquals($this->fixtures[0]->name, $result[0][0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1][0]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2][0]->name); + + $this->assertEquals($this->fixtures[0]->email->email, $result[0][0]->email); + $this->assertEquals($this->fixtures[1]->email->email, $result[1][0]->email); + $this->assertEquals($this->fixtures[2]->email->email, $result[2][0]->email); + + + $this->assertEquals($this->fixtures[0]->address->city, $result[0][1]->city); + $this->assertEquals($this->fixtures[1]->address->city, $result[1][1]->city); + $this->assertEquals($this->fixtures[2]->address->city, $result[2][1]->city); + + $this->assertEquals($this->fixtures[0]->address->country, $result[0][1]->country); + $this->assertEquals($this->fixtures[1]->address->country, $result[1][1]->country); + $this->assertEquals($this->fixtures[2]->address->country, $result[2][1]->country); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 573621d25..351615f42 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1561,24 +1561,30 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase public function testSupportsNewOperator() { $this->assertSqlGeneration( - 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', - 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + "SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a", + "SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id" ); $this->assertSqlGeneration( - 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.id + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', + "SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.id + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a", "SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.id + c0_.id AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id" ); $this->assertSqlGeneration( - 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', - 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' + "SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p", + "SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id" ); $this->assertSqlGeneration( - 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p) + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', - 'SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) + c0_.id AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' + "SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p) + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p", + "SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) + c0_.id AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id" ); + + $this->assertSqlGeneration( + "SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(a.id, a.country, a.city), new Doctrine\Tests\Models\CMS\CmsAddressDTO(u.name, e.email) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name", + "SELECT c0_.id AS sclr0, c0_.country AS sclr1, c0_.city AS sclr2, c1_.name AS sclr3, c2_.email AS sclr4 FROM cms_users c1_ INNER JOIN cms_emails c2_ ON c1_.email_id = c2_.id INNER JOIN cms_addresses c0_ ON c1_.id = c0_.user_id ORDER BY c1_.name ASC" + ); + } public function testCustomTypeValueSql() From 91efe10855db45b79ac33d7e5bc84366d77e93d6 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 21:38:02 -0300 Subject: [PATCH 16/22] fix some cs on ObjectHydrator --- .../ORM/Internal/Hydration/ObjectHydrator.php | 193 ++++++++++-------- 1 file changed, 110 insertions(+), 83 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index e311507db..00fa42f32 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -19,15 +19,12 @@ namespace Doctrine\ORM\Internal\Hydration; -use PDO, - Doctrine\ORM\Mapping\ClassMetadata, - Doctrine\ORM\PersistentCollection, - Doctrine\ORM\Query, - Doctrine\ORM\Event\LifecycleEventArgs, - Doctrine\ORM\Events, - Doctrine\Common\Collections\ArrayCollection, - Doctrine\Common\Collections\Collection, - Doctrine\ORM\Proxy\Proxy; +use PDO; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Query; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Proxy\Proxy; /** * The ObjectHydrator constructs an object graph out of an SQL result set. @@ -35,6 +32,7 @@ use PDO, * @since 2.0 * @author Roman Borschel * @author Guilherme Blanco + * @author Fabio B. Silva * * @internal Highly performance-sensitive code. */ @@ -43,38 +41,67 @@ class ObjectHydrator extends AbstractHydrator /* Local ClassMetadata cache to avoid going to the EntityManager all the time. * This local cache is maintained between hydration runs and not cleared. */ - private $_ce = array(); + private $ce = array(); /* The following parts are reinitialized on every hydration run. */ - private $_identifierMap; - private $_resultPointers; - private $_idTemplate; - private $_resultCounter; - private $_rootAliases = array(); - private $_initializedCollections = array(); - private $_existingCollections = array(); + /** + * @var array + */ + private $identifierMap; + + /** + * @var array + */ + private $resultPointers; + + /** + * @var array + */ + private $idTemplate; + + /** + * @var integer + */ + private $resultCounter; + + /** + * @var array + */ + private $rootAliases = array(); + + /** + * @var array + */ + private $initializedCollections = array(); + + /** + * @var array + */ + private $existingCollections = array(); - /** @override */ + /** + * @override + */ protected function prepare() { - $this->_identifierMap = - $this->_resultPointers = - $this->_idTemplate = array(); + $this->identifierMap = + $this->resultPointers = + $this->idTemplate = array(); - $this->_resultCounter = 0; + $this->resultCounter = 0; if ( ! isset($this->_hints['deferEagerLoad'])) { $this->_hints['deferEagerLoad'] = true; } foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { - $this->_identifierMap[$dqlAlias] = array(); - $this->_idTemplate[$dqlAlias] = ''; + $this->identifierMap[$dqlAlias] = array(); + $this->idTemplate[$dqlAlias] = ''; - if ( ! isset($this->_ce[$className])) { - $this->_ce[$className] = $this->_em->getClassMetadata($className); + if ( ! isset($this->ce[$className])) { + $this->ce[$className] = $this->_em->getClassMetadata($className); } // Remember which associations are "fetch joined", so that we know where to inject @@ -106,7 +133,7 @@ class ObjectHydrator extends AbstractHydrator // handle fetch-joined owning side bi-directional one-to-one associations if ($assoc['inversedBy']) { - $class = $this->_ce[$className]; + $class = $this->ce[$className]; $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) { @@ -127,10 +154,10 @@ class ObjectHydrator extends AbstractHydrator parent::cleanup(); - $this->_identifierMap = - $this->_initializedCollections = - $this->_existingCollections = - $this->_resultPointers = array(); + $this->identifierMap = + $this->initializedCollections = + $this->existingCollections = + $this->resultPointers = array(); if ($eagerLoad) { $this->_em->getUnitOfWork()->triggerEagerLoads(); @@ -150,7 +177,7 @@ class ObjectHydrator extends AbstractHydrator } // Take snapshots from all newly initialized collections - foreach ($this->_initializedCollections as $coll) { + foreach ($this->initializedCollections as $coll) { $coll->takeSnapshot(); } @@ -177,14 +204,14 @@ class ObjectHydrator extends AbstractHydrator if ( ! $value instanceof PersistentCollection) { $value = new PersistentCollection( - $this->_em, $this->_ce[$relation['targetEntity']], $value + $this->_em, $this->ce[$relation['targetEntity']], $value ); $value->setOwner($entity, $relation); $class->reflFields[$fieldName]->setValue($entity, $value); $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); - $this->_initializedCollections[$oid . $fieldName] = $value; + $this->initializedCollections[$oid . $fieldName] = $value; } else if ( isset($this->_hints[Query::HINT_REFRESH]) || isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && @@ -195,10 +222,10 @@ class ObjectHydrator extends AbstractHydrator $value->setInitialized(true); $value->unwrap()->clear(); - $this->_initializedCollections[$oid . $fieldName] = $value; + $this->initializedCollections[$oid . $fieldName] = $value; } else { // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN! - $this->_existingCollections[$oid . $fieldName] = $value; + $this->existingCollections[$oid . $fieldName] = $value; } return $value; @@ -231,13 +258,13 @@ class ObjectHydrator extends AbstractHydrator throw HydrationException::emptyDiscriminatorValue($dqlAlias); } - $className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]]; + $className = $this->ce[$className]->discriminatorMap[$data[$discrColumn]]; unset($data[$discrColumn]); } - if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) { - $this->registerManaged($this->_ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) { + $this->registerManaged($this->ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); } $this->_hints['fetchAlias'] = $dqlAlias; @@ -253,7 +280,7 @@ class ObjectHydrator extends AbstractHydrator private function _getEntityFromIdentityMap($className, array $data) { // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? - $class = $this->_ce[$className]; + $class = $this->ce[$className]; /* @var $class ClassMetadata */ if ($class->isIdentifierComposite) { @@ -283,11 +310,11 @@ class ObjectHydrator extends AbstractHydrator */ private function _getClassMetadata($className) { - if ( ! isset($this->_ce[$className])) { - $this->_ce[$className] = $this->_em->getClassMetadata($className); + if ( ! isset($this->ce[$className])) { + $this->ce[$className] = $this->_em->getClassMetadata($className); } - return $this->_ce[$className]; + return $this->ce[$className]; } /** @@ -314,7 +341,7 @@ class ObjectHydrator extends AbstractHydrator protected function hydrateRowData(array $row, array &$cache, array &$result) { // Initialize - $id = $this->_idTemplate; // initialize the id-memory + $id = $this->idTemplate; // initialize the id-memory $nonemptyComponents = array(); // Split the row data into chunks of class data. $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); @@ -326,7 +353,7 @@ class ObjectHydrator extends AbstractHydrator unset($rowData['scalars']); if (empty($rowData)) { - ++$this->_resultCounter; + ++$this->resultCounter; } } @@ -337,7 +364,7 @@ class ObjectHydrator extends AbstractHydrator unset($rowData['newObjects']); if (empty($rowData)) { - ++$this->_resultCounter; + ++$this->resultCounter; } } @@ -360,17 +387,17 @@ class ObjectHydrator extends AbstractHydrator } // Get a reference to the parent object to which the joined element belongs. - if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) { - $first = reset($this->_resultPointers); + if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) { + $first = reset($this->resultPointers); $parentObject = $first[key($first)]; - } else if (isset($this->_resultPointers[$parentAlias])) { - $parentObject = $this->_resultPointers[$parentAlias]; + } else if (isset($this->resultPointers[$parentAlias])) { + $parentObject = $this->resultPointers[$parentAlias]; } else { // Parent object of relation not found, so skip it. continue; } - $parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]]; + $parentClass = $this->ce[$this->_rsm->aliasMap[$parentAlias]]; $oid = spl_object_hash($parentObject); $relationField = $this->_rsm->relationMap[$dqlAlias]; $relation = $parentClass->associationMappings[$relationField]; @@ -382,23 +409,23 @@ class ObjectHydrator extends AbstractHydrator // PATH A: Collection-valued association if (isset($nonemptyComponents[$dqlAlias])) { $collKey = $oid . $relationField; - if (isset($this->_initializedCollections[$collKey])) { - $reflFieldValue = $this->_initializedCollections[$collKey]; - } else if ( ! isset($this->_existingCollections[$collKey])) { + if (isset($this->initializedCollections[$collKey])) { + $reflFieldValue = $this->initializedCollections[$collKey]; + } else if ( ! isset($this->existingCollections[$collKey])) { $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); } - $indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); - $argIndex = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; - $indexIsValid = $argIndex !== false ? isset($reflFieldValue[$argIndex]) : false; + $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); + $index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; if ( ! $indexExists || ! $indexIsValid) { - if (isset($this->_existingCollections[$collKey])) { + if (isset($this->existingCollections[$collKey])) { // Collection exists, only look for the element in the identity map. if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) { - $this->_resultPointers[$dqlAlias] = $element; + $this->resultPointers[$dqlAlias] = $element; } else { - unset($this->_resultPointers[$dqlAlias]); + unset($this->resultPointers[$dqlAlias]); } } else { $element = $this->_getEntity($data, $dqlAlias); @@ -406,18 +433,18 @@ class ObjectHydrator extends AbstractHydrator if (isset($this->_rsm->indexByMap[$dqlAlias])) { $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; $reflFieldValue->hydrateSet($indexValue, $element); - $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; + $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; } else { $reflFieldValue->hydrateAdd($element); $reflFieldValue->last(); - $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); + $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); } // Update result pointer - $this->_resultPointers[$dqlAlias] = $element; + $this->resultPointers[$dqlAlias] = $element; } } else { // Update result pointer - $this->_resultPointers[$dqlAlias] = $reflFieldValue[$argIndex]; + $this->resultPointers[$dqlAlias] = $reflFieldValue[$index]; } } else if ( ! $reflFieldValue) { $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); @@ -435,7 +462,7 @@ class ObjectHydrator extends AbstractHydrator $element = $this->_getEntity($data, $dqlAlias); $reflField->setValue($parentObject, $element); $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); - $targetClass = $this->_ce[$relation['targetEntity']]; + $targetClass = $this->ce[$relation['targetEntity']]; if ($relation['isOwningSide']) { //TODO: Just check hints['fetched'] here? @@ -456,7 +483,7 @@ class ObjectHydrator extends AbstractHydrator $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject); } // Update result pointer - $this->_resultPointers[$dqlAlias] = $element; + $this->resultPointers[$dqlAlias] = $element; } else { $this->_uow->setOriginalEntityProperty($oid, $relationField, null); $reflField->setValue($parentObject, null); @@ -464,12 +491,12 @@ class ObjectHydrator extends AbstractHydrator // else leave $reflFieldValue null for single-valued associations } else { // Update result pointer - $this->_resultPointers[$dqlAlias] = $reflFieldValue; + $this->resultPointers[$dqlAlias] = $reflFieldValue; } } } else { // PATH C: Its a root result element - $this->_rootAliases[$dqlAlias] = true; // Mark as root alias + $this->rootAliases[$dqlAlias] = true; // Mark as root alias $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; // if this row has a NULL value for the root result id then make it a null result. @@ -479,13 +506,13 @@ class ObjectHydrator extends AbstractHydrator } else { $result[] = null; } - $resultKey = $this->_resultCounter; - ++$this->_resultCounter; + $resultKey = $this->resultCounter; + ++$this->resultCounter; continue; } // check for existing result from the iterations before - if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { + if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) { $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias); if ($this->_rsm->isMixed) { $element = array($entityKey => $element); @@ -500,8 +527,8 @@ class ObjectHydrator extends AbstractHydrator $result[$resultKey] = $element; } else { - $resultKey = $this->_resultCounter; - ++$this->_resultCounter; + $resultKey = $this->resultCounter; + ++$this->resultCounter; if (isset($this->_hints['collection'])) { $this->_hints['collection']->hydrateAdd($element); @@ -510,16 +537,16 @@ class ObjectHydrator extends AbstractHydrator $result[] = $element; } - $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; // Update result pointer - $this->_resultPointers[$dqlAlias] = $element; + $this->resultPointers[$dqlAlias] = $element; } else { // Update result pointer - $argIndex = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; - $this->_resultPointers[$dqlAlias] = $result[$argIndex]; - $resultKey = $argIndex; + $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]]; + $this->resultPointers[$dqlAlias] = $result[$index]; + $resultKey = $index; /*if ($this->_rsm->isMixed) { $result[] = $result[$index]; ++$this->_resultCounter; @@ -534,7 +561,7 @@ class ObjectHydrator extends AbstractHydrator if (isset($this->_rsm->indexByMap['scalars'])) { $resultKey = $row[$this->_rsm->indexByMap['scalars']]; } else { - $resultKey = $this->_resultCounter - 1; + $resultKey = $this->resultCounter - 1; } } @@ -546,7 +573,7 @@ class ObjectHydrator extends AbstractHydrator // Append new object to mixed result sets if (isset($newObjects)) { if ( ! isset($resultKey) ) { - $resultKey = $this->_resultCounter - 1; + $resultKey = $this->resultCounter - 1; } $count = count($newObjects); @@ -575,11 +602,11 @@ class ObjectHydrator extends AbstractHydrator { parent::onClear($eventArgs); - $aliases = array_keys($this->_identifierMap); - $this->_identifierMap = array(); + $aliases = array_keys($this->identifierMap); + $this->identifierMap = array(); foreach ($aliases as $alias) { - $this->_identifierMap[$alias] = array(); + $this->identifierMap[$alias] = array(); } } } From 3aa8d3fdac760ca23491eef1157b76b970f52522 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 21:52:03 -0300 Subject: [PATCH 17/22] test constructor exceptions --- .../ORM/Internal/Hydration/ObjectHydrator.php | 22 +++++------ .../Tests/ORM/Functional/NewOperatorTest.php | 39 +++++++++++++++++++ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 00fa42f32..edce92f80 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -115,7 +115,7 @@ class ObjectHydrator extends AbstractHydrator } $sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]]; - $sourceClass = $this->_getClassMetadata($sourceClassName); + $sourceClass = $this->getClassMetadata($sourceClassName); $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; $this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true; @@ -192,7 +192,7 @@ class ObjectHydrator extends AbstractHydrator * @param string $fieldName The name of the field on the entity that holds the collection. * @param string $parentDqlAlias Alias of the parent fetch joining this collection. */ - private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias) + private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias) { $oid = spl_object_hash($entity); $relation = $class->associationMappings[$fieldName]; @@ -238,7 +238,7 @@ class ObjectHydrator extends AbstractHydrator * @param string $dqlAlias The DQL alias of the entity's class. * @return object The entity. */ - private function _getEntity(array $data, $dqlAlias) + private function getEntity(array $data, $dqlAlias) { $className = $this->_rsm->aliasMap[$dqlAlias]; @@ -277,7 +277,7 @@ class ObjectHydrator extends AbstractHydrator * @param array $data * @return mixed */ - private function _getEntityFromIdentityMap($className, array $data) + private function getEntityFromIdentityMap($className, array $data) { // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? $class = $this->ce[$className]; @@ -308,7 +308,7 @@ class ObjectHydrator extends AbstractHydrator * @param string $className The name of the class. * @return ClassMetadata */ - private function _getClassMetadata($className) + private function getClassMetadata($className) { if ( ! isset($this->ce[$className])) { $this->ce[$className] = $this->_em->getClassMetadata($className); @@ -412,7 +412,7 @@ class ObjectHydrator extends AbstractHydrator if (isset($this->initializedCollections[$collKey])) { $reflFieldValue = $this->initializedCollections[$collKey]; } else if ( ! isset($this->existingCollections[$collKey])) { - $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); } $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); @@ -422,13 +422,13 @@ class ObjectHydrator extends AbstractHydrator if ( ! $indexExists || ! $indexIsValid) { if (isset($this->existingCollections[$collKey])) { // Collection exists, only look for the element in the identity map. - if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) { + if ($element = $this->getEntityFromIdentityMap($entityName, $data)) { $this->resultPointers[$dqlAlias] = $element; } else { unset($this->resultPointers[$dqlAlias]); } } else { - $element = $this->_getEntity($data, $dqlAlias); + $element = $this->getEntity($data, $dqlAlias); if (isset($this->_rsm->indexByMap[$dqlAlias])) { $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; @@ -447,7 +447,7 @@ class ObjectHydrator extends AbstractHydrator $this->resultPointers[$dqlAlias] = $reflFieldValue[$index]; } } else if ( ! $reflFieldValue) { - $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) { $reflFieldValue->setInitialized(true); } @@ -459,7 +459,7 @@ class ObjectHydrator extends AbstractHydrator // we only need to take action if this value is null, // we refresh the entity or its an unitialized proxy. if (isset($nonemptyComponents[$dqlAlias])) { - $element = $this->_getEntity($data, $dqlAlias); + $element = $this->getEntity($data, $dqlAlias); $reflField->setValue($parentObject, $element); $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); $targetClass = $this->ce[$relation['targetEntity']]; @@ -513,7 +513,7 @@ class ObjectHydrator extends AbstractHydrator // check for existing result from the iterations before if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) { - $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias); + $element = $this->getEntity($rowData[$dqlAlias], $dqlAlias); if ($this->_rsm->isMixed) { $element = array($entityKey => $element); } diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index e83130e28..2109482fa 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -450,4 +450,43 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($this->fixtures[1]->address->country, $result[1][1]->country); $this->assertEquals($this->fixtures[2]->address->country, $result[2][1]->country); } + + /** + * @expectedException Doctrine\ORM\Query\QueryException + * @expectedExceptionMessage [Semantical Error] line 0, col 11 near '\InvalidClass(u.name)': Error: Class "\InvalidClass" is not defined. + */ + public function testInvalidClassException() + { + $dql = "SELECT new \InvalidClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u"; + $this->_em->createQuery($dql)->getResult(); + } + + /** + * @expectedException Doctrine\ORM\Query\QueryException + * @expectedExceptionMessage [Semantical Error] line 0, col 11 near '\stdClass(u.name)': Error: Class "\stdClass" has not a valid contructor. + */ + public function testInvalidClassConstructorException() + { + $dql = "SELECT new \stdClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u"; + $this->_em->createQuery($dql)->getResult(); + } + + /** + * @expectedException Doctrine\ORM\Query\QueryException + * @expectedExceptionMessage [Semantical Error] line 0, col 68 near ') FROM Doctrine\Tests\Models\CMS\CmsUser': Error: Number of arguments does not match. + */ + public function testInvalidClassWithoutConstructorException() + { + $dql = "SELECT new Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u"; + $this->_em->createQuery($dql)->getResult(); + } +} + +class ClassWithTooMuchArgs +{ + public function __construct($foo, $bar) + { + $this->foo = $foo; + $this->bor = $bar; + } } \ No newline at end of file From de93983dff875c898e953a660cae9ea4661cdaa1 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 11 Aug 2012 23:08:34 -0300 Subject: [PATCH 18/22] assume entity namespace when not given --- lib/Doctrine/ORM/Query/Parser.php | 776 ++++++++++-------- .../Tests/ORM/Functional/NewOperatorTest.php | 36 +- 2 files changed, 460 insertions(+), 352 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index a245afebb..22605a3e0 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -67,75 +67,96 @@ class Parser 'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction', ); - /** + /* * Expressions that were encountered during parsing of identifiers and expressions * and still need to be validated. */ - private $_deferredIdentificationVariables = array(); - private $_deferredPartialObjectExpressions = array(); - private $_deferredPathExpressions = array(); - private $_deferredResultVariables = array(); + + /** + * @var array + */ + private $deferredIdentificationVariables = array(); + + /** + * @var array + */ + private $deferredPartialObjectExpressions = array(); + + /** + * @var array + */ + private $deferredPathExpressions = array(); + + /** + * @var array + */ + private $deferredResultVariables = array(); + + /** + * @var array + */ + private $deferredNewObjectExpressions = array(); /** * The lexer. * * @var \Doctrine\ORM\Query\Lexer */ - private $_lexer; + private $lexer; /** * The parser result. * * @var \Doctrine\ORM\Query\ParserResult */ - private $_parserResult; + private $parserResult; /** * The EntityManager. * * @var \Doctrine\ORM\EntityManager */ - private $_em; + private $em; /** * The Query to parse. * * @var Query */ - private $_query; + private $query; /** * Map of declared query components in the parsed query. * * @var array */ - private $_queryComponents = array(); + private $queryComponents = array(); /** * Keeps the nesting level of defined ResultVariables * * @var integer */ - private $_nestingLevel = 0; + private $nestingLevel = 0; /** * Any additional custom tree walkers that modify the AST. * * @var array */ - private $_customTreeWalkers = array(); + private $customTreeWalkers = array(); /** * The custom last tree walker, if any, that is responsible for producing the output. * * @var TreeWalker */ - private $_customOutputWalker; + private $customOutputWalker; /** * @var array */ - private $_identVariableExpressions = array(); + private $identVariableExpressions = array(); /** * Check if a function is internally defined. Used to prevent overwriting @@ -160,10 +181,10 @@ class Parser */ public function __construct(Query $query) { - $this->_query = $query; - $this->_em = $query->getEntityManager(); - $this->_lexer = new Lexer($query->getDql()); - $this->_parserResult = new ParserResult(); + $this->query = $query; + $this->em = $query->getEntityManager(); + $this->lexer = new Lexer($query->getDql()); + $this->parserResult = new ParserResult(); } /** @@ -174,7 +195,7 @@ class Parser */ public function setCustomOutputTreeWalker($className) { - $this->_customOutputWalker = $className; + $this->customOutputWalker = $className; } /** @@ -184,7 +205,7 @@ class Parser */ public function addCustomTreeWalker($className) { - $this->_customTreeWalkers[] = $className; + $this->customTreeWalkers[] = $className; } /** @@ -194,7 +215,7 @@ class Parser */ public function getLexer() { - return $this->_lexer; + return $this->lexer; } /** @@ -204,7 +225,7 @@ class Parser */ public function getParserResult() { - return $this->_parserResult; + return $this->parserResult; } /** @@ -214,7 +235,7 @@ class Parser */ public function getEntityManager() { - return $this->_em; + return $this->em; } /** @@ -231,21 +252,25 @@ class Parser // Process any deferred validations of some nodes in the AST. // This also allows post-processing of the AST for modification purposes. - $this->_processDeferredIdentificationVariables(); + $this->processDeferredIdentificationVariables(); - if ($this->_deferredPartialObjectExpressions) { - $this->_processDeferredPartialObjectExpressions(); + if ($this->deferredPartialObjectExpressions) { + $this->processDeferredPartialObjectExpressions(); } - if ($this->_deferredPathExpressions) { - $this->_processDeferredPathExpressions($AST); + if ($this->deferredPathExpressions) { + $this->processDeferredPathExpressions($AST); } - if ($this->_deferredResultVariables) { - $this->_processDeferredResultVariables(); + if ($this->deferredResultVariables) { + $this->processDeferredResultVariables(); } - $this->_processRootEntityAliasSelected(); + if ($this->deferredNewObjectExpressions) { + $this->processDeferredNewObjectExpressions($AST); + } + + $this->processRootEntityAliasSelected(); // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot! $this->fixIdentificationVariableOrder($AST); @@ -265,14 +290,14 @@ class Parser */ public function match($token) { - $lookaheadType = $this->_lexer->lookahead['type']; + $lookaheadType = $this->lexer->lookahead['type']; // short-circuit on first condition, usually types match if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) { - $this->syntaxError($this->_lexer->getLiteral($token)); + $this->syntaxError($this->lexer->getLiteral($token)); } - $this->_lexer->moveNext(); + $this->lexer->moveNext(); } /** @@ -284,15 +309,15 @@ class Parser public function free($deep = false, $position = 0) { // WARNING! Use this method with care. It resets the scanner! - $this->_lexer->resetPosition($position); + $this->lexer->resetPosition($position); // Deep = true cleans peek and also any previously defined errors if ($deep) { - $this->_lexer->resetPeek(); + $this->lexer->resetPeek(); } - $this->_lexer->token = null; - $this->_lexer->lookahead = null; + $this->lexer->token = null; + $this->lexer->lookahead = null; } /** @@ -304,19 +329,19 @@ class Parser { $AST = $this->getAST(); - if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { - $this->_customTreeWalkers = $customWalkers; + if (($customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { + $this->customTreeWalkers = $customWalkers; } - if (($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) { - $this->_customOutputWalker = $customOutputWalker; + if (($customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) { + $this->customOutputWalker = $customOutputWalker; } // Run any custom tree walkers over the AST - if ($this->_customTreeWalkers) { - $treeWalkerChain = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents); + if ($this->customTreeWalkers) { + $treeWalkerChain = new TreeWalkerChain($this->query, $this->parserResult, $this->queryComponents); - foreach ($this->_customTreeWalkers as $walker) { + foreach ($this->customTreeWalkers as $walker) { $treeWalkerChain->addTreeWalker($walker); } @@ -335,13 +360,13 @@ class Parser } } - $outputWalkerClass = $this->_customOutputWalker ?: __NAMESPACE__ . '\SqlWalker'; - $outputWalker = new $outputWalkerClass($this->_query, $this->_parserResult, $this->_queryComponents); + $outputWalkerClass = $this->customOutputWalker ?: __NAMESPACE__ . '\SqlWalker'; + $outputWalker = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents); // Assign an SQL executor to the parser result - $this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST)); + $this->parserResult->setSqlExecutor($outputWalker->getExecutor($AST)); - return $this->_parserResult; + return $this->parserResult; } /** @@ -356,16 +381,16 @@ class Parser */ private function fixIdentificationVariableOrder($AST) { - if (count($this->_identVariableExpressions) <= 1) { + if (count($this->identVariableExpressions) <= 1) { return; } - foreach ($this->_queryComponents as $dqlAlias => $qComp) { - if ( ! isset($this->_identVariableExpressions[$dqlAlias])) { + foreach ($this->queryComponents as $dqlAlias => $qComp) { + if ( ! isset($this->identVariableExpressions[$dqlAlias])) { continue; } - $expr = $this->_identVariableExpressions[$dqlAlias]; + $expr = $this->identVariableExpressions[$dqlAlias]; $key = array_search($expr, $AST->selectClause->selectExpressions); unset($AST->selectClause->selectExpressions[$key]); @@ -385,16 +410,16 @@ class Parser public function syntaxError($expected = '', $token = null) { if ($token === null) { - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; } $tokenPos = (isset($token['position'])) ? $token['position'] : '-1'; $message = "line 0, col {$tokenPos}: Error: "; $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected '; - $message .= ($this->_lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'"; + $message .= ($this->lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'"; - throw QueryException::syntaxError($message, QueryException::dqlError($this->_query->getDQL())); + throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL())); } /** @@ -408,14 +433,14 @@ class Parser public function semanticalError($message = '', $token = null) { if ($token === null) { - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; } // Minimum exposed chars ahead of token $distance = 12; // Find a position of a final word to display in error string - $dql = $this->_query->getDql(); + $dql = $this->query->getDql(); $length = strlen($dql); $pos = $token['position'] + $distance; $pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length); @@ -427,7 +452,7 @@ class Parser // Building informative message $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message; - throw QueryException::semanticalError($message, QueryException::dqlError($this->_query->getDQL())); + throw QueryException::semanticalError($message, QueryException::dqlError($this->query->getDQL())); } /** @@ -436,9 +461,9 @@ class Parser * @param boolean $resetPeek Reset peek after finding the closing parenthesis * @return array */ - private function _peekBeyondClosingParenthesis($resetPeek = true) + private function peekBeyondClosingParenthesis($resetPeek = true) { - $token = $this->_lexer->peek(); + $token = $this->lexer->peek(); $numUnmatched = 1; while ($numUnmatched > 0 && $token !== null) { @@ -455,11 +480,11 @@ class Parser // Do nothing } - $token = $this->_lexer->peek(); + $token = $this->lexer->peek(); } if ($resetPeek) { - $this->_lexer->resetPeek(); + $this->lexer->resetPeek(); } return $token; @@ -470,7 +495,7 @@ class Parser * * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise. */ - private function _isMathOperator($token) + private function isMathOperator($token) { return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY)); } @@ -480,12 +505,12 @@ class Parser * * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise. */ - private function _isFunction() + private function isFunction() { - $peek = $this->_lexer->peek(); - $nextpeek = $this->_lexer->peek(); + $peek = $this->lexer->peek(); + $nextpeek = $this->lexer->peek(); - $this->_lexer->resetPeek(); + $this->lexer->resetPeek(); // We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function return ($peek['type'] === Lexer::T_OPEN_PARENTHESIS && $nextpeek['type'] !== Lexer::T_SELECT); @@ -496,7 +521,7 @@ class Parser * * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise. */ - private function _isAggregateFunction($tokenType) + private function isAggregateFunction($tokenType) { return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT)); } @@ -506,9 +531,9 @@ class Parser * * @return boolean */ - private function _isNextAllAnySome() + private function isNextAllAnySome() { - return in_array($this->_lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME)); + return in_array($this->lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME)); } /** @@ -517,19 +542,19 @@ class Parser * * @return void */ - private function _processDeferredIdentificationVariables() + private function processDeferredIdentificationVariables() { - foreach ($this->_deferredIdentificationVariables as $deferredItem) { + foreach ($this->deferredIdentificationVariables as $deferredItem) { $identVariable = $deferredItem['expression']; // Check if IdentificationVariable exists in queryComponents - if ( ! isset($this->_queryComponents[$identVariable])) { + if ( ! isset($this->queryComponents[$identVariable])) { $this->semanticalError( "'$identVariable' is not defined.", $deferredItem['token'] ); } - $qComp = $this->_queryComponents[$identVariable]; + $qComp = $this->queryComponents[$identVariable]; // Check if queryComponent points to an AbstractSchemaName or a ResultVariable if ( ! isset($qComp['metadata'])) { @@ -547,17 +572,79 @@ class Parser } } + /** + * Validates that the given NewObjectExpression. + * + * @param \Doctrine\ORM\Query\AST\SelectClause $AST + * @return void + */ + private function processDeferredNewObjectExpressions($AST) + { + foreach ($this->deferredNewObjectExpressions as $deferredItem) { + $expression = $deferredItem['expression']; + $token = $deferredItem['token']; + $className = $expression->className; + $args = $expression->args; + + //first from item + if ( strpos($className, '\\') === false + && ! class_exists($className) + && isset($AST->fromClause + ->identificationVariableDeclarations[0] + ->rangeVariableDeclaration + ->abstractSchemaName)) { + + $fromClassName = $AST->fromClause + ->identificationVariableDeclarations[0] + ->rangeVariableDeclaration + ->abstractSchemaName; + + if (strpos($fromClassName, '\\') !== false) { + $fromClassName = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); + $className = $fromClassName . '\\' . $className; + } + + if (class_exists($className)) { + $expression->className = $className; + } + } + + if ( ! class_exists($className)) { + $this->semanticalError(sprintf( + 'Class "%s" is not defined.', + $expression->className + ), $token); + } + + $class = new \ReflectionClass($className); + + if($class->getConstructor() === null) { + $this->semanticalError(sprintf( + 'Class "%s" has not a valid contructor.', + $className + ), $token); + } + + if($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { + $this->semanticalError(sprintf( + 'Number of arguments does not match with "%s" constructor declaration.', + $className + ), $token); + } + } + } + /** * Validates that the given PartialObjectExpression is semantically correct. * It must exist in query components list. * * @return void */ - private function _processDeferredPartialObjectExpressions() + private function processDeferredPartialObjectExpressions() { - foreach ($this->_deferredPartialObjectExpressions as $deferredItem) { + foreach ($this->deferredPartialObjectExpressions as $deferredItem) { $expr = $deferredItem['expression']; - $class = $this->_queryComponents[$expr->identificationVariable]['metadata']; + $class = $this->queryComponents[$expr->identificationVariable]['metadata']; foreach ($expr->partialFieldSet as $field) { if (isset($class->fieldMappings[$field])) { @@ -584,19 +671,19 @@ class Parser * * @return void */ - private function _processDeferredResultVariables() + private function processDeferredResultVariables() { - foreach ($this->_deferredResultVariables as $deferredItem) { + foreach ($this->deferredResultVariables as $deferredItem) { $resultVariable = $deferredItem['expression']; // Check if ResultVariable exists in queryComponents - if ( ! isset($this->_queryComponents[$resultVariable])) { + if ( ! isset($this->queryComponents[$resultVariable])) { $this->semanticalError( "'$resultVariable' is not defined.", $deferredItem['token'] ); } - $qComp = $this->_queryComponents[$resultVariable]; + $qComp = $this->queryComponents[$resultVariable]; // Check if queryComponent points to an AbstractSchemaName or a ResultVariable if ( ! isset($qComp['resultVariable'])) { @@ -626,12 +713,12 @@ class Parser * @param array $deferredItem * @param mixed $AST */ - private function _processDeferredPathExpressions($AST) + private function processDeferredPathExpressions($AST) { - foreach ($this->_deferredPathExpressions as $deferredItem) { + foreach ($this->deferredPathExpressions as $deferredItem) { $pathExpression = $deferredItem['expression']; - $qComp = $this->_queryComponents[$pathExpression->identificationVariable]; + $qComp = $this->queryComponents[$pathExpression->identificationVariable]; $class = $qComp['metadata']; if (($field = $pathExpression->field) === null) { @@ -692,16 +779,16 @@ class Parser } } - private function _processRootEntityAliasSelected() + private function processRootEntityAliasSelected() { - if ( ! count($this->_identVariableExpressions)) { + if ( ! count($this->identVariableExpressions)) { return; } $foundRootEntity = false; - foreach ($this->_identVariableExpressions as $dqlAlias => $expr) { - if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) { + foreach ($this->identVariableExpressions as $dqlAlias => $expr) { + if (isset($this->queryComponents[$dqlAlias]) && $this->queryComponents[$dqlAlias]['parent'] === null) { $foundRootEntity = true; } } @@ -720,9 +807,9 @@ class Parser */ public function QueryLanguage() { - $this->_lexer->moveNext(); + $this->lexer->moveNext(); - switch ($this->_lexer->lookahead['type']) { + switch ($this->lexer->lookahead['type']) { case Lexer::T_SELECT: $statement = $this->SelectStatement(); break; @@ -741,7 +828,7 @@ class Parser } // Check for end of string - if ($this->_lexer->lookahead !== null) { + if ($this->lexer->lookahead !== null) { $this->syntaxError('end of string'); } @@ -757,10 +844,10 @@ class Parser { $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause()); - $selectStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; - $selectStatement->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; - $selectStatement->havingClause = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; - $selectStatement->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + $selectStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $selectStatement->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $selectStatement->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $selectStatement->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; return $selectStatement; } @@ -774,7 +861,7 @@ class Parser { $updateStatement = new AST\UpdateStatement($this->UpdateClause()); - $updateStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $updateStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; return $updateStatement; } @@ -788,7 +875,7 @@ class Parser { $deleteStatement = new AST\DeleteStatement($this->DeleteClause()); - $deleteStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $deleteStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; return $deleteStatement; } @@ -802,12 +889,12 @@ class Parser { $this->match(Lexer::T_IDENTIFIER); - $identVariable = $this->_lexer->token['value']; + $identVariable = $this->lexer->token['value']; - $this->_deferredIdentificationVariables[] = array( + $this->deferredIdentificationVariables[] = array( 'expression' => $identVariable, - 'nestingLevel' => $this->_nestingLevel, - 'token' => $this->_lexer->token, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, ); return $identVariable; @@ -822,11 +909,11 @@ class Parser { $this->match(Lexer::T_IDENTIFIER); - $aliasIdentVariable = $this->_lexer->token['value']; - $exists = isset($this->_queryComponents[$aliasIdentVariable]); + $aliasIdentVariable = $this->lexer->token['value']; + $exists = isset($this->queryComponents[$aliasIdentVariable]); if ($exists) { - $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->_lexer->token); + $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->lexer->token); } return $aliasIdentVariable; @@ -841,18 +928,18 @@ class Parser { $this->match(Lexer::T_IDENTIFIER); - $schemaName = ltrim($this->_lexer->token['value'], '\\'); + $schemaName = ltrim($this->lexer->token['value'], '\\'); if (strrpos($schemaName, ':') !== false) { list($namespaceAlias, $simpleClassName) = explode(':', $schemaName); - $schemaName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + $schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; } $exists = class_exists($schemaName, true); if ( ! $exists) { - $this->semanticalError("Class '$schemaName' is not defined.", $this->_lexer->token); + $this->semanticalError("Class '$schemaName' is not defined.", $this->lexer->token); } return $schemaName; @@ -867,11 +954,11 @@ class Parser { $this->match(Lexer::T_IDENTIFIER); - $resultVariable = $this->_lexer->token['value']; - $exists = isset($this->_queryComponents[$resultVariable]); + $resultVariable = $this->lexer->token['value']; + $exists = isset($this->queryComponents[$resultVariable]); if ($exists) { - $this->semanticalError("'$resultVariable' is already defined.", $this->_lexer->token); + $this->semanticalError("'$resultVariable' is already defined.", $this->lexer->token); } return $resultVariable; @@ -886,13 +973,13 @@ class Parser { $this->match(Lexer::T_IDENTIFIER); - $resultVariable = $this->_lexer->token['value']; + $resultVariable = $this->lexer->token['value']; // Defer ResultVariable validation - $this->_deferredResultVariables[] = array( + $this->deferredResultVariables[] = array( 'expression' => $resultVariable, - 'nestingLevel' => $this->_nestingLevel, - 'token' => $this->_lexer->token, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, ); return $resultVariable; @@ -907,7 +994,7 @@ class Parser { $identVariable = $this->IdentificationVariable(); - if ( ! isset($this->_queryComponents[$identVariable])) { + if ( ! isset($this->queryComponents[$identVariable])) { $this->semanticalError( 'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.' ); @@ -916,10 +1003,10 @@ class Parser $this->match(Lexer::T_DOT); $this->match(Lexer::T_IDENTIFIER); - $field = $this->_lexer->token['value']; + $field = $this->lexer->token['value']; // Validate association field - $qComp = $this->_queryComponents[$identVariable]; + $qComp = $this->queryComponents[$identVariable]; $class = $qComp['metadata']; if ( ! $class->hasAssociation($field)) { @@ -943,21 +1030,21 @@ class Parser $identVariable = $this->IdentificationVariable(); $field = null; - if ($this->_lexer->isNextToken(Lexer::T_DOT)) { + if ($this->lexer->isNextToken(Lexer::T_DOT)) { $this->match(Lexer::T_DOT); $this->match(Lexer::T_IDENTIFIER); - $field = $this->_lexer->token['value']; + $field = $this->lexer->token['value']; } // Creating AST node $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field); // Defer PathExpression validation if requested to be defered - $this->_deferredPathExpressions[] = array( + $this->deferredPathExpressions[] = array( 'expression' => $pathExpr, - 'nestingLevel' => $this->_nestingLevel, - 'token' => $this->_lexer->token, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, ); return $pathExpr; @@ -1030,7 +1117,7 @@ class Parser $this->match(Lexer::T_SELECT); // Check for DISTINCT - if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { $this->match(Lexer::T_DISTINCT); $isDistinct = true; @@ -1040,7 +1127,7 @@ class Parser $selectExpressions = array(); $selectExpressions[] = $this->SelectExpression(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $selectExpressions[] = $this->SelectExpression(); @@ -1059,7 +1146,7 @@ class Parser $isDistinct = false; $this->match(Lexer::T_SELECT); - if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { $this->match(Lexer::T_DISTINCT); $isDistinct = true; @@ -1076,16 +1163,16 @@ class Parser public function UpdateClause() { $this->match(Lexer::T_UPDATE); - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; $abstractSchemaName = $this->AbstractSchemaName(); - if ($this->_lexer->isNextToken(Lexer::T_AS)) { + if ($this->lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); } $aliasIdentificationVariable = $this->AliasIdentificationVariable(); - $class = $this->_em->getClassMetadata($abstractSchemaName); + $class = $this->em->getClassMetadata($abstractSchemaName); // Building queryComponent $queryComponent = array( @@ -1093,18 +1180,18 @@ class Parser 'parent' => null, 'relation' => null, 'map' => null, - 'nestingLevel' => $this->_nestingLevel, + 'nestingLevel' => $this->nestingLevel, 'token' => $token, ); - $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; $this->match(Lexer::T_SET); $updateItems = array(); $updateItems[] = $this->UpdateItem(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $updateItems[] = $this->UpdateItem(); @@ -1125,21 +1212,21 @@ class Parser { $this->match(Lexer::T_DELETE); - if ($this->_lexer->isNextToken(Lexer::T_FROM)) { + if ($this->lexer->isNextToken(Lexer::T_FROM)) { $this->match(Lexer::T_FROM); } - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; $deleteClause = new AST\DeleteClause($this->AbstractSchemaName()); - if ($this->_lexer->isNextToken(Lexer::T_AS)) { + if ($this->lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); } $aliasIdentificationVariable = $this->AliasIdentificationVariable(); $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable; - $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName); + $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); // Building queryComponent $queryComponent = array( @@ -1147,11 +1234,11 @@ class Parser 'parent' => null, 'relation' => null, 'map' => null, - 'nestingLevel' => $this->_nestingLevel, + 'nestingLevel' => $this->nestingLevel, 'token' => $token, ); - $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; return $deleteClause; } @@ -1168,7 +1255,7 @@ class Parser $identificationVariableDeclarations = array(); $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); @@ -1189,7 +1276,7 @@ class Parser $identificationVariables = array(); $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); @@ -1234,7 +1321,7 @@ class Parser $groupByItems = array($this->GroupByItem()); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $groupByItems[] = $this->GroupByItem(); @@ -1256,7 +1343,7 @@ class Parser $orderByItems = array(); $orderByItems[] = $this->OrderByItem(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $orderByItems[] = $this->OrderByItem(); @@ -1273,17 +1360,17 @@ class Parser public function Subselect() { // Increase query nesting level - $this->_nestingLevel++; + $this->nestingLevel++; $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause()); - $subselect->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; - $subselect->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; - $subselect->havingClause = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; - $subselect->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + $subselect->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $subselect->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $subselect->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $subselect->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; // Decrease query nesting level - $this->_nestingLevel--; + $this->nestingLevel--; return $subselect; } @@ -1312,20 +1399,20 @@ class Parser public function GroupByItem() { // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression - $glimpse = $this->_lexer->glimpse(); + $glimpse = $this->lexer->glimpse(); if ($glimpse['type'] === Lexer::T_DOT) { return $this->SingleValuedPathExpression(); } // Still need to decide between IdentificationVariable or ResultVariable - $lookaheadValue = $this->_lexer->lookahead['value']; + $lookaheadValue = $this->lexer->lookahead['value']; - if ( ! isset($this->_queryComponents[$lookaheadValue])) { + if ( ! isset($this->queryComponents[$lookaheadValue])) { $this->semanticalError('Cannot group by undefined identification or result variable.'); } - return (isset($this->_queryComponents[$lookaheadValue]['metadata'])) + return (isset($this->queryComponents[$lookaheadValue]['metadata'])) ? $this->IdentificationVariable() : $this->ResultVariable(); } @@ -1341,15 +1428,15 @@ class Parser public function OrderByItem() { - $this->_lexer->peek(); // lookahead => '.' - $this->_lexer->peek(); // lookahead => token after '.' - $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.' - $this->_lexer->resetPeek(); - $glimpse = $this->_lexer->glimpse(); + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + $this->lexer->resetPeek(); + $glimpse = $this->lexer->glimpse(); switch (true) { - case ($this->_isMathOperator($peek)): + case ($this->isMathOperator($peek)): $expr = $this->SimpleArithmeticExpression(); break; @@ -1357,7 +1444,7 @@ class Parser $expr = $this->SingleValuedPathExpression(); break; - case ($this->_lexer->peek() && $this->_isMathOperator($this->_peekBeyondClosingParenthesis())): + case ($this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis())): $expr = $this->ScalarExpression(); break; @@ -1371,12 +1458,12 @@ class Parser $item = new AST\OrderByItem($expr); switch (true) { - case ($this->_lexer->isNextToken(Lexer::T_DESC)): + case ($this->lexer->isNextToken(Lexer::T_DESC)): $this->match(Lexer::T_DESC); $type = 'DESC'; break; - case ($this->_lexer->isNextToken(Lexer::T_ASC)): + case ($this->lexer->isNextToken(Lexer::T_ASC)): $this->match(Lexer::T_ASC); break; @@ -1402,16 +1489,16 @@ class Parser */ public function NewValue() { - if ($this->_lexer->isNextToken(Lexer::T_NULL)) { + if ($this->lexer->isNextToken(Lexer::T_NULL)) { $this->match(Lexer::T_NULL); return null; } - if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { $this->match(Lexer::T_INPUT_PARAMETER); - return new AST\InputParameter($this->_lexer->token['value']); + return new AST\InputParameter($this->lexer->token['value']); } return $this->SimpleArithmeticExpression(); @@ -1425,13 +1512,13 @@ class Parser public function IdentificationVariableDeclaration() { $rangeVariableDeclaration = $this->RangeVariableDeclaration(); - $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; $joins = array(); while ( - $this->_lexer->isNextToken(Lexer::T_LEFT) || - $this->_lexer->isNextToken(Lexer::T_INNER) || - $this->_lexer->isNextToken(Lexer::T_JOIN) + $this->lexer->isNextToken(Lexer::T_LEFT) || + $this->lexer->isNextToken(Lexer::T_INNER) || + $this->lexer->isNextToken(Lexer::T_JOIN) ) { $joins[] = $this->Join(); } @@ -1449,7 +1536,7 @@ class Parser */ public function SubselectIdentificationVariableDeclaration() { - $this->_lexer->glimpse(); + $this->lexer->glimpse(); /* NOT YET IMPLEMENTED! @@ -1479,20 +1566,20 @@ class Parser $joinType = AST\Join::JOIN_TYPE_INNER; switch (true) { - case ($this->_lexer->isNextToken(Lexer::T_LEFT)): + case ($this->lexer->isNextToken(Lexer::T_LEFT)): $this->match(Lexer::T_LEFT); $joinType = AST\Join::JOIN_TYPE_LEFT; // Possible LEFT OUTER join - if ($this->_lexer->isNextToken(Lexer::T_OUTER)) { + if ($this->lexer->isNextToken(Lexer::T_OUTER)) { $this->match(Lexer::T_OUTER); $joinType = AST\Join::JOIN_TYPE_LEFTOUTER; } break; - case ($this->_lexer->isNextToken(Lexer::T_INNER)): + case ($this->lexer->isNextToken(Lexer::T_INNER)): $this->match(Lexer::T_INNER); break; @@ -1502,7 +1589,7 @@ class Parser $this->match(Lexer::T_JOIN); - $next = $this->_lexer->glimpse(); + $next = $this->lexer->glimpse(); $joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration(); @@ -1511,7 +1598,7 @@ class Parser $join = new AST\Join($joinType, $joinDeclaration); // Check for ad-hoc Join conditions - if ($this->_lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) { + if ($this->lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) { $this->match(Lexer::T_WITH); $join->conditionalExpression = $this->ConditionalExpression(); @@ -1529,13 +1616,13 @@ class Parser { $abstractSchemaName = $this->AbstractSchemaName(); - if ($this->_lexer->isNextToken(Lexer::T_AS)) { + if ($this->lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); } - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; $aliasIdentificationVariable = $this->AliasIdentificationVariable(); - $classMetadata = $this->_em->getClassMetadata($abstractSchemaName); + $classMetadata = $this->em->getClassMetadata($abstractSchemaName); // Building queryComponent $queryComponent = array( @@ -1543,11 +1630,11 @@ class Parser 'parent' => null, 'relation' => null, 'map' => null, - 'nestingLevel' => $this->_nestingLevel, + 'nestingLevel' => $this->nestingLevel, 'token' => $token ); - $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable); } @@ -1561,18 +1648,18 @@ class Parser { $joinAssociationPathExpression = $this->JoinAssociationPathExpression(); - if ($this->_lexer->isNextToken(Lexer::T_AS)) { + if ($this->lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); } $aliasIdentificationVariable = $this->AliasIdentificationVariable(); - $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; $identificationVariable = $joinAssociationPathExpression->identificationVariable; $field = $joinAssociationPathExpression->associationField; - $class = $this->_queryComponents[$identificationVariable]['metadata']; - $targetClass = $this->_em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + $class = $this->queryComponents[$identificationVariable]['metadata']; + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); // Building queryComponent $joinQueryComponent = array( @@ -1580,11 +1667,11 @@ class Parser 'parent' => $joinAssociationPathExpression->identificationVariable, 'relation' => $class->getAssociationMapping($field), 'map' => null, - 'nestingLevel' => $this->_nestingLevel, - 'token' => $this->_lexer->lookahead + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->lookahead ); - $this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy); } @@ -1607,13 +1694,13 @@ class Parser $this->match(Lexer::T_OPEN_CURLY_BRACE); $this->match(Lexer::T_IDENTIFIER); - $partialFieldSet[] = $this->_lexer->token['value']; + $partialFieldSet[] = $this->lexer->token['value']; - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $this->match(Lexer::T_IDENTIFIER); - $partialFieldSet[] = $this->_lexer->token['value']; + $partialFieldSet[] = $this->lexer->token['value']; } $this->match(Lexer::T_CLOSE_CURLY_BRACE); @@ -1621,10 +1708,10 @@ class Parser $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet); // Defer PartialObjectExpression validation - $this->_deferredPartialObjectExpressions[] = array( + $this->deferredPartialObjectExpressions[] = array( 'expression' => $partialObjectExpression, - 'nestingLevel' => $this->_nestingLevel, - 'token' => $this->_lexer->token, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, ); return $partialObjectExpression; @@ -1639,21 +1726,13 @@ class Parser $this->match(Lexer::T_NEW); $this->match(Lexer::T_IDENTIFIER); - $className = $this->_lexer->token['value']; - - if ( ! class_exists($className, true)) { - $this->semanticalError("Class \"$className\" is not defined.", $this->_lexer->token); - } - - $class = new \ReflectionClass($className); - if($class->getConstructor() === null) { - $this->semanticalError("Class \"$className\" has not a valid contructor.", $this->_lexer->token); - } + $token = $this->lexer->token; + $className = $token['value']; $this->match(Lexer::T_OPEN_PARENTHESIS); $args[] = $this->NewObjectArg(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $args[] = $this->NewObjectArg(); @@ -1661,11 +1740,16 @@ class Parser $this->match(Lexer::T_CLOSE_PARENTHESIS); - if($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { - $this->semanticalError("Number of arguments does not match.", $this->_lexer->token); - } + $expression = new AST\NewObjectExpression($className, $args); - return new AST\NewObjectExpression($className, $args);; + // Defer NewObjectExpression validation + $this->deferredNewObjectExpressions[] = array( + 'token' => $token, + 'expression' => $expression, + 'nestingLevel' => $this->nestingLevel, + ); + + return $expression; } /** @@ -1692,7 +1776,7 @@ class Parser $pathExpr = $this->StateFieldPathExpression(); // Add the INDEX BY info to the query component - $this->_queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field; + $this->queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field; return new AST\IndexBy($pathExpr); } @@ -1706,16 +1790,16 @@ class Parser */ public function ScalarExpression() { - $lookahead = $this->_lexer->lookahead['type']; + $lookahead = $this->lexer->lookahead['type']; switch ($lookahead) { case Lexer::T_IDENTIFIER: - $this->_lexer->peek(); // lookahead => '.' - $this->_lexer->peek(); // lookahead => token after '.' - $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.' - $this->_lexer->resetPeek(); + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + $this->lexer->resetPeek(); - if ($this->_isMathOperator($peek)) { + if ($this->isMathOperator($peek)) { return $this->SimpleArithmeticExpression(); } @@ -1732,7 +1816,7 @@ class Parser case Lexer::T_FALSE: $this->match($lookahead); - return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); case Lexer::T_INPUT_PARAMETER: return $this->InputParameter(); @@ -1745,19 +1829,19 @@ class Parser return $this->CaseExpression(); default: - if ( ! ($this->_isFunction() || $this->_isAggregateFunction($lookahead))) { + if ( ! ($this->isFunction() || $this->isAggregateFunction($lookahead))) { $this->syntaxError(); } // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) - $this->_lexer->peek(); // "(" - $peek = $this->_peekBeyondClosingParenthesis(); + $this->lexer->peek(); // "(" + $peek = $this->peekBeyondClosingParenthesis(); - if ($this->_isMathOperator($peek)) { + if ($this->isMathOperator($peek)) { return $this->SimpleArithmeticExpression(); } - if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { return $this->AggregateExpression(); } @@ -1779,7 +1863,7 @@ class Parser */ public function CaseExpression() { - $lookahead = $this->_lexer->lookahead['type']; + $lookahead = $this->lexer->lookahead['type']; switch ($lookahead) { case Lexer::T_NULLIF: @@ -1789,8 +1873,8 @@ class Parser return $this->CoalesceExpression(); case Lexer::T_CASE: - $this->_lexer->resetPeek(); - $peek = $this->_lexer->peek(); + $this->lexer->resetPeek(); + $peek = $this->lexer->peek(); if ($peek['type'] === Lexer::T_WHEN) { return $this->GeneralCaseExpression(); @@ -1820,7 +1904,7 @@ class Parser $scalarExpressions = array(); $scalarExpressions[] = $this->ScalarExpression(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $scalarExpressions[] = $this->ScalarExpression(); @@ -1864,7 +1948,7 @@ class Parser do { $whenClauses[] = $this->WhenClause(); - } while ($this->_lexer->isNextToken(Lexer::T_WHEN)); + } while ($this->lexer->isNextToken(Lexer::T_WHEN)); $this->match(Lexer::T_ELSE); $scalarExpression = $this->ScalarExpression(); @@ -1887,7 +1971,7 @@ class Parser do { $simpleWhenClauses[] = $this->SimpleWhenClause(); - } while ($this->_lexer->isNextToken(Lexer::T_WHEN)); + } while ($this->lexer->isNextToken(Lexer::T_WHEN)); $this->match(Lexer::T_ELSE); $scalarExpression = $this->ScalarExpression(); @@ -1936,8 +2020,8 @@ class Parser { $expression = null; $identVariable = null; - $peek = $this->_lexer->glimpse(); - $lookaheadType = $this->_lexer->lookahead['type']; + $peek = $this->lexer->glimpse(); + $lookaheadType = $this->lexer->lookahead['type']; switch (true) { // ScalarExpression (u.name) @@ -1958,16 +2042,16 @@ class Parser break; // DQL Function (SUM(u.value) or SUM(u.value) + 1) - case ($this->_isFunction()): - $this->_lexer->peek(); // "(" + case ($this->isFunction()): + $this->lexer->peek(); // "(" switch (true) { - case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())): + case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): // SUM(u.id) + COUNT(u.id) $expression = $this->ScalarExpression(); break; - case ($this->_isAggregateFunction($lookaheadType)): + case ($this->isAggregateFunction($lookaheadType)): // COUNT(u.id) $expression = $this->AggregateExpression(); break; @@ -2012,19 +2096,19 @@ class Parser default: $this->syntaxError( 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', - $this->_lexer->lookahead + $this->lexer->lookahead ); } // [["AS"] ["HIDDEN"] AliasResultVariable] - if ($this->_lexer->isNextToken(Lexer::T_AS)) { + if ($this->lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); } $hiddenAliasResultVariable = false; - if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) { + if ($this->lexer->isNextToken(Lexer::T_HIDDEN)) { $this->match(Lexer::T_HIDDEN); $hiddenAliasResultVariable = true; @@ -2032,14 +2116,14 @@ class Parser $aliasResultVariable = null; - if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { - $token = $this->_lexer->lookahead; + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->lexer->lookahead; $aliasResultVariable = $this->AliasResultVariable(); // Include AliasResultVariable in query components. - $this->_queryComponents[$aliasResultVariable] = array( + $this->queryComponents[$aliasResultVariable] = array( 'resultVariable' => $expression, - 'nestingLevel' => $this->_nestingLevel, + 'nestingLevel' => $this->nestingLevel, 'token' => $token, ); } @@ -2049,7 +2133,7 @@ class Parser $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable); if ($identVariable) { - $this->_identVariableExpressions[$identVariable] = $expr; + $this->identVariableExpressions[$identVariable] = $expr; } return $expr; @@ -2065,9 +2149,9 @@ class Parser */ public function SimpleSelectExpression() { - $peek = $this->_lexer->glimpse(); + $peek = $this->lexer->glimpse(); - switch ($this->_lexer->lookahead['type']) { + switch ($this->lexer->lookahead['type']) { case Lexer::T_IDENTIFIER: switch (true) { case ($peek['type'] === Lexer::T_DOT): @@ -2080,13 +2164,13 @@ class Parser return new AST\SimpleSelectExpression($expression); - case ($this->_isFunction()): + case ($this->isFunction()): // SUM(u.id) + COUNT(u.id) - if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) { + if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) { return new AST\SimpleSelectExpression($this->ScalarExpression()); } // COUNT(u.id) - if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { return new AST\SimpleSelectExpression($this->AggregateExpression()); } // IDENTITY(u) @@ -2116,24 +2200,24 @@ class Parser // Do nothing } - $this->_lexer->peek(); + $this->lexer->peek(); $expression = $this->ScalarExpression(); $expr = new AST\SimpleSelectExpression($expression); - if ($this->_lexer->isNextToken(Lexer::T_AS)) { + if ($this->lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); } - if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { - $token = $this->_lexer->lookahead; + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->lexer->lookahead; $resultVariable = $this->AliasResultVariable(); $expr->fieldIdentificationVariable = $resultVariable; // Include AliasResultVariable in query components. - $this->_queryComponents[$resultVariable] = array( + $this->queryComponents[$resultVariable] = array( 'resultvariable' => $expr, - 'nestingLevel' => $this->_nestingLevel, + 'nestingLevel' => $this->nestingLevel, 'token' => $token, ); } @@ -2151,7 +2235,7 @@ class Parser $conditionalTerms = array(); $conditionalTerms[] = $this->ConditionalTerm(); - while ($this->_lexer->isNextToken(Lexer::T_OR)) { + while ($this->lexer->isNextToken(Lexer::T_OR)) { $this->match(Lexer::T_OR); $conditionalTerms[] = $this->ConditionalTerm(); @@ -2176,7 +2260,7 @@ class Parser $conditionalFactors = array(); $conditionalFactors[] = $this->ConditionalFactor(); - while ($this->_lexer->isNextToken(Lexer::T_AND)) { + while ($this->lexer->isNextToken(Lexer::T_AND)) { $this->match(Lexer::T_AND); $conditionalFactors[] = $this->ConditionalFactor(); @@ -2200,7 +2284,7 @@ class Parser { $not = false; - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $not = true; @@ -2229,18 +2313,18 @@ class Parser { $condPrimary = new AST\ConditionalPrimary; - if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + if ( ! $this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); return $condPrimary; } // Peek beyond the matching closing paranthesis ')' - $peek = $this->_peekBeyondClosingParenthesis(); + $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)) || - $this->_isMathOperator($peek)) { + $this->isMathOperator($peek)) { $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); return $condPrimary; @@ -2262,54 +2346,54 @@ class Parser */ public function SimpleConditionalExpression() { - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { - $token = $this->_lexer->glimpse(); + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $token = $this->lexer->glimpse(); } if ($token['type'] === Lexer::T_EXISTS) { return $this->ExistsExpression(); } - $peek = $this->_lexer->glimpse(); + $peek = $this->lexer->glimpse(); if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) { if ($peek['value'] == '(') { // Peek beyond the matching closing paranthesis ')' - $this->_lexer->peek(); - $token = $this->_peekBeyondClosingParenthesis(false); + $this->lexer->peek(); + $token = $this->peekBeyondClosingParenthesis(false); if ($token['type'] === Lexer::T_NOT) { - $token = $this->_lexer->peek(); + $token = $this->lexer->peek(); } - $this->_lexer->resetPeek(); + $this->lexer->resetPeek(); } else { // Peek beyond the PathExpression (or InputParameter) - $peek = $this->_lexer->peek(); + $peek = $this->lexer->peek(); while ($peek['value'] === '.') { - $this->_lexer->peek(); - $peek = $this->_lexer->peek(); + $this->lexer->peek(); + $peek = $this->lexer->peek(); } // Also peek beyond a NOT if there is one if ($peek['type'] === Lexer::T_NOT) { - $peek = $this->_lexer->peek(); + $peek = $this->lexer->peek(); } $token = $peek; // We need to go even further in case of IS (differenciate between NULL and EMPTY) - $lookahead = $this->_lexer->peek(); + $lookahead = $this->lexer->peek(); // Also peek beyond a NOT if there is one if ($lookahead['type'] === Lexer::T_NOT) { - $lookahead = $this->_lexer->peek(); + $lookahead = $this->lexer->peek(); } - $this->_lexer->resetPeek(); + $this->lexer->resetPeek(); } } @@ -2346,7 +2430,7 @@ class Parser ); $this->match(Lexer::T_IS); - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $emptyColletionCompExpr->not = true; } @@ -2369,7 +2453,7 @@ class Parser $not = false; $entityExpr = $this->EntityExpression(); - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $not = true; @@ -2377,7 +2461,7 @@ class Parser $this->match(Lexer::T_MEMBER); - if ($this->_lexer->isNextToken(Lexer::T_OF)) { + if ($this->lexer->isNextToken(Lexer::T_OF)) { $this->match(Lexer::T_OF); } @@ -2396,24 +2480,24 @@ class Parser */ public function Literal() { - switch ($this->_lexer->lookahead['type']) { + switch ($this->lexer->lookahead['type']) { case Lexer::T_STRING: $this->match(Lexer::T_STRING); - return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']); + return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); case Lexer::T_INTEGER: case Lexer::T_FLOAT: $this->match( - $this->_lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT + $this->lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT ); - return new AST\Literal(AST\Literal::NUMERIC, $this->_lexer->token['value']); + return new AST\Literal(AST\Literal::NUMERIC, $this->lexer->token['value']); case Lexer::T_TRUE: case Lexer::T_FALSE: $this->match( - $this->_lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE + $this->lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE ); - return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); default: $this->syntaxError('Literal'); @@ -2427,7 +2511,7 @@ class Parser */ public function InParameter() { - if ($this->_lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) { + if ($this->lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) { return $this->InputParameter(); } @@ -2443,7 +2527,7 @@ class Parser { $this->match(Lexer::T_INPUT_PARAMETER); - return new AST\InputParameter($this->_lexer->token['value']); + return new AST\InputParameter($this->lexer->token['value']); } /** @@ -2455,8 +2539,8 @@ class Parser { $expr = new AST\ArithmeticExpression; - if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { - $peek = $this->_lexer->glimpse(); + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->lexer->glimpse(); if ($peek['type'] === Lexer::T_SELECT) { $this->match(Lexer::T_OPEN_PARENTHESIS); @@ -2482,10 +2566,10 @@ class Parser $terms = array(); $terms[] = $this->ArithmeticTerm(); - while (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) { + while (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) { $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); - $terms[] = $this->_lexer->token['value']; + $terms[] = $this->lexer->token['value']; $terms[] = $this->ArithmeticTerm(); } @@ -2508,10 +2592,10 @@ class Parser $factors = array(); $factors[] = $this->ArithmeticFactor(); - while (($isMult = $this->_lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->_lexer->isNextToken(Lexer::T_DIVIDE)) { + while (($isMult = $this->lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->lexer->isNextToken(Lexer::T_DIVIDE)) { $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE); - $factors[] = $this->_lexer->token['value']; + $factors[] = $this->lexer->token['value']; $factors[] = $this->ArithmeticFactor(); } @@ -2533,7 +2617,7 @@ class Parser { $sign = null; - if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) { + if (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) { $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); $sign = $isPlus; } @@ -2557,7 +2641,7 @@ class Parser */ public function ArithmeticPrimary() { - if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { $this->match(Lexer::T_OPEN_PARENTHESIS); $expr = $this->SimpleArithmeticExpression(); @@ -2566,14 +2650,14 @@ class Parser return $expr; } - switch ($this->_lexer->lookahead['type']) { + switch ($this->lexer->lookahead['type']) { case Lexer::T_COALESCE: case Lexer::T_NULLIF: case Lexer::T_CASE: return $this->CaseExpression(); case Lexer::T_IDENTIFIER: - $peek = $this->_lexer->glimpse(); + $peek = $this->lexer->glimpse(); if ($peek['value'] == '(') { return $this->FunctionDeclaration(); @@ -2583,7 +2667,7 @@ class Parser return $this->SingleValuedPathExpression(); } - if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) { + if (isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) { return $this->ResultVariable(); } @@ -2593,10 +2677,10 @@ class Parser return $this->InputParameter(); default: - $peek = $this->_lexer->glimpse(); + $peek = $this->lexer->glimpse(); if ($peek['value'] == '(') { - if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { return $this->AggregateExpression(); } @@ -2615,8 +2699,8 @@ class Parser */ public function StringExpression() { - if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { - $peek = $this->_lexer->glimpse(); + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->lexer->glimpse(); if ($peek['type'] === Lexer::T_SELECT) { $this->match(Lexer::T_OPEN_PARENTHESIS); @@ -2635,11 +2719,11 @@ class Parser */ public function StringPrimary() { - $lookaheadType = $this->_lexer->lookahead['type']; + $lookaheadType = $this->lexer->lookahead['type']; switch ($lookaheadType) { case Lexer::T_IDENTIFIER: - $peek = $this->_lexer->glimpse(); + $peek = $this->lexer->glimpse(); if ($peek['value'] == '.') { return $this->StateFieldPathExpression(); @@ -2656,7 +2740,7 @@ class Parser case Lexer::T_STRING: $this->match(Lexer::T_STRING); - return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']); + return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); case Lexer::T_INPUT_PARAMETER: return $this->InputParameter(); @@ -2667,7 +2751,7 @@ class Parser return $this->CaseExpression(); default: - if ($this->_isAggregateFunction($lookaheadType)) { + if ($this->isAggregateFunction($lookaheadType)) { return $this->AggregateExpression(); } } @@ -2685,9 +2769,9 @@ class Parser */ public function EntityExpression() { - $glimpse = $this->_lexer->glimpse(); + $glimpse = $this->lexer->glimpse(); - if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') { + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') { return $this->SingleValuedAssociationPathExpression(); } @@ -2701,7 +2785,7 @@ class Parser */ public function SimpleEntityExpression() { - if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { return $this->InputParameter(); } @@ -2717,7 +2801,7 @@ class Parser */ public function AggregateExpression() { - $lookaheadType = $this->_lexer->lookahead['type']; + $lookaheadType = $this->lexer->lookahead['type']; $isDistinct = false; if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) { @@ -2725,10 +2809,10 @@ class Parser } $this->match($lookaheadType); - $functionName = $this->_lexer->token['value']; + $functionName = $this->lexer->token['value']; $this->match(Lexer::T_OPEN_PARENTHESIS); - if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { $this->match(Lexer::T_DISTINCT); $isDistinct = true; } @@ -2749,8 +2833,8 @@ class Parser */ public function QuantifiedExpression() { - $lookaheadType = $this->_lexer->lookahead['type']; - $value = $this->_lexer->lookahead['value']; + $lookaheadType = $this->lexer->lookahead['type']; + $value = $this->lexer->lookahead['value']; if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) { $this->syntaxError('ALL, ANY or SOME'); @@ -2777,7 +2861,7 @@ class Parser $not = false; $arithExpr1 = $this->ArithmeticExpression(); - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $not = true; } @@ -2800,11 +2884,11 @@ class Parser */ public function ComparisonExpression() { - $this->_lexer->glimpse(); + $this->lexer->glimpse(); $leftExpr = $this->ArithmeticExpression(); $operator = $this->ComparisonOperator(); - $rightExpr = ($this->_isNextAllAnySome()) + $rightExpr = ($this->isNextAllAnySome()) ? $this->QuantifiedExpression() : $this->ArithmeticExpression(); @@ -2820,7 +2904,7 @@ class Parser { $inExpression = new AST\InExpression($this->ArithmeticExpression()); - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $inExpression->not = true; } @@ -2828,13 +2912,13 @@ class Parser $this->match(Lexer::T_IN); $this->match(Lexer::T_OPEN_PARENTHESIS); - if ($this->_lexer->isNextToken(Lexer::T_SELECT)) { + if ($this->lexer->isNextToken(Lexer::T_SELECT)) { $inExpression->subselect = $this->Subselect(); } else { $literals = array(); $literals[] = $this->InParameter(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $literals[] = $this->InParameter(); } @@ -2856,7 +2940,7 @@ class Parser { $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable()); - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $instanceOfExpression->not = true; } @@ -2866,12 +2950,12 @@ class Parser $exprValues = array(); - if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { $this->match(Lexer::T_OPEN_PARENTHESIS); $exprValues[] = $this->InstanceOfParameter(); - while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); $exprValues[] = $this->InstanceOfParameter(); @@ -2898,10 +2982,10 @@ class Parser */ public function InstanceOfParameter() { - if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { $this->match(Lexer::T_INPUT_PARAMETER); - return new AST\InputParameter($this->_lexer->token['value']); + return new AST\InputParameter($this->lexer->token['value']); } return $this->AliasIdentificationVariable(); @@ -2917,27 +3001,27 @@ class Parser $stringExpr = $this->StringExpression(); $not = false; - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $not = true; } $this->match(Lexer::T_LIKE); - if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { $this->match(Lexer::T_INPUT_PARAMETER); - $stringPattern = new AST\InputParameter($this->_lexer->token['value']); + $stringPattern = new AST\InputParameter($this->lexer->token['value']); } else { $stringPattern = $this->StringPrimary(); } $escapeChar = null; - if ($this->_lexer->lookahead['type'] === Lexer::T_ESCAPE) { + if ($this->lexer->lookahead['type'] === Lexer::T_ESCAPE) { $this->match(Lexer::T_ESCAPE); $this->match(Lexer::T_STRING); - $escapeChar = new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']); + $escapeChar = new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); } $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar); @@ -2953,9 +3037,9 @@ class Parser */ public function NullComparisonExpression() { - if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { $this->match(Lexer::T_INPUT_PARAMETER); - $expr = new AST\InputParameter($this->_lexer->token['value']); + $expr = new AST\InputParameter($this->lexer->token['value']); } else { $expr = $this->SingleValuedPathExpression(); } @@ -2963,7 +3047,7 @@ class Parser $nullCompExpr = new AST\NullComparisonExpression($expr); $this->match(Lexer::T_IS); - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $nullCompExpr->not = true; } @@ -2982,7 +3066,7 @@ class Parser { $not = false; - if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $not = true; } @@ -3005,7 +3089,7 @@ class Parser */ public function ComparisonOperator() { - switch ($this->_lexer->lookahead['value']) { + switch ($this->lexer->lookahead['value']) { case '=': $this->match(Lexer::T_EQUALS); @@ -3015,10 +3099,10 @@ class Parser $this->match(Lexer::T_LOWER_THAN); $operator = '<'; - if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) { + if ($this->lexer->isNextToken(Lexer::T_EQUALS)) { $this->match(Lexer::T_EQUALS); $operator .= '='; - } else if ($this->_lexer->isNextToken(Lexer::T_GREATER_THAN)) { + } else if ($this->lexer->isNextToken(Lexer::T_GREATER_THAN)) { $this->match(Lexer::T_GREATER_THAN); $operator .= '>'; } @@ -3029,7 +3113,7 @@ class Parser $this->match(Lexer::T_GREATER_THAN); $operator = '>'; - if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) { + if ($this->lexer->isNextToken(Lexer::T_EQUALS)) { $this->match(Lexer::T_EQUALS); $operator .= '='; } @@ -3052,7 +3136,7 @@ class Parser */ public function FunctionDeclaration() { - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; $funcName = strtolower($token['value']); // Check for built-in functions first! @@ -3076,11 +3160,11 @@ class Parser */ private function CustomFunctionDeclaration() { - $token = $this->_lexer->lookahead; + $token = $this->lexer->lookahead; $funcName = strtolower($token['value']); // Check for custom functions afterwards - $config = $this->_em->getConfiguration(); + $config = $this->em->getConfiguration(); switch (true) { case ($config->getCustomStringFunction($funcName) !== null): @@ -3108,7 +3192,7 @@ class Parser */ public function FunctionsReturningNumerics() { - $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcNameLower = strtolower($this->lexer->lookahead['value']); $funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower]; $function = new $funcClass($funcNameLower); @@ -3120,8 +3204,8 @@ class Parser public function CustomFunctionsReturningNumerics() { // getCustomNumericFunction is case-insensitive - $funcName = strtolower($this->_lexer->lookahead['value']); - $funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName); + $funcName = strtolower($this->lexer->lookahead['value']); + $funcClass = $this->em->getConfiguration()->getCustomNumericFunction($funcName); $function = new $funcClass($funcName); $function->parse($this); @@ -3134,7 +3218,7 @@ class Parser */ public function FunctionsReturningDatetime() { - $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcNameLower = strtolower($this->lexer->lookahead['value']); $funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower]; $function = new $funcClass($funcNameLower); @@ -3146,8 +3230,8 @@ class Parser public function CustomFunctionsReturningDatetime() { // getCustomDatetimeFunction is case-insensitive - $funcName = $this->_lexer->lookahead['value']; - $funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName); + $funcName = $this->lexer->lookahead['value']; + $funcClass = $this->em->getConfiguration()->getCustomDatetimeFunction($funcName); $function = new $funcClass($funcName); $function->parse($this); @@ -3165,7 +3249,7 @@ class Parser */ public function FunctionsReturningStrings() { - $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcNameLower = strtolower($this->lexer->lookahead['value']); $funcClass = self::$_STRING_FUNCTIONS[$funcNameLower]; $function = new $funcClass($funcNameLower); @@ -3177,8 +3261,8 @@ class Parser public function CustomFunctionsReturningStrings() { // getCustomStringFunction is case-insensitive - $funcName = $this->_lexer->lookahead['value']; - $funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName); + $funcName = $this->lexer->lookahead['value']; + $funcClass = $this->em->getConfiguration()->getCustomStringFunction($funcName); $function = new $funcClass($funcName); $function->parse($this); diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 2109482fa..cb340912b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -98,7 +98,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase e.email, a.city ) - FROM + FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e @@ -119,16 +119,40 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($this->fixtures[0]->name, $result[0]->name); $this->assertEquals($this->fixtures[1]->name, $result[1]->name); $this->assertEquals($this->fixtures[2]->name, $result[2]->name); - + $this->assertEquals($this->fixtures[0]->email->email, $result[0]->email); $this->assertEquals($this->fixtures[1]->email->email, $result[1]->email); $this->assertEquals($this->fixtures[2]->email->email, $result[2]->email); - + $this->assertEquals($this->fixtures[0]->address->city, $result[0]->address); $this->assertEquals($this->fixtures[1]->address->city, $result[1]->address); $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); } + public function testShouldAssumeFromEntityNamespaceWhenNotGiven() + { + $dql = " + SELECT + new CmsUserDTO(u.name, e.email, a.city) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + } + public function testShouldSupportLiteralExpression() { $dql = " @@ -403,11 +427,11 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase { $dql = " SELECT - new Doctrine\Tests\Models\CMS\CmsUserDTO( + new CmsUserDTO( u.name, e.email ), - new Doctrine\Tests\Models\CMS\CmsAddressDTO( + new CmsAddressDTO( a.country, a.city ) @@ -473,7 +497,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase /** * @expectedException Doctrine\ORM\Query\QueryException - * @expectedExceptionMessage [Semantical Error] line 0, col 68 near ') FROM Doctrine\Tests\Models\CMS\CmsUser': Error: Number of arguments does not match. + * @expectedExceptionMessage [Semantical Error] line 0, col 11 near 'Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name)': Error: Number of arguments does not match with "Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs" constructor declaration. */ public function testInvalidClassWithoutConstructorException() { From 1bd6e841bfd965a2ba1ccf863e40adcced448721 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 12 Aug 2012 14:40:26 -0300 Subject: [PATCH 19/22] Fix some CS --- .../Internal/Hydration/AbstractHydrator.php | 11 ++-- .../ORM/Internal/Hydration/ObjectHydrator.php | 20 ++++--- .../ORM/Query/AST/NewObjectExpression.php | 2 +- lib/Doctrine/ORM/Query/Parser.php | 59 ++++++++----------- lib/Doctrine/ORM/Query/SqlWalker.php | 5 +- .../Tests/ORM/Functional/NewOperatorTest.php | 18 ++++++ 6 files changed, 61 insertions(+), 54 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index f8b7607ca..5e30c825d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -236,10 +236,12 @@ abstract class AbstractHydrator } if (isset($this->_rsm->newObjectMappings[$key])) { + $mapping = $this->_rsm->newObjectMappings[$key]; + $cache[$key]['isNewObjectParameter'] = true; - $cache[$key]['argIndex'] = $this->_rsm->newObjectMappings[$key]['argIndex']; - $cache[$key]['objIndex'] = $this->_rsm->newObjectMappings[$key]['objIndex']; - $cache[$key]['class'] = new \ReflectionClass($this->_rsm->newObjectMappings[$key]['className']); + $cache[$key]['argIndex'] = $mapping['argIndex']; + $cache[$key]['objIndex'] = $mapping['objIndex']; + $cache[$key]['class'] = new \ReflectionClass($mapping['className']); } } @@ -247,8 +249,7 @@ abstract class AbstractHydrator $class = $cache[$key]['class']; $argIndex = $cache[$key]['argIndex']; $objIndex = $cache[$key]['objIndex']; - $value = $cache[$key]['type'] - ->convertToPHPValue($value, $this->_platform); + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); $rowData['newObjects'][$objIndex]['class'] = $class; $rowData['newObjects'][$objIndex]['args'][$argIndex] = $value; diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index edce92f80..b68bf01cc 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -397,11 +397,11 @@ class ObjectHydrator extends AbstractHydrator continue; } - $parentClass = $this->ce[$this->_rsm->aliasMap[$parentAlias]]; - $oid = spl_object_hash($parentObject); - $relationField = $this->_rsm->relationMap[$dqlAlias]; - $relation = $parentClass->associationMappings[$relationField]; - $reflField = $parentClass->reflFields[$relationField]; + $parentClass = $this->ce[$this->_rsm->aliasMap[$parentAlias]]; + $oid = spl_object_hash($parentObject); + $relationField = $this->_rsm->relationMap[$dqlAlias]; + $relation = $parentClass->associationMappings[$relationField]; + $reflField = $parentClass->reflFields[$relationField]; // Check the type of the relation (many or single-valued) if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { @@ -415,9 +415,9 @@ class ObjectHydrator extends AbstractHydrator $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); } - $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); - $index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; - $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; + $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); + $index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; if ( ! $indexExists || ! $indexIsValid) { if (isset($this->existingCollections[$collKey])) { @@ -514,6 +514,7 @@ class ObjectHydrator extends AbstractHydrator // check for existing result from the iterations before if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) { $element = $this->getEntity($rowData[$dqlAlias], $dqlAlias); + if ($this->_rsm->isMixed) { $element = array($entityKey => $element); } @@ -585,6 +586,7 @@ class ObjectHydrator extends AbstractHydrator if ($count === 1) { $result[$resultKey] = $obj; + continue; } @@ -602,7 +604,7 @@ class ObjectHydrator extends AbstractHydrator { parent::onClear($eventArgs); - $aliases = array_keys($this->identifierMap); + $aliases = array_keys($this->identifierMap); $this->identifierMap = array(); foreach ($aliases as $alias) { diff --git a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php index 5ddaf0a5e..caf7a80b7 100644 --- a/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php +++ b/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -46,7 +46,7 @@ class NewObjectExpression extends Node public function __construct($className, array $args) { $this->className = $className; - $this->args = $args; + $this->args = $args; } /** diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 22605a3e0..06d329b37 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -581,28 +581,18 @@ class Parser private function processDeferredNewObjectExpressions($AST) { foreach ($this->deferredNewObjectExpressions as $deferredItem) { - $expression = $deferredItem['expression']; - $token = $deferredItem['token']; - $className = $expression->className; - $args = $expression->args; + $expression = $deferredItem['expression']; + $token = $deferredItem['token']; + $className = $expression->className; + $args = $expression->args; + $fromClassName = isset($AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName) + ? $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName + : null; - //first from item - if ( strpos($className, '\\') === false - && ! class_exists($className) - && isset($AST->fromClause - ->identificationVariableDeclarations[0] - ->rangeVariableDeclaration - ->abstractSchemaName)) { - - $fromClassName = $AST->fromClause - ->identificationVariableDeclarations[0] - ->rangeVariableDeclaration - ->abstractSchemaName; - - if (strpos($fromClassName, '\\') !== false) { - $fromClassName = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); - $className = $fromClassName . '\\' . $className; - } + // If the namespace is not given then assumes the first from entity namespace + if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) { + $namespace = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); + $className = $namespace . '\\' . $className; if (class_exists($className)) { $expression->className = $className; @@ -610,26 +600,21 @@ class Parser } if ( ! class_exists($className)) { - $this->semanticalError(sprintf( - 'Class "%s" is not defined.', - $expression->className - ), $token); + $this->semanticalError(sprintf('Class "%s" is not defined.', $expression->className), $token); } $class = new \ReflectionClass($className); - if($class->getConstructor() === null) { - $this->semanticalError(sprintf( - 'Class "%s" has not a valid contructor.', - $className - ), $token); + if ( ! $class->isInstantiable()) { + $this->semanticalError(sprintf('Class "%s" can not be instantiated.', $className), $token); } - if($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { - $this->semanticalError(sprintf( - 'Number of arguments does not match with "%s" constructor declaration.', - $className - ), $token); + if ($class->getConstructor() === null) { + $this->semanticalError(sprintf('Class "%s" has not a valid contructor.', $className), $token); + } + + if ($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { + $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $token); } } } @@ -1719,6 +1704,7 @@ class Parser /** * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * * @return \Doctrine\ORM\Query\AST\NewObjectExpression */ public function NewObjectExpression() @@ -1732,6 +1718,7 @@ class Parser $this->match(Lexer::T_OPEN_PARENTHESIS); $args[] = $this->NewObjectArg(); + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); @@ -2090,7 +2077,7 @@ class Parser // NewObjectExpression (New ClassName(id, name)) case ($lookaheadType === Lexer::T_NEW): - $expression = $this->NewObjectExpression(); + $expression = $this->NewObjectExpression(); break; default: diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index a24b92836..e6b962919 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1406,15 +1406,14 @@ class SqlWalker implements TreeWalker public function walkNewObject($newObjectExpression) { $sqlSelectExpressions = array(); - $objIndex = $this->newObjectCounter ++; + $objIndex = $this->newObjectCounter++; foreach ($newObjectExpression->args as $argIndex => $e) { - $resultAlias = $this->scalarResultCounter++; $columnAlias = $this->getSQLColumnAlias('sclr'); switch (true) { - case $e instanceof AST\NewObjectExpression: + case ($e instanceof AST\NewObjectExpression): $sqlSelectExpressions[] = $e->dispatch($this); break; diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index cb340912b..6859af5e6 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -504,6 +504,16 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $dql = "SELECT new Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u"; $this->_em->createQuery($dql)->getResult(); } + + /** + * @expectedException Doctrine\ORM\Query\QueryException + * @expectedExceptionMessage [Semantical Error] line 0, col 11 near 'Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor(u.name)': Error: Class "Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor" can not be instantiated. + */ + public function testClassCantBeInstantiatedException() + { + $dql = "SELECT new Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u"; + $this->_em->createQuery($dql)->getResult(); + } } class ClassWithTooMuchArgs @@ -513,4 +523,12 @@ class ClassWithTooMuchArgs $this->foo = $foo; $this->bor = $bar; } +} + +class ClassWithPrivateConstructor +{ + private function __construct($foo) + { + $this->foo = $foo; + } } \ No newline at end of file From 7c754e495e7c847da4ec56da623e626b382cadf2 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Tue, 14 Aug 2012 21:02:52 -0300 Subject: [PATCH 20/22] support namespace alias --- lib/Doctrine/ORM/Query/Parser.php | 7 +++ .../Tests/ORM/Functional/NewOperatorTest.php | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 06d329b37..154e869bf 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1715,6 +1715,13 @@ class Parser $token = $this->lexer->token; $className = $token['value']; + if (strrpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + + $className = $this->em->getConfiguration() + ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + $this->match(Lexer::T_OPEN_PARENTHESIS); $args[] = $this->NewObjectArg(); diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 6859af5e6..4ef9d29e2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -153,6 +153,62 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); } + public function testShouldSupportFromEntityNamespaceAlias() + { + $dql = " + SELECT + new CmsUserDTO(u.name, e.email, a.city) + FROM + cms:CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name"; + + + $this->_em->getConfiguration() + ->addEntityNamespace('cms', 'Doctrine\Tests\Models\CMS'); + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + } + + public function testShouldSupportValueObjectNamespaceAlias() + { + $dql = " + SELECT + new cms:CmsUserDTO(u.name, e.email, a.city) + FROM + cms:CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name"; + + + $this->_em->getConfiguration() + ->addEntityNamespace('cms', 'Doctrine\Tests\Models\CMS'); + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + } + public function testShouldSupportLiteralExpression() { $dql = " From 5f89fa419051f06e2d17ac6f7e53402275023042 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 20 Aug 2012 22:43:16 -0300 Subject: [PATCH 21/22] fix CS --- .../ORM/Internal/Hydration/ObjectHydrator.php | 4 ++-- lib/Doctrine/ORM/Query/Parser.php | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index b68bf01cc..1dd18566e 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -81,8 +81,8 @@ class ObjectHydrator extends AbstractHydrator private $existingCollections = array(); - /** - * @override + /** + * {@inheritdoc} */ protected function prepare() { diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 154e869bf..0b9bb5bd3 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -71,7 +71,7 @@ class Parser * Expressions that were encountered during parsing of identifiers and expressions * and still need to be validated. */ - + /** * @var array */ @@ -81,12 +81,12 @@ class Parser * @var array */ private $deferredPartialObjectExpressions = array(); - + /** * @var array */ private $deferredPathExpressions = array(); - + /** * @var array */ @@ -589,18 +589,19 @@ class Parser ? $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName : null; - // If the namespace is not given then assumes the first from entity namespace + // If the namespace is not given then assumes the first FROM entity namespace if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) { $namespace = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); - $className = $namespace . '\\' . $className; + $fqcn = $namespace . '\\' . $className; - if (class_exists($className)) { - $expression->className = $className; + if (class_exists($fqcn)) { + $expression->className = $fqcn; + $className = $fqcn; } } if ( ! class_exists($className)) { - $this->semanticalError(sprintf('Class "%s" is not defined.', $expression->className), $token); + $this->semanticalError(sprintf('Class "%s" is not defined.', $className), $token); } $class = new \ReflectionClass($className); @@ -613,7 +614,7 @@ class Parser $this->semanticalError(sprintf('Class "%s" has not a valid contructor.', $className), $token); } - if ($class->getConstructor()->getNumberOfRequiredParameters() > sizeof($args)) { + if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) { $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $token); } } From dd984c73196a4c255651378b0864e76f8f7c1ef2 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 20 Aug 2012 22:48:37 -0300 Subject: [PATCH 22/22] remove extra line --- lib/Doctrine/ORM/Query/SqlWalker.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index e6b962919..75de800ae 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1422,7 +1422,6 @@ class SqlWalker implements TreeWalker break; } - switch (true) { case ($e instanceof AST\PathExpression): $fieldName = $e->field;