From 54476882c7045e39dc309f383945af5508d552ce Mon Sep 17 00:00:00 2001 From: Christian Heinrich Date: Tue, 6 Jul 2010 17:06:31 +0200 Subject: [PATCH 001/119] CLI configured for development purposes --- tools/sandbox/cli-config.php | 61 ++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/tools/sandbox/cli-config.php b/tools/sandbox/cli-config.php index c949668e0..8e503e234 100644 --- a/tools/sandbox/cli-config.php +++ b/tools/sandbox/cli-config.php @@ -1,36 +1,63 @@ register(); -$classLoader = new \Doctrine\Common\ClassLoader('Doctrine\DBAL', realpath(__DIR__ . '/../../lib/vendor/doctrine-dbal/lib')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Doctrine\Common', realpath(__DIR__ . '/../../lib/vendor/doctrine-common/lib')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Symfony', realpath(__DIR__ . '/../../lib/vendor')); -$classLoader->register(); $classLoader = new \Doctrine\Common\ClassLoader('Entities', __DIR__); $classLoader->register(); + $classLoader = new \Doctrine\Common\ClassLoader('Proxies', __DIR__); $classLoader->register(); +require_once '/var/workspaces/nexxone/library/Zend/Acl/Resource/Interface.php'; +require_once '/var/workspaces/nexxone/library/Zend/Acl/Role/Interface.php'; + +$classLoader = new \Doctrine\Common\ClassLoader('Core\Model', '/var/workspaces/nexxone/application/modules/core/models/'); +$classLoader->register(); + +$classLoader = new \Doctrine\Common\ClassLoader('Content\Model', '/var/workspaces/nexxone/application/modules/content/models/'); +$classLoader->register(); + +$classLoader = new \Doctrine\Common\ClassLoader('User\Model', '/var/workspaces/nexxone/application/modules/user/models/'); +$classLoader->register(); + + $config = new \Doctrine\ORM\Configuration(); $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); -$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); -$config->setMetadataDriverImpl($driverImpl); $config->setProxyDir(__DIR__ . '/Proxies'); -$config->setProxyNamespace('Proxies'); -$connectionOptions = array( - 'driver' => 'pdo_sqlite', - 'path' => 'database.sqlite' +$config->setProxyNamespace('LivandoCMSProxies'); +$driver = new Doctrine\ORM\Mapping\Driver\YamlDriver( + array( + '/var/workspaces/nexxone/application/modules/core/config/schema', + '/var/workspaces/nexxone/application/modules/content/config/schema', + '/var/workspaces/nexxone/application/modules/user/config/schema', + ) ); +#$driver = new Doctrine\ORM\Mapping\Driver\YamlDriver(array('./yaml/'), YamlDriver::PRELOAD); +$config->setMetadataDriverImpl($driver); -$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config); +$conn = new \Doctrine\DBAL\Connection( + array ( + 'pdo' => new PDO( + "mysql:host=localhost;dbname=nexxone;unix_socket=/var/run/mysqld/mysqld.sock", + 'root', + 'mysql5023' + ) + ), + new Doctrine\DBAL\Driver\PDOMySql\Driver(), + $config + ); + +//$connectionOptions = array( +// 'driver' => 'pdo_sqlite', +// 'path' => 'database.sqlite' +//); + +// These are required named variables (names can't change!) +$em = \Doctrine\ORM\EntityManager::create($conn, $config); $helpers = array( 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) -); \ No newline at end of file +); From 9b03a1ecdde5b58fc63469016929637511c9eaca Mon Sep 17 00:00:00 2001 From: Christian Heinrich Date: Tue, 6 Jul 2010 20:19:43 +0200 Subject: [PATCH 002/119] Added testcase for #DDC-671 --- .../ORM/Mapping/YamlMappingDriverTest.php | 68 ++++++++++++++++++- ...ts.ORM.Mapping.AbstractContentItem.dcm.yml | 17 +++++ ...ctrine.Tests.ORM.Mapping.Directory.dcm.yml | 17 +++++ .../Doctrine.Tests.ORM.Mapping.Page.dcm.yml | 7 ++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml create mode 100644 tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml create mode 100644 tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml diff --git a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php index 2aad38645..741621aa9 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php @@ -18,4 +18,70 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest return new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml'); } -} \ No newline at end of file + public function testJoinTablesWithMappedSuperclassForYamlDriver() { + $em = $this->_getTestEntityManager(); + $em->getConfiguration()->setMetadataDriverImpl(new \Doctrine\ORM\Mapping\Driver\YamlDriver(__DIR__ .'/yaml/')); + $qb = $em->createQueryBuilder(); + + $qb->select('f') + ->from('Doctrine\Tests\ORM\Mapping\Page', 'f') + ->join('f.parentDirectory', 'd') + ->where( + $qb->expr()->andx( + $qb->expr()->eq('d.url', ':url'), + $qb->expr()->eq('f.extension', ':extension') + ) + ) + ->setParameter('url', "test") + ->setParameter('filename', "filename") + ->setParameter('extension', "extension"); + + // Is there a way to generalize this more? (Instead of a2_., check if the table prefix in the ON clause is set within a FROM or JOIN clause..) + $this->assertTrue(strpos($qb->getQuery()->getSql(), 'a2_.') === false); + } +} + +class Directory extends AbstractContentItem { + + protected $subDirectories; + + /** + * This is a collection of files that are contained in this Directory. Files, for example, could be Pages, but even other files + * like media files (css, images etc) are possible. + * + * @var \Doctrine\Common\Collections\Collection + * @access protected + */ + protected $containedFiles; + + /** + * @var string + */ + protected $url; +} + +class Page + extends AbstractContentItem { + + protected $extension = "html"; +} + +abstract class AbstractContentItem { + /** + * Doctrine2 entity id + * @var integer + */ + private $id; + + /** + * The parent directory + * @var Directory + */ + protected $parentDirectory; + + /** + * Filename (without extension) or directory name + * @var string + */ + protected $name; +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml new file mode 100644 index 000000000..419eb0449 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml @@ -0,0 +1,17 @@ +Doctrine\Tests\ORM\Mapping\AbstractContentItem: + type: mappedSuperclass + table: abstract_item + id: + id: + type: integer + unsigned: true + generator: + strategy: AUTO + fields: + # FilesystemIdentifier + manyToOne: + parentDirectory: + targetEntity: Doctrine\Tests\ORM\Mapping\Directory + joinColumn: + name: parent_directory_id + referencedColumnName: id diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml new file mode 100644 index 000000000..129ae6b28 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml @@ -0,0 +1,17 @@ +Doctrine\Tests\ORM\Mapping\Directory: + type: entity + table: core_content_directories + fields: + url: + type: string + length: 255 + oneToMany: + subDirectories: + targetEntity: Doctrine\Tests\ORM\Mapping\Directory + mappedBy: parentDirectory + cascade: + [ all ] + containedFiles: + targetEntity: Doctrine\Tests\ORM\Mapping\Page + mappedBy: parentDirectory + cascade: [ remove ] diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml new file mode 100644 index 000000000..a3b0fb836 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml @@ -0,0 +1,7 @@ +Doctrine\Tests\ORM\Mapping\Page: + type: entity + table: core_content_pages + fields: + extension: + type: string + length: 10 From 2a2936fde5c025e9a72bf1cd3c623e827ad774bb Mon Sep 17 00:00:00 2001 From: beberlei Date: Sat, 28 Aug 2010 14:53:42 +0200 Subject: [PATCH 003/119] DDC-771 - Bugfix in EntityGenerator generated use statement --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index b32197895..616c60a9d 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -312,7 +312,7 @@ public function () private function _generateEntityUse(ClassMetadataInfo $metadata) { if ($this->_extendsClass()) { - return "\n\nuse " . $this->_getClassToExtendNamespace() . ";\n"; + return "\n\nuse " . $this->_getClassToExtendName() . ";\n"; } } @@ -382,13 +382,6 @@ public function () return $refl->getName(); } - private function _getClassToExtendNamespace() - { - $refl = new \ReflectionClass($this->_getClassToExtend()); - - return $refl->getNamespaceName() ? $refl->getNamespaceName():$refl->getShortName(); - } - private function _getClassName(ClassMetadataInfo $metadata) { return ($pos = strrpos($metadata->name, '\\')) From c77a12ac83c82c00376593feadeb50b041aa8f29 Mon Sep 17 00:00:00 2001 From: beberlei Date: Sat, 28 Aug 2010 16:29:08 +0200 Subject: [PATCH 004/119] DDC-770 - Refactored EntityGenerator Bugfix NOT to generate a use statement. Simplifies code and circumvents further problems (like importing a class from the namespace we are in) --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 11 +---------- .../Tests/ORM/Tools/EntityGeneratorTest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 616c60a9d..230ab98b9 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -82,7 +82,7 @@ class EntityGenerator private static $_classTemplate = ' + @@ -189,7 +189,6 @@ public function () { $placeHolders = array( '', - '', '', '', '' @@ -197,7 +196,6 @@ public function () $replacements = array( $this->_generateEntityNamespace($metadata), - $this->_generateEntityUse($metadata), $this->_generateEntityDocBlock($metadata), $this->_generateEntityClassName($metadata), $this->_generateEntityBody($metadata) @@ -309,13 +307,6 @@ public function () } } - private function _generateEntityUse(ClassMetadataInfo $metadata) - { - if ($this->_extendsClass()) { - return "\n\nuse " . $this->_getClassToExtendName() . ";\n"; - } - } - private function _generateEntityClassName(ClassMetadataInfo $metadata) { return 'class ' . $this->_getClassName($metadata) . diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index ef4f51353..8a5431094 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -20,6 +20,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $this->_generator->setGenerateStubMethods(true); $this->_generator->setRegenerateEntityIfExists(false); $this->_generator->setUpdateEntityIfExists(true); + $this->_generator->setClassToExtend('stdClass'); } public function testWriteEntityClass() @@ -100,6 +101,18 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $this->assertTrue(strstr($code, 'public function getTest(') !== false); $this->assertTrue(strstr($code, 'public function setTest(') !== false); + return $metadata; + } + + /** + * @depends testEntityUpdatingWorks + * @param ClassMetadata $metadata + */ + public function testEntityExtendsStdClass($metadata) + { + $book = new \EntityGeneratorBook(); + $this->assertType('stdClass', $book); + unlink(__DIR__ . '/EntityGeneratorBook.php'); } } From 33d0bb454b361e4af2f5416ad033eacc5e7c1821 Mon Sep 17 00:00:00 2001 From: beberlei Date: Sat, 28 Aug 2010 16:41:18 +0200 Subject: [PATCH 005/119] DDC-752 - Moved verify inheritance block behind the loadMetadata event --- .../ORM/Mapping/ClassMetadataFactory.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 400970d6d..63c869b38 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -302,16 +302,6 @@ class ClassMetadataFactory $this->completeIdGeneratorMapping($class); } - // verify inheritance - if (!$parent && !$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { - if (count($class->discriminatorMap) == 0) { - throw MappingException::missingDiscriminatorMap($class->name); - } - if (!$class->discriminatorColumn) { - throw MappingException::missingDiscriminatorColumn($class->name); - } - } - if ($parent && $parent->isInheritanceTypeSingleTable()) { $class->setPrimaryTable($parent->table); } @@ -323,6 +313,16 @@ class ClassMetadataFactory $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); } + // verify inheritance + if (!$parent && !$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { + if (count($class->discriminatorMap) == 0) { + throw MappingException::missingDiscriminatorMap($class->name); + } + if (!$class->discriminatorColumn) { + throw MappingException::missingDiscriminatorColumn($class->name); + } + } + $this->loadedMetadata[$className] = $class; $parent = $class; From 0904bc5cc52954c8afaab97fabe86e42ee3f5a7c Mon Sep 17 00:00:00 2001 From: beberlei Date: Sun, 29 Aug 2010 11:19:23 +0200 Subject: [PATCH 006/119] DDC-762 - Added test for NULL association finding in OneToOne relations --- .../OneToOneUnidirectionalAssociationTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php index 01b2d00f9..3adde7c10 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php @@ -106,4 +106,20 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona )->fetchColumn(); $this->assertEquals($value, $foreignKey); } + + /** + * @group DDC-762 + */ + public function testNullForeignKey() + { + $product = new ECommerceProduct(); + $product->setName('Doctrine 2 Manual'); + + $this->_em->persist($product); + $this->_em->flush(); + + $product = $this->_em->find(get_class($product), $product->getId()); + + $this->assertNull($product->getShipping()); + } } From 0b5c694a7e1ed717954b3aac986e05da374e1713 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 30 Aug 2010 20:30:11 +0200 Subject: [PATCH 007/119] DDC-778 - Fix AbstractQuery::__clone implementation that was wrongly implemented in DDC-770. Added more tests. --- lib/Doctrine/ORM/AbstractQuery.php | 4 +++- lib/Doctrine/ORM/Query.php | 11 +++++++++++ tests/Doctrine/Tests/ORM/Query/QueryTest.php | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 21974b792..e578a7e70 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -583,6 +583,8 @@ abstract class AbstractQuery */ public function __clone() { - $this->free(); + $this->_params = array(); + $this->_paramTypes = array(); + $this->_hints = array(); } } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 4c755d0f4..f8338267b 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -544,4 +544,15 @@ final class Query extends AbstractQuery '&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT' ); } + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + parent::__clone(); + $this->_state = self::STATE_DIRTY; + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/QueryTest.php b/tests/Doctrine/Tests/ORM/Query/QueryTest.php index f606f0900..514c9d81c 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryTest.php @@ -44,4 +44,19 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array(), $query->getParameters()); } + + public function testClone() + { + $dql = "select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1"; + + $query = $this->_em->createQuery($dql); + $query->setParameter(2, 84, \PDO::PARAM_INT); + $query->setHint('foo', 'bar'); + + $cloned = clone $query; + + $this->assertEquals($dql, $cloned->getDql()); + $this->assertEquals(array(), $cloned->getParameters()); + $this->assertFalse($cloned->getHint('foo')); + } } \ No newline at end of file From 803e33836518b7c019db667f482601f55803c29d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 30 Aug 2010 23:35:54 +0200 Subject: [PATCH 008/119] DDC-684 - Fix flaw in build process. ORM not overwriting DBAL and Common dirs now, instead PEAR packages depend on it now, full package is generated for downloading. --- build.properties.dev | 2 ++ build.xml | 35 ++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/build.properties.dev b/build.properties.dev index e1ba8fb49..d0844332f 100644 --- a/build.properties.dev +++ b/build.properties.dev @@ -1,4 +1,6 @@ version=2.0.0BETA2 +dependencies.common=2.0.0BETA4 +dependencies.dbal=2.0.0BETA4 stability=beta build.dir=build dist.dir=dist diff --git a/build.xml b/build.xml index 024b63c89..b8a3ac2ff 100644 --- a/build.xml +++ b/build.xml @@ -51,7 +51,7 @@ Fileset for source of the Symfony YAML and Console components. --> - + - + - + - + - + - - + + @@ -142,7 +142,7 @@ Builds distributable PEAR packages. --> - + DoctrineORM Doctrine Object Relational Mapper pear.doctrine-project.org @@ -150,6 +150,7 @@ + LGPL @@ -157,17 +158,25 @@ + + script - - - + Doctrine/Common/ + Doctrine/DBAL/ - - + + + + + + + + + \ No newline at end of file From 7ff9976b3cbb3b95ff4c83368be5ca8a40bce0f8 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 31 Aug 2010 23:42:27 +0200 Subject: [PATCH 009/119] Made using schema-tool:drop and schema-tool:update more secure by requiring the user to confirm the operation with another flag --force. --- .../Command/SchemaTool/DropCommand.php | 19 ++++++++++++++++--- .../Command/SchemaTool/UpdateCommand.php | 19 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index 0d6741717..af6d8d026 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -49,13 +49,17 @@ class DropCommand extends AbstractCommand $this ->setName('orm:schema-tool:drop') ->setDescription( - 'Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output.' + 'Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.' ) ->setDefinition(array( new InputOption( 'dump-sql', null, InputOption::PARAMETER_NONE, 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' - ) + ), + new InputOption( + 'force', null, InputOption::PARAMETER_NONE, + "Don't ask for the deletion of the database, but force the operation to run." + ), )) ->setHelp(<<getOption('dump-sql') === true) { $sqls = $schemaTool->getDropSchemaSql($metadatas); $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); - } else { + } else if ($input->getOption('force') === true) { $output->write('Dropping database schema...' . PHP_EOL); $schemaTool->dropSchema($metadatas); $output->write('Database schema dropped successfully!' . PHP_EOL); + } else { + $sqls = $schemaTool->getDropSchemaSql($metadatas); + + if (count($sqls)) { + $output->write('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.' . PHP_EOL); + $output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL); + } else { + $output->write('Nothing to drop. The database is empty!' . PHP_EOL); + } } } } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 986d97954..193d8acc9 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -59,7 +59,11 @@ class UpdateCommand extends AbstractCommand new InputOption( 'dump-sql', null, InputOption::PARAMETER_NONE, 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' - ) + ), + new InputOption( + 'force', null, InputOption::PARAMETER_NONE, + "Don't ask for the deletion of the database, but force the operation to run." + ), )) ->setHelp(<<getOption('complete') === true); + $saveMode = ($input->getOption('complete') !== true); if ($input->getOption('dump-sql') === true) { $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); - } else { + } else if ($input->getOption('force') === true) { $output->write('Updating database schema...' . PHP_EOL); $schemaTool->updateSchema($metadatas, $saveMode); $output->write('Database schema updated successfully!' . PHP_EOL); + } else { + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); + + if (count($sqls)) { + $output->write('Schema-Tool would execute ' . count($sqls) . ' queries to update the database.' . PHP_EOL); + $output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL); + } else { + $output->write('Nothing to update. The database is in sync with the current entity metadata.' . PHP_EOL); + } } } } From 207d624f5f67c73dc5e4d484f6ba484c70dc6ce8 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Wed, 1 Sep 2010 20:43:23 +0200 Subject: [PATCH 010/119] Bumped Common dependency to RC1, fixing related issues in the test suite. --- lib/vendor/doctrine-common | 2 +- .../Tests/Models/ECommerce/ECommerceCategory.php | 8 ++++---- .../Tests/Models/ECommerce/ECommerceProduct.php | 11 +++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/vendor/doctrine-common b/lib/vendor/doctrine-common index 3b5123434..0bd0fa68b 160000 --- a/lib/vendor/doctrine-common +++ b/lib/vendor/doctrine-common @@ -1 +1 @@ -Subproject commit 3b5123434e979c7adfd42061484b5a8f10a3431b +Subproject commit 0bd0fa68bbdc4d81c7742f2c972b5ed0c02b0640 diff --git a/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php b/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php index ea2ade09d..a8d8dc6bb 100644 --- a/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php +++ b/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php @@ -73,8 +73,8 @@ class ECommerceCategory public function removeProduct(ECommerceProduct $product) { $removed = $this->products->removeElement($product); - if ($removed !== null) { - $removed->removeCategory($this); + if ($removed) { + $product->removeCategory($this); } } @@ -114,8 +114,8 @@ class ECommerceCategory public function removeChild(ECommerceCategory $child) { $removed = $this->children->removeElement($child); - if ($removed !== null) { - $removed->removeParent(); + if ($removed) { + $child->removeParent(); } } diff --git a/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php b/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php index 4851778e2..75f0f17c6 100644 --- a/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php +++ b/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php @@ -112,11 +112,10 @@ class ECommerceProduct public function removeFeature(ECommerceFeature $feature) { $removed = $this->features->removeElement($feature); - if ($removed !== null) { - $removed->removeProduct(); - return true; + if ($removed) { + $feature->removeProduct(); } - return false; + return $removed; } public function addCategory(ECommerceCategory $category) @@ -130,8 +129,8 @@ class ECommerceProduct public function removeCategory(ECommerceCategory $category) { $removed = $this->categories->removeElement($category); - if ($removed !== null) { - $removed->removeProduct($this); + if ($removed) { + $category->removeProduct($this); } } From 20af9d6d9fb1bb968e1da9ebfaf3bea4de4bca82 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Wed, 1 Sep 2010 20:47:01 +0200 Subject: [PATCH 011/119] Bumping DBAL dependency to BETA4. --- lib/vendor/doctrine-dbal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vendor/doctrine-dbal b/lib/vendor/doctrine-dbal index ce1e5f0f9..c49def311 160000 --- a/lib/vendor/doctrine-dbal +++ b/lib/vendor/doctrine-dbal @@ -1 +1 @@ -Subproject commit ce1e5f0f9c4f2e8894e1d2036ab11cc0597dd0a4 +Subproject commit c49def3112ffae7781e228677ae069454395110c From f415fa71749da32e2d6f700f0ee407a4b5a8a259 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Wed, 1 Sep 2010 20:57:12 +0200 Subject: [PATCH 012/119] Bumping dev. version. --- lib/Doctrine/ORM/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 9a23316ae..89f885a38 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.0.0BETA4-DEV'; + const VERSION = '2.0.0RC1-DEV'; /** * Compares a Doctrine version with the current one. From 24c6bb3f4691073787f743d9751a5f87a7f74a35 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Sep 2010 12:18:02 +0200 Subject: [PATCH 013/119] Fix errors in EntityRepository docblocks --- lib/Doctrine/ORM/EntityRepository.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 2e4a2191c..48ba9dc9d 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -133,7 +133,6 @@ class EntityRepository /** * Finds all entities in the repository. * - * @param int $hydrationMode * @return array The entities. */ public function findAll() @@ -144,8 +143,7 @@ class EntityRepository /** * Finds entities by a set of criteria. * - * @param string $column - * @param string $value + * @param array $criteria * @return array */ public function findBy(array $criteria) @@ -156,8 +154,7 @@ class EntityRepository /** * Finds a single entity by a set of criteria. * - * @param string $column - * @param string $value + * @param array $criteria * @return object */ public function findOneBy(array $criteria) From 13da816f4ea24012e8b8147592d6ab6b42defff9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 12 Sep 2010 21:41:22 +0200 Subject: [PATCH 014/119] DDC-748 - Fix bug in EntityManager::refresh() when entity has an owning side many-to-one bi-directional association --- .../ORM/Persisters/BasicEntityPersister.php | 2 +- .../ORM/Functional/Ticket/DDC748Test.php | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC748Test.php diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 93f6efa75..cc381e27b 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -653,7 +653,7 @@ class BasicEntityPersister if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) { $this->_class->reflFields[$field]->setValue($entity, $found); // Complete inverse side, if necessary. - if ($assoc['inversedBy']) { + if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) { $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']]; $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($found, $entity); } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC748Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC748Test.php new file mode 100644 index 000000000..1548552d7 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC748Test.php @@ -0,0 +1,64 @@ +useModelSet('cms'); + parent::setUp(); + } + + public function testRefreshWithManyToOne() + { + $user = new CmsUser(); + $user->name = "beberlei"; + $user->status = "active"; + $user->username = "beberlei"; + + $article = new CmsArticle(); + $article->setAuthor($user); + $article->text = "foo"; + $article->topic = "bar"; + + $this->_em->persist($user); + $this->_em->persist($article); + $this->_em->flush(); + + $this->assertType('Doctrine\Common\Collections\Collection', $user->articles); + $this->_em->refresh($article); + $this->assertTrue($article !== $user->articles, "The article should not be replaced on the inverse side of the relation."); + $this->assertType('Doctrine\Common\Collections\Collection', $user->articles); + } + + public function testRefreshOneToOne() + { + $user = new CmsUser(); + $user->name = "beberlei"; + $user->status = "active"; + $user->username = "beberlei"; + + $address = new CmsAddress(); + $address->city = "Bonn"; + $address->country = "Germany"; + $address->street = "A street"; + $address->zip = 12345; + $address->setUser($user); + + $this->_em->persist($user); + $this->_em->persist($address); + $this->_em->flush(); + + $this->_em->refresh($address); + $this->assertSame($user, $address->user); + $this->assertSame($user->address, $address); + } +} \ No newline at end of file From 4727489134d45442f993300c5f26ea6092156177 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 12 Sep 2010 22:34:32 +0200 Subject: [PATCH 015/119] DDC-761 - Fix join columns not using the same lengh, precision and scale for string and decimal types. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index fba93cde8..13834ae96 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -1,7 +1,5 @@ addColumn( $columnName, $class->getTypeOfColumn($joinColumn['referencedColumnName']), $columnOptions From ee9158ffb4f80268a9bb31905dd6f1284856eae3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 12 Sep 2010 22:44:02 +0200 Subject: [PATCH 016/119] DDC-749 - Refactor Query tests, add a test for setParameters() --- .../Tests/ORM/Functional/QueryTest.php | 27 +++++-------------- tests/Doctrine/Tests/ORM/Query/QueryTest.php | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php index 9a0de4bd2..c753960f8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php @@ -128,6 +128,13 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $user = $q->getSingleResult(); } + public function testSetParameters() + { + $q = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = ?1 AND u.status = ?2'); + $q->setParameters(array(1 => 'jwage', 2 => 'active')); + $users = $q->getResult(); + } + public function testIterateResult_IterativelyBuildUpUnitOfWork() { $article1 = new CmsArticle; @@ -175,26 +182,6 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $query = $this->_em->createQuery("SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a"); $articles = $query->iterate(); } - - public function testFluentQueryInterface() - { - $q = $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a"); - $q2 = $q->expireQueryCache(true) - ->setQueryCacheLifetime(3600) - ->setQueryCacheDriver(null) - ->expireResultCache(true) - ->setHint('foo', 'bar') - ->setHint('bar', 'baz') - ->setParameter(1, 'bar') - ->setParameters(array(2 => 'baz')) - ->setResultCacheDriver(null) - ->setResultCacheId('foo') - ->setDql('foo') - ->setFirstResult(10) - ->setMaxResults(10); - - $this->assertSame($q2, $q); - } /** * @expectedException Doctrine\ORM\NoResultException diff --git a/tests/Doctrine/Tests/ORM/Query/QueryTest.php b/tests/Doctrine/Tests/ORM/Query/QueryTest.php index 514c9d81c..98458c6bd 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryTest.php @@ -35,6 +35,13 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array(2 => 84), $query->getParameters()); } + public function testSetParameters() + { + $query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1"); + $query->setParameters(array(1 => 'foo', 2 => 'bar')); + $this->assertEquals(array(1 => 'foo', 2 => 'bar'), $query->getParameters()); + } + public function testFree() { $query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1"); @@ -59,4 +66,24 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array(), $cloned->getParameters()); $this->assertFalse($cloned->getHint('foo')); } + + public function testFluentQueryInterface() + { + $q = $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a"); + $q2 = $q->expireQueryCache(true) + ->setQueryCacheLifetime(3600) + ->setQueryCacheDriver(null) + ->expireResultCache(true) + ->setHint('foo', 'bar') + ->setHint('bar', 'baz') + ->setParameter(1, 'bar') + ->setParameters(array(2 => 'baz')) + ->setResultCacheDriver(null) + ->setResultCacheId('foo') + ->setDql('foo') + ->setFirstResult(10) + ->setMaxResults(10); + + $this->assertSame($q2, $q); + } } \ No newline at end of file From da63bad9c8cef978ecbc5dd968d17a5e11e4028c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 13 Sep 2010 21:48:25 +0200 Subject: [PATCH 017/119] DDC-762 - Fixed notice when mapping foreign keys to field having null values --- lib/Doctrine/ORM/UnitOfWork.php | 2 +- .../ORM/Functional/Ticket/DDC522Test.php | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 242d84b74..e3cd01568 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1881,7 +1881,7 @@ class UnitOfWork implements PropertyChangedListener if ($assoc['isOwningSide']) { $associatedId = array(); foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { - $joinColumnValue = $data[$srcColumn]; + $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null; if ($joinColumnValue !== null) { $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue; } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC522Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC522Test.php index dbfa024ca..d0e8f4c7d 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC522Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC522Test.php @@ -65,6 +65,23 @@ class DDC522Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue($fkt2->cart instanceof \Doctrine\ORM\Proxy\Proxy); $this->assertFalse($fkt2->cart->__isInitialized__); } + + /** + * @group DDC-522 + * @group DDC-762 + */ + public function testJoinColumnWithNullSameNameAssociationField() + { + $fkCust = new DDC522ForeignKeyTest; + $fkCust->name = "name"; + $fkCust->cart = null; + + $this->_em->persist($fkCust); + $this->_em->flush(); + $this->_em->clear(); + + $newCust = $this->_em->find(get_class($fkCust), $fkCust->id); + } } /** @Entity */ @@ -94,7 +111,7 @@ class DDC522Cart { class DDC522ForeignKeyTest { /** @Id @Column(type="integer") @GeneratedValue */ public $id; - /** @Column(type="integer", name="cart_id") */ + /** @Column(type="integer", name="cart_id", nullable="true") */ public $cartId; /** * @OneToOne(targetEntity="DDC522Cart") From 4845745337dbbed5eead25c3062a07103b38649f Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 15 Sep 2010 14:16:53 -0300 Subject: [PATCH 018/119] [DDC-802] Fixed wrong variable reference in XML exporter. --- lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php index 097e77083..75c0ca3e8 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -109,7 +109,7 @@ class XmlExporter extends AbstractExporter foreach ($metadata->table['uniqueConstraints'] as $unique) { $uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint'); - $uniqueConstraintXml->addAttribute('name', $name); + $uniqueConstraintXml->addAttribute('name', $unique['name']); $uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns'])); } } From 2e3c1506fbeec0f3598a71c8a9624b72c59e3674 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 15 Sep 2010 14:29:55 -0300 Subject: [PATCH 019/119] [DDC-792] Fixed issue with run-dql when using max result was triggering undefined method error. --- lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php index 678b045d3..7619534f7 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -114,7 +114,7 @@ EOT throw new \LogicException("Option 'max-result' must contains an integer value"); } - $query->setMaxResult((int) $maxResult); + $query->setMaxResults((int) $maxResult); } $resultSet = $query->execute(array(), constant($hydrationMode)); From 97e572e2d886a730f1b5d0dc2ccfd8653ef2eea5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 15 Sep 2010 21:51:44 +0200 Subject: [PATCH 020/119] DDC-806 - Fix xsd schema not allowing entities without id (in inheritance hierachies) --- doctrine-mapping.xsd | 2 +- .../ORM/Mapping/XmlMappingDriverTest.php | 21 +++++++++++++------ .../Tests/ORM/Mapping/xml/CatNoId.dcm.xml | 8 +++++++ 3 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Mapping/xml/CatNoId.dcm.xml diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 3a09f24b3..badd28f4f 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -63,7 +63,7 @@ - + diff --git a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php index 5c2edff5b..8308ea260 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php @@ -33,18 +33,27 @@ class XmlMappingDriverTest extends AbstractMappingDriverTest $this->assertEquals($expectedMap, $class->discriminatorMap); } - public function testValidateXmlSchema() + /** + * @param string $xmlMappingFile + * @dataProvider dataValidSchema + */ + public function testValidateXmlSchema($xmlMappingFile) { $xsdSchemaFile = __DIR__ . "/../../../../../doctrine-mapping.xsd"; $dom = new \DOMDocument('UTF-8'); - $dom->load(__DIR__ . "/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml"); - $this->assertTrue($dom->schemaValidate($xsdSchemaFile)); - - $dom = new \DOMDocument('UTF-8'); - $dom->load(__DIR__ . "/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml"); + $dom->load($xmlMappingFile); $this->assertTrue($dom->schemaValidate($xsdSchemaFile)); } + + static public function dataValidSchema() + { + return array( + array(__DIR__ . "/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml"), + array(__DIR__ . "/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml"), + array(__DIR__ . "/xml/CatNoId.dcm.xml"), + ); + } } class CTI diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/CatNoId.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/CatNoId.dcm.xml new file mode 100644 index 000000000..6025d350f --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/CatNoId.dcm.xml @@ -0,0 +1,8 @@ + + + + + From 810a129a3273a3826ecedb4b744a55e33a54a3ff Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 15 Sep 2010 22:11:09 +0200 Subject: [PATCH 021/119] DDC-767 - Add testcase that shows described behavior works and not produces notices. --- .../ORM/Functional/Ticket/DDC767Test.php | 74 +++++++++++++++++++ .../Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC767Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC767Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC767Test.php new file mode 100644 index 000000000..e3c3975ac --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC767Test.php @@ -0,0 +1,74 @@ +useModelSet('cms'); + parent::setUp(); + } + + /** + * @group DDC-767 + */ + public function testCollectionChangesInsideTransaction() + { + $user = new CmsUser(); + $user->name = "beberlei"; + $user->status = "active"; + $user->username = "beberlei"; + + $group1 = new CmsGroup(); + $group1->name = "foo"; + + $group2 = new CmsGroup(); + $group2->name = "bar"; + + $group3 = new CmsGroup(); + $group3->name = "baz"; + + $user->addGroup($group1); + $user->addGroup($group2); + + $this->_em->persist($user); + $this->_em->persist($group1); + $this->_em->persist($group2); + $this->_em->persist($group3); + + $this->_em->flush(); + $this->_em->clear(); + + /* @var $pUser CmsUser */ + $pUser = $this->_em->find(get_class($user), $user->id); + + $this->assertNotNull($pUser, "User not retrieved from database."); + + $groups = array(2, 3); + + try { + $this->_em->beginTransaction(); + + $pUser->groups->clear(); + + $this->_em->flush(); + + // Add new + foreach ($groups as $groupId) { + $pUser->addGroup($this->_em->find(get_class($group1), $groupId)); + } + + $this->_em->flush(); + $this->_em->commit(); + } catch(\Exception $e) { + $this->_em->rollback(); + } + } +} diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index a1bf2b2bf..2f305f9a8 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -264,7 +264,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase if(isset($this->_sqlLoggerStack->queries) && count($this->_sqlLoggerStack->queries)) { $queries = ""; - for($i = count($this->_sqlLoggerStack->queries)-1; $i > max(count($this->_sqlLoggerStack->queries)-25, 0); $i--) { + for($i = count($this->_sqlLoggerStack->queries)-1; $i > max(count($this->_sqlLoggerStack->queries)-25, 0) && isset($this->_sqlLoggerStack->queries[$i]); $i--) { $query = $this->_sqlLoggerStack->queries[$i]; $params = array_map(function($p) { if (is_object($p)) return get_class($p); else return "'".$p."'"; }, $query['params'] ?: array()); $queries .= ($i+1).". SQL: '".$query['sql']."' Params: ".implode(", ", $params).PHP_EOL; From d3419780f9daa3eaf0400c2b6fcee0234240b1de Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 15 Sep 2010 22:24:17 +0200 Subject: [PATCH 022/119] DDC-727 - Test shows expected behavior, no failure --- .../Tests/ORM/Functional/ReferenceProxyTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php b/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php index 38f9b8429..f144c7885 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php @@ -40,4 +40,15 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase $productProxy = $this->_factory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceProduct', array('id' => $id)); $this->assertEquals('Doctrine Cookbook', $productProxy->getName()); } + + /** + * @group DDC-727 + */ + public function testAccessMetatadaForProxy() + { + $entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , 1); + $class = $this->_em->getClassMetadata(get_class($entity)); + + $this->assertEquals('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $class->name); + } } From 9fa8ff86f8a64e6d70059b3046f88d8729f302d5 Mon Sep 17 00:00:00 2001 From: steffkes Date: Wed, 15 Sep 2010 21:14:15 +0200 Subject: [PATCH 023/119] show given path for MappingException::fileMappingDriversRequireConfiguredDirectoryPath --- .../ORM/Mapping/Driver/AbstractFileDriver.php | 2 +- lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php | 2 +- lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php | 2 +- lib/Doctrine/ORM/Mapping/MappingException.php | 11 +++++++++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php index 637971cb5..457b7cda7 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php @@ -155,7 +155,7 @@ abstract class AbstractFileDriver implements Driver if ($this->_paths) { foreach ((array) $this->_paths as $path) { if ( ! is_dir($path)) { - throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath(); + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new \RecursiveIteratorIterator( diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index d62370559..e531a27f7 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -435,7 +435,7 @@ class AnnotationDriver implements Driver foreach ($this->_paths as $path) { if ( ! is_dir($path)) { - throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath(); + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new \RecursiveIteratorIterator( diff --git a/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php b/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php index 01edab71b..d89b1ed68 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php @@ -78,7 +78,7 @@ class StaticPHPDriver implements Driver foreach ($this->_paths as $path) { if ( ! is_dir($path)) { - throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath(); + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new \RecursiveIteratorIterator( diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index 9fa5906bf..8ea5be213 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -170,9 +170,16 @@ class MappingException extends \Doctrine\ORM\ORMException ); } - public static function fileMappingDriversRequireConfiguredDirectoryPath() + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) { - return new self('File mapping drivers must have a valid directory path, however the given path seems to be incorrect!'); + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); } /** From 25f5ab6557b207cfa3e5506e85b01c77ee667a12 Mon Sep 17 00:00:00 2001 From: steffkes Date: Wed, 15 Sep 2010 21:22:26 +0200 Subject: [PATCH 024/119] add inverseBy-Attribute to User-Entity to get valide schema --- tools/sandbox/Entities/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sandbox/Entities/User.php b/tools/sandbox/Entities/User.php index 9f6af35e1..cd184212c 100644 --- a/tools/sandbox/Entities/User.php +++ b/tools/sandbox/Entities/User.php @@ -13,7 +13,7 @@ class User /** @Column(type="string", length=50) */ private $name; /** - * @OneToOne(targetEntity="Address") + * @OneToOne(targetEntity="Address", inversedBy="user") * @JoinColumn(name="address_id", referencedColumnName="id") */ private $address; From 72f65c366557bc75231d0caee8a6ba500e2e6114 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 20 Sep 2010 19:23:41 +0200 Subject: [PATCH 025/119] Some changes to the TestCase --- .../ORM/Mapping/YamlMappingDriverTest.php | 26 +++++++++---------- ...ts.ORM.Mapping.AbstractContentItem.dcm.yml | 1 - 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php index 1a60f7eb7..aa7ce716b 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php @@ -23,23 +23,21 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest { $em = $this->_getTestEntityManager(); $em->getConfiguration()->setMetadataDriverImpl(new \Doctrine\ORM\Mapping\Driver\YamlDriver(__DIR__ . '/yaml/')); - $qb = $em->createQueryBuilder(); - $qb->select('f') - ->from('Doctrine\Tests\ORM\Mapping\Page', 'f') - ->join('f.parentDirectory', 'd') - ->where( - $qb->expr()->andx( - $qb->expr()->eq('d.url', ':url'), - $qb->expr()->eq('f.extension', ':extension') - ) - ) - ->setParameter('url', "test") - ->setParameter('filename', "filename") - ->setParameter('extension', "extension"); + var_dump($em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Page')); + var_dump($em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Directory')); + + $dql = "SELECT f FROM Doctrine\Tests\ORM\Mapping\Page f JOIN f.parentDirectory d " . + "WHERE (d.url = :url) AND (f.extension = :extension)"; + + $query = $em->createQuery($dql) + ->setParameter('url', "test") + ->setParameter('extension', "extension"); + + var_dump($query->getSql()); // Is there a way to generalize this more? (Instead of a2_., check if the table prefix in the ON clause is set within a FROM or JOIN clause..) - $this->assertTrue(strpos($qb->getQuery()->getSql(), 'a2_.') === false); + $this->assertTrue(strpos($query->getSql(), 'a2_.') === false); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml index 419eb0449..9137619ec 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml @@ -1,6 +1,5 @@ Doctrine\Tests\ORM\Mapping\AbstractContentItem: type: mappedSuperclass - table: abstract_item id: id: type: integer From c70f32f4c92310ca6e28ed989c668e7b34b9ded8 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Sep 2010 00:32:07 +0200 Subject: [PATCH 026/119] DDC-809 - Fix nasty issue in ObjectHydrator yielding Many-To-Many hydration problems with multi-valued collections that are join-fetched. --- .../ORM/Internal/Hydration/ObjectHydrator.php | 11 +- .../ORM/Functional/Ticket/DDC809Test.php | 111 ++++++++++++++++ .../ORM/Hydration/ObjectHydratorTest.php | 120 ++++++++++++++++++ 3 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index a13119aea..ca937059b 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -269,6 +269,9 @@ class ObjectHydrator extends AbstractHydrator // It's a joined result $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias]; + // we need the $path to save into the identifier map which entities were already + // seen for this parent-child relationship + $path = $parentAlias . '.' . $dqlAlias; // Get a reference to the parent object to which the joined element belongs. if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) { @@ -298,8 +301,8 @@ class ObjectHydrator extends AbstractHydrator $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField); } - $indexExists = isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]]); - $index = $indexExists ? $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] : 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) { @@ -317,11 +320,11 @@ class ObjectHydrator extends AbstractHydrator $field = $this->_rsm->indexByMap[$dqlAlias]; $indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element); $reflFieldValue->hydrateSet($indexValue, $element); - $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $indexValue; + $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; } else { $reflFieldValue->hydrateAdd($element); $reflFieldValue->last(); - $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $reflFieldValue->key(); + $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); } // Update result pointer $this->_resultPointers[$dqlAlias] = $element; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php new file mode 100644 index 000000000..c0dcba90e --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php @@ -0,0 +1,111 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC809Variant'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC809SpecificationValue') + )); + + $conn = $this->_em->getConnection(); + $conn->insert('specification_value_test', array('specification_value_id' => 94589)); + $conn->insert('specification_value_test', array('specification_value_id' => 94593)); + $conn->insert('specification_value_test', array('specification_value_id' => 94606)); + $conn->insert('specification_value_test', array('specification_value_id' => 94607)); + $conn->insert('specification_value_test', array('specification_value_id' => 94609)); + $conn->insert('specification_value_test', array('specification_value_id' => 94711)); + + $conn->insert('variant_test', array('variant_id' => 545208)); + $conn->insert('variant_test', array('variant_id' => 545209)); + + $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94606)); + $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94607)); + $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94609)); + $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94711)); + + $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94589)); + $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94593)); + $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94606)); + $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94607)); + } + + /** + * @group DDC-809 + */ + public function testIssue() + { + $result = $this->_em->createQueryBuilder() + ->select('Variant, SpecificationValue') + ->from('Doctrine\Tests\ORM\Functional\Ticket\DDC809Variant', 'Variant') + ->leftJoin('Variant.SpecificationValues', 'SpecificationValue') + ->getQuery() + ->getResult(); + + $this->assertEquals(4, count($result[0]->getSpecificationValues()), "Works in test-setup."); + $this->assertEquals(4, count($result[1]->getSpecificationValues()), "Only returns 2 in the case of the hydration bug."); + } +} + +/** + * @Table(name="variant_test") + * @Entity + */ +class DDC809Variant +{ + /** + * @Column(name="variant_id", type="integer") + * @Id + * @GeneratedValue(strategy="AUTO") + */ + protected $variantId; + + /** + * @ManyToMany(targetEntity="DDC809SpecificationValue", inversedBy="Variants") + * @JoinTable(name="variant_specification_value_test", + * joinColumns={ + * @JoinColumn(name="variant_id", referencedColumnName="variant_id") + * }, + * inverseJoinColumns={ + * @JoinColumn(name="specification_value_id", referencedColumnName="specification_value_id") + * } + * ) + */ + protected $SpecificationValues; + + public function getSpecificationValues() + { + return $this->SpecificationValues; + } +} + +/** + * @Table(name="specification_value_test") + * @Entity + */ +class DDC809SpecificationValue +{ + /** + * @Column(name="specification_value_id", type="integer") + * @Id + * @GeneratedValue(strategy="AUTO") + */ + protected $specificationValueId; + + /** + * @var Variant + * + * @ManyToMany(targetEntity="DDC809Variant", mappedBy="SpecificationValues") + */ + protected $Variants; +} diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index 442961266..ecd50f237 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -888,4 +888,124 @@ class ObjectHydratorTest extends HydrationTestCase ++$rowNum; } } + + /** + * This issue tests if, with multiple joined multiple-valued collections the hydration is done correctly. + * + * User x Phonenumbers x Groups blow up the resultset quite a bit, however the hydration should correctly assemble those. + * + * @group DDC-809 + */ + public function testManyToManyHydration() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__name', 'name'); + $rsm->addJoinedEntityResult('Doctrine\Tests\Models\CMS\CmsGroup', 'g', 'u', 'groups'); + $rsm->addFieldResult('g', 'g__id', 'id'); + $rsm->addFieldResult('g', 'g__name', 'name'); + $rsm->addJoinedEntityResult('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', 'phonenumbers'); + $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber'); + + // Faked result set + $resultSet = array( + array( + 'u__id' => '1', + 'u__name' => 'romanb', + 'g__id' => '3', + 'g__name' => 'TestGroupB', + 'p__phonenumber' => 1111, + ), + array( + 'u__id' => '1', + 'u__name' => 'romanb', + 'g__id' => '5', + 'g__name' => 'TestGroupD', + 'p__phonenumber' => 1111, + ), + array( + 'u__id' => '1', + 'u__name' => 'romanb', + 'g__id' => '3', + 'g__name' => 'TestGroupB', + 'p__phonenumber' => 2222, + ), + array( + 'u__id' => '1', + 'u__name' => 'romanb', + 'g__id' => '5', + 'g__name' => 'TestGroupD', + 'p__phonenumber' => 2222, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '2', + 'g__name' => 'TestGroupA', + 'p__phonenumber' => 3333, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '3', + 'g__name' => 'TestGroupB', + 'p__phonenumber' => 3333, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '4', + 'g__name' => 'TestGroupC', + 'p__phonenumber' => 3333, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '5', + 'g__name' => 'TestGroupD', + 'p__phonenumber' => 3333, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '2', + 'g__name' => 'TestGroupA', + 'p__phonenumber' => 4444, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '3', + 'g__name' => 'TestGroupB', + 'p__phonenumber' => 4444, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '4', + 'g__name' => 'TestGroupC', + 'p__phonenumber' => 4444, + ), + array( + 'u__id' => '2', + 'u__name' => 'jwage', + 'g__id' => '5', + 'g__name' => 'TestGroupD', + 'p__phonenumber' => 4444, + ), + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + + $this->assertEquals(2, count($result)); + $this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsUser', $result); + $this->assertEquals(2, count($result[0]->groups)); + $this->assertEquals(2, count($result[0]->phonenumbers)); + $this->assertEquals(4, count($result[1]->groups)); + $this->assertEquals(2, count($result[1]->phonenumbers)); + } } From 62a8e2aad57cdf41fa4828cf7e3d88c53400aecb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Sep 2010 22:08:29 +0200 Subject: [PATCH 027/119] Enhance Schema-Tool commands by being more aggresive on warning the user that these commands do stuff that can be potentially dangerous to the database (and its contents). --- .../ORM/Tools/Console/Command/SchemaTool/CreateCommand.php | 2 ++ .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 2 ++ .../ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php | 6 +++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php index c3870221b..a772381b3 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -65,6 +65,8 @@ EOT protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) { + $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL); + if ($input->getOption('dump-sql') === true) { $sqls = $schemaTool->getCreateSchemaSql($metadatas); $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index af6d8d026..6e4811b0e 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -70,6 +70,8 @@ EOT protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) { + $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL); + if ($input->getOption('dump-sql') === true) { $sqls = $schemaTool->getDropSchemaSql($metadatas); $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 193d8acc9..0677347f9 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -62,7 +62,7 @@ class UpdateCommand extends AbstractCommand ), new InputOption( 'force', null, InputOption::PARAMETER_NONE, - "Don't ask for the deletion of the database, but force the operation to run." + "Don't ask for the incremental update of the database, but force the operation to run." ), )) ->setHelp(<<write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL); + $output->write('Use the incremental update to detect changes during development and use' . PHP_EOL); + $output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL); + // Defining if update is complete or not (--complete not defined means $saveMode = true) $saveMode = ($input->getOption('complete') !== true); From 7dc8ef1db9b55ab1a76f416d78e6bd882bbbd1d5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Sep 2010 23:14:45 +0200 Subject: [PATCH 028/119] Fix DDC-671 - The sourceEntity field has to be corrected to the subclass name when copied from a mapped superclass. Otherwise DQL queries will be wrong, generating wrong table aliases. --- .../ORM/Mapping/ClassMetadataFactory.php | 4 ++ .../ORM/Mapping/AbstractMappingDriverTest.php | 47 ++++++++++++- .../ORM/Mapping/AnnotationDriverTest.php | 28 ++++++++ .../ORM/Mapping/YamlMappingDriverTest.php | 66 ++++--------------- 4 files changed, 92 insertions(+), 53 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 63c869b38..51994f334 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -379,6 +379,10 @@ class ClassMetadataFactory private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) { foreach ($parentClass->associationMappings as $field => $mapping) { + if ($parentClass->isMappedSuperclass) { + $mapping['sourceEntity'] = $subClass->name; + } + //$subclassMapping = $mapping; if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { $mapping['inherited'] = $parentClass->name; diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 653cff7d7..ad1fa3d5c 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -458,4 +458,49 @@ class User 'initialValue' => 1, )); } -} \ No newline at end of file +} + +abstract class AbstractContentItem +{ + + /** + * Doctrine2 entity id + * @var integer + */ + private $id; + /** + * The parent directory + * @var Directory + */ + protected $parentDirectory; + /** + * Filename (without extension) or directory name + * @var string + */ + protected $name; +} + +class Directory extends AbstractContentItem +{ + + protected $subDirectories; + /** + * This is a collection of files that are contained in this Directory. Files, for example, could be Pages, but even other files + * like media files (css, images etc) are possible. + * + * @var \Doctrine\Common\Collections\Collection + * @access protected + */ + protected $containedFiles; + /** + * @var string + */ + protected $url; +} + +class Page extends AbstractContentItem +{ + + protected $extension = "html"; + +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php index 468ef680d..f28f7a6d4 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php @@ -109,6 +109,34 @@ class AnnotationDriverTest extends AbstractMappingDriverTest { new $entityClassName; } + + /** + * @group DDC-671 + * + * Entities for this test are in AbstractMappingDriverTest + */ + public function testJoinTablesWithMappedSuperclassForAnnotationDriver() + { + $em = $this->_getTestEntityManager(); + $em->getConfiguration()->setMetadataDriverImpl($this->_loadDriver()); + + $classPage = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Page'); + $this->assertEquals('Doctrine\Tests\ORM\Mapping\Page', $classPage->associationMappings['parentDirectory']['sourceEntity']); + $classDirectory = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Directory'); + $this->assertEquals('Doctrine\Tests\ORM\Mapping\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); + + $dql = "SELECT f FROM Doctrine\Tests\ORM\Mapping\Page f JOIN f.parentDirectory d " . + "WHERE (d.url = :url) AND (f.extension = :extension)"; + + $query = $em->createQuery($dql) + ->setParameter('url', "test") + ->setParameter('extension', "extension"); + + $this->assertEquals( + 'SELECT c0_.id AS id0, c0_.extension AS extension1, c0_.parent_directory_id AS parent_directory_id2 FROM core_content_pages c0_ INNER JOIN core_content_directories c1_ ON c0_.parent_directory_id = c1_.id WHERE (c1_.url = ?) AND (c0_.extension = ?)', + $query->getSql() + ); + } } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php index aa7ce716b..46e3ff15b 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php @@ -19,13 +19,20 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest return new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml'); } + /** + * @group DDC-671 + * + * Entities for this test are in AbstractMappingDriverTest + */ public function testJoinTablesWithMappedSuperclassForYamlDriver() { $em = $this->_getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl(new \Doctrine\ORM\Mapping\Driver\YamlDriver(__DIR__ . '/yaml/')); + $em->getConfiguration()->setMetadataDriverImpl($this->_loadDriver()); - var_dump($em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Page')); - var_dump($em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Directory')); + $classPage = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Page'); + $this->assertEquals('Doctrine\Tests\ORM\Mapping\Page', $classPage->associationMappings['parentDirectory']['sourceEntity']); + $classDirectory = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Directory'); + $this->assertEquals('Doctrine\Tests\ORM\Mapping\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); $dql = "SELECT f FROM Doctrine\Tests\ORM\Mapping\Page f JOIN f.parentDirectory d " . "WHERE (d.url = :url) AND (f.extension = :extension)"; @@ -34,55 +41,10 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest ->setParameter('url', "test") ->setParameter('extension', "extension"); - var_dump($query->getSql()); - - // Is there a way to generalize this more? (Instead of a2_., check if the table prefix in the ON clause is set within a FROM or JOIN clause..) - $this->assertTrue(strpos($query->getSql(), 'a2_.') === false); + $this->assertEquals( + 'SELECT c0_.id AS id0, c0_.extension AS extension1, c0_.parent_directory_id AS parent_directory_id2 FROM core_content_pages c0_ INNER JOIN core_content_directories c1_ ON c0_.parent_directory_id = c1_.id WHERE (c1_.url = ?) AND (c0_.extension = ?)', + $query->getSql() + ); } } - -class Directory extends AbstractContentItem -{ - - protected $subDirectories; - /** - * This is a collection of files that are contained in this Directory. Files, for example, could be Pages, but even other files - * like media files (css, images etc) are possible. - * - * @var \Doctrine\Common\Collections\Collection - * @access protected - */ - protected $containedFiles; - /** - * @var string - */ - protected $url; -} - -class Page extends AbstractContentItem -{ - - protected $extension = "html"; - -} - -abstract class AbstractContentItem -{ - - /** - * Doctrine2 entity id - * @var integer - */ - private $id; - /** - * The parent directory - * @var Directory - */ - protected $parentDirectory; - /** - * Filename (without extension) or directory name - * @var string - */ - protected $name; -} From 39f732ab9101ca58f3a8a21a2768680f449f080e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Sep 2010 23:53:26 +0200 Subject: [PATCH 029/119] Refactored DDC-671 model to become a first-class modelset, we dont have one with mapped superclass yet. --- .../DirectoryTree/AbstractContentItem.php | 49 +++++++++++++++++++ .../Tests/Models/DirectoryTree/Directory.php | 39 +++++++++++++++ .../Tests/Models/DirectoryTree/File.php | 30 ++++++++++++ .../ORM/Mapping/AbstractMappingDriverTest.php | 45 ----------------- .../ORM/Mapping/AnnotationDriverTest.php | 27 +++++----- .../ORM/Mapping/YamlMappingDriverTest.php | 27 +++++----- ....DirectoryTree.AbstractContentItem.dcm.yml | 14 ++++++ ...ts.Models.DirectoryTree.Directory.dcm.yml} | 9 ++-- ...e.Tests.Models.DirectoryTree.File.dcm.yml} | 3 +- ...ts.ORM.Mapping.AbstractContentItem.dcm.yml | 16 ------ 10 files changed, 159 insertions(+), 100 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php create mode 100644 tests/Doctrine/Tests/Models/DirectoryTree/Directory.php create mode 100644 tests/Doctrine/Tests/Models/DirectoryTree/File.php create mode 100644 tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml rename tests/Doctrine/Tests/ORM/Mapping/yaml/{Doctrine.Tests.ORM.Mapping.Directory.dcm.yml => Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml} (55%) rename tests/Doctrine/Tests/ORM/Mapping/yaml/{Doctrine.Tests.ORM.Mapping.Page.dcm.yml => Doctrine.Tests.Models.DirectoryTree.File.dcm.yml} (55%) delete mode 100644 tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php b/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php new file mode 100644 index 000000000..16a212c61 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\Tests\Models\DirectoryTree; + +/** + * @MappedSuperclass + */ +abstract class AbstractContentItem +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + private $id; + + /** + * @ManyToOne(targetEntity="Directory") + */ + protected $parentDirectory; + + /** @column(type="string") */ + protected $name; + + public function __get($name) + { + return $this->$name; + } + + public function __set($name, $value) + { + $this->$name = $value; + } +} diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php b/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php new file mode 100644 index 000000000..d337389ef --- /dev/null +++ b/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\Tests\Models\DirectoryTree; + +/** + * @Entity + */ +class Directory extends AbstractContentItem +{ + /** + * @OneToMany(targetEntity="Directory", mappedBy="parent") + */ + protected $subDirectories; + /** + * @OneToMany(targetEntity="File", mappedBy="parent") + */ + protected $containedFiles; + /** + * @Column(type="string") + */ + protected $path; +} diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/File.php b/tests/Doctrine/Tests/Models/DirectoryTree/File.php new file mode 100644 index 000000000..bae6323e3 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DirectoryTree/File.php @@ -0,0 +1,30 @@ +. + */ + + +namespace Doctrine\Tests\Models\DirectoryTree; + +/** + * @Entity + */ +class File extends AbstractContentItem +{ + /** @Column(type="string") */ + protected $extension = "html"; +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index ad1fa3d5c..12ccfb32f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -459,48 +459,3 @@ class User )); } } - -abstract class AbstractContentItem -{ - - /** - * Doctrine2 entity id - * @var integer - */ - private $id; - /** - * The parent directory - * @var Directory - */ - protected $parentDirectory; - /** - * Filename (without extension) or directory name - * @var string - */ - protected $name; -} - -class Directory extends AbstractContentItem -{ - - protected $subDirectories; - /** - * This is a collection of files that are contained in this Directory. Files, for example, could be Pages, but even other files - * like media files (css, images etc) are possible. - * - * @var \Doctrine\Common\Collections\Collection - * @access protected - */ - protected $containedFiles; - /** - * @var string - */ - protected $url; -} - -class Page extends AbstractContentItem -{ - - protected $extension = "html"; - -} diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php index f28f7a6d4..b34d727a7 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php @@ -117,25 +117,20 @@ class AnnotationDriverTest extends AbstractMappingDriverTest */ public function testJoinTablesWithMappedSuperclassForAnnotationDriver() { + $annotationDriver = $this->_loadDriver(); + $annotationDriver->addPaths(array(__DIR__ . '/../../Models/DirectoryTree/')); + $em = $this->_getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($this->_loadDriver()); + $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); + $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory($em); - $classPage = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Page'); - $this->assertEquals('Doctrine\Tests\ORM\Mapping\Page', $classPage->associationMappings['parentDirectory']['sourceEntity']); - $classDirectory = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Directory'); - $this->assertEquals('Doctrine\Tests\ORM\Mapping\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); + $classPage = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\File'); + $classPage = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\File'); + $this->assertEquals('Doctrine\Tests\Models\DirectoryTree\File', $classPage->associationMappings['parentDirectory']['sourceEntity']); - $dql = "SELECT f FROM Doctrine\Tests\ORM\Mapping\Page f JOIN f.parentDirectory d " . - "WHERE (d.url = :url) AND (f.extension = :extension)"; - - $query = $em->createQuery($dql) - ->setParameter('url', "test") - ->setParameter('extension', "extension"); - - $this->assertEquals( - 'SELECT c0_.id AS id0, c0_.extension AS extension1, c0_.parent_directory_id AS parent_directory_id2 FROM core_content_pages c0_ INNER JOIN core_content_directories c1_ ON c0_.parent_directory_id = c1_.id WHERE (c1_.url = ?) AND (c0_.extension = ?)', - $query->getSql() - ); + $classDirectory = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\Directory'); + $classDirectory = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\Directory'); + $this->assertEquals('Doctrine\Tests\Models\DirectoryTree\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php index 46e3ff15b..4e58485f1 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php @@ -26,25 +26,20 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest */ public function testJoinTablesWithMappedSuperclassForYamlDriver() { + $yamlDriver = $this->_loadDriver(); + $yamlDriver->addPaths(array(__DIR__ . DIRECTORY_SEPARATOR . 'yaml')); + $em = $this->_getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($this->_loadDriver()); + $em->getConfiguration()->setMetadataDriverImpl($yamlDriver); + $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory($em); - $classPage = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Page'); - $this->assertEquals('Doctrine\Tests\ORM\Mapping\Page', $classPage->associationMappings['parentDirectory']['sourceEntity']); - $classDirectory = $em->getClassMetadata('Doctrine\Tests\ORM\Mapping\Directory'); - $this->assertEquals('Doctrine\Tests\ORM\Mapping\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); + $classPage = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\File'); + $classPage = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\File'); + $this->assertEquals('Doctrine\Tests\Models\DirectoryTree\File', $classPage->associationMappings['parentDirectory']['sourceEntity']); - $dql = "SELECT f FROM Doctrine\Tests\ORM\Mapping\Page f JOIN f.parentDirectory d " . - "WHERE (d.url = :url) AND (f.extension = :extension)"; - - $query = $em->createQuery($dql) - ->setParameter('url', "test") - ->setParameter('extension', "extension"); - - $this->assertEquals( - 'SELECT c0_.id AS id0, c0_.extension AS extension1, c0_.parent_directory_id AS parent_directory_id2 FROM core_content_pages c0_ INNER JOIN core_content_directories c1_ ON c0_.parent_directory_id = c1_.id WHERE (c1_.url = ?) AND (c0_.extension = ?)', - $query->getSql() - ); + $classDirectory = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\Directory'); + $classDirectory = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\Directory'); + $this->assertEquals('Doctrine\Tests\Models\DirectoryTree\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml new file mode 100644 index 000000000..9c573a561 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml @@ -0,0 +1,14 @@ +Doctrine\Tests\Models\DirectoryTree\AbstractContentItem: + type: mappedSuperclass + id: + id: + type: integer + unsigned: true + generator: + strategy: AUTO + fields: + name: + type: string + manyToOne: + parentDirectory: + targetEntity: Doctrine\Tests\Models\DirectoryTree\Directory diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml similarity index 55% rename from tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml rename to tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml index 129ae6b28..c8393ee0f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Directory.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml @@ -1,17 +1,16 @@ -Doctrine\Tests\ORM\Mapping\Directory: +Doctrine\Tests\Models\DirectoryTree\Directory: type: entity - table: core_content_directories fields: - url: + path: type: string length: 255 oneToMany: subDirectories: - targetEntity: Doctrine\Tests\ORM\Mapping\Directory + targetEntity: Doctrine\Tests\Models\DirectoryTree\Directory mappedBy: parentDirectory cascade: [ all ] containedFiles: - targetEntity: Doctrine\Tests\ORM\Mapping\Page + targetEntity: Doctrine\Tests\Models\DirectoryTree\File mappedBy: parentDirectory cascade: [ remove ] diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.File.dcm.yml similarity index 55% rename from tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml rename to tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.File.dcm.yml index a3b0fb836..cbc8edfec 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Page.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.File.dcm.yml @@ -1,6 +1,5 @@ -Doctrine\Tests\ORM\Mapping\Page: +Doctrine\Tests\Models\DirectoryTree\File: type: entity - table: core_content_pages fields: extension: type: string diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml deleted file mode 100644 index 9137619ec..000000000 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.AbstractContentItem.dcm.yml +++ /dev/null @@ -1,16 +0,0 @@ -Doctrine\Tests\ORM\Mapping\AbstractContentItem: - type: mappedSuperclass - id: - id: - type: integer - unsigned: true - generator: - strategy: AUTO - fields: - # FilesystemIdentifier - manyToOne: - parentDirectory: - targetEntity: Doctrine\Tests\ORM\Mapping\Directory - joinColumn: - name: parent_directory_id - referencedColumnName: id From 13047aa12e248a7252ba24de6caeae8835030f2b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 22 Sep 2010 00:15:45 +0200 Subject: [PATCH 030/119] Fixed Mappedsuperclass Functional Test to work with new modelset and verify that relevant features work --- .../DirectoryTree/AbstractContentItem.php | 23 +++- .../Tests/Models/DirectoryTree/Directory.php | 18 +-- .../Tests/Models/DirectoryTree/File.php | 15 +++ .../ORM/Functional/MappedSuperclassTest.php | 111 ++++-------------- ...sts.Models.DirectoryTree.Directory.dcm.yml | 10 -- .../Doctrine/Tests/OrmFunctionalTestCase.php | 9 ++ 6 files changed, 76 insertions(+), 110 deletions(-) diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php b/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php index 16a212c61..200a88ab1 100644 --- a/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php +++ b/tests/Doctrine/Tests/Models/DirectoryTree/AbstractContentItem.php @@ -37,13 +37,28 @@ abstract class AbstractContentItem /** @column(type="string") */ protected $name; - public function __get($name) + public function __construct(Directory $parentDir = null) { - return $this->$name; + $this->parentDirectory = $parentDir; } - public function __set($name, $value) + public function getId() { - $this->$name = $value; + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getParent() + { + return $this->parentDirectory; } } diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php b/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php index d337389ef..f0db778b8 100644 --- a/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php +++ b/tests/Doctrine/Tests/Models/DirectoryTree/Directory.php @@ -24,16 +24,18 @@ namespace Doctrine\Tests\Models\DirectoryTree; */ class Directory extends AbstractContentItem { - /** - * @OneToMany(targetEntity="Directory", mappedBy="parent") - */ - protected $subDirectories; - /** - * @OneToMany(targetEntity="File", mappedBy="parent") - */ - protected $containedFiles; /** * @Column(type="string") */ protected $path; + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } } diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/File.php b/tests/Doctrine/Tests/Models/DirectoryTree/File.php index bae6323e3..353177c81 100644 --- a/tests/Doctrine/Tests/Models/DirectoryTree/File.php +++ b/tests/Doctrine/Tests/Models/DirectoryTree/File.php @@ -27,4 +27,19 @@ class File extends AbstractContentItem { /** @Column(type="string") */ protected $extension = "html"; + + public function __construct(Directory $parent = null) + { + parent::__construct($parent); + } + + public function getExtension() + { + return $this->extension; + } + + public function setExtension($ext) + { + $this->extension = $ext; + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php b/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php index 90ab33fa3..9b1f8d607 100644 --- a/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php @@ -12,100 +12,35 @@ require_once __DIR__ . '/../../TestInit.php'; class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase { protected function setUp() { + $this->useModelSet('directorytree'); parent::setUp(); - try { - $this->_schemaTool->createSchema(array( - $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\EntitySubClass'), - )); - } catch (\Exception $e) { - // Swallow all exceptions. We do not test the schema tool here. - } } public function testCRUD() { - $e = new EntitySubClass; - $e->setId(1); - $e->setName('Roman'); - $e->setMapped1(42); - $e->setMapped2('bar'); - - $this->_em->persist($e); + $root = new \Doctrine\Tests\Models\DirectoryTree\Directory(); + $root->setName('Root'); + $root->setPath('/root'); + + $directory = new \Doctrine\Tests\Models\DirectoryTree\Directory($root); + $directory->setName('TestA'); + $directory->setPath('/root/dir'); + + $file = new \Doctrine\Tests\Models\DirectoryTree\File($directory); + $file->setName('test-b.html'); + + $this->_em->persist($root); + $this->_em->persist($directory); + $this->_em->persist($file); + $this->_em->flush(); $this->_em->clear(); - - $e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\EntitySubClass', 1); - $this->assertEquals(1, $e2->getId()); - $this->assertEquals('Roman', $e2->getName()); - $this->assertNull($e2->getMappedRelated1()); - $this->assertEquals(42, $e2->getMapped1()); - $this->assertEquals('bar', $e2->getMapped2()); + + $cleanFile = $this->_em->find(get_class($file), $file->getId()); + + $this->assertType('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()); + $this->assertEquals($directory->getId(), $cleanFile->getParent()->getId()); + $this->assertType('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()->getParent()); + $this->assertEquals($root->getId(), $cleanFile->getParent()->getParent()->getId()); } } - -/** @MappedSuperclass */ -class MappedSuperclassBase { - /** @Column(type="integer") */ - private $mapped1; - /** @Column(type="string") */ - private $mapped2; - /** - * @OneToOne(targetEntity="MappedSuperclassRelated1") - * @JoinColumn(name="related1_id", referencedColumnName="id") - */ - private $mappedRelated1; - private $transient; - - public function setMapped1($val) { - $this->mapped1 = $val; - } - - public function getMapped1() { - return $this->mapped1; - } - - public function setMapped2($val) { - $this->mapped2 = $val; - } - - public function getMapped2() { - return $this->mapped2; - } - - public function getMappedRelated1() { - return $this->mappedRelated1; - } -} - -/** @Entity */ -class MappedSuperclassRelated1 { - /** @Id @Column(type="integer") */ - private $id; - /** @Column(type="string") */ - private $name; -} - -/** @Entity */ -class EntitySubClass extends MappedSuperclassBase { - /** @Id @Column(type="integer") */ - private $id; - /** @Column(type="string") */ - private $name; - - public function setName($name) { - $this->name = $name; - } - - public function getName() { - return $this->name; - } - - public function setId($id) { - $this->id = $id; - } - - public function getId() { - return $this->id; - } -} - diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml index c8393ee0f..d2b93d490 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml @@ -4,13 +4,3 @@ Doctrine\Tests\Models\DirectoryTree\Directory: path: type: string length: 255 - oneToMany: - subDirectories: - targetEntity: Doctrine\Tests\Models\DirectoryTree\Directory - mappedBy: parentDirectory - cascade: - [ all ] - containedFiles: - targetEntity: Doctrine\Tests\Models\DirectoryTree\File - mappedBy: parentDirectory - cascade: [ remove ] diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 2f305f9a8..5bb8cbf58 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -86,6 +86,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\Navigation\NavTour', 'Doctrine\Tests\Models\Navigation\NavPointOfInterest', ), + 'directorytree' => array( + 'Doctrine\Tests\Models\DirectoryTree\AbstractContentItem', + 'Doctrine\Tests\Models\DirectoryTree\File', + 'Doctrine\Tests\Models\DirectoryTree\Directory', + ), ); protected function useModelSet($setName) @@ -162,6 +167,10 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM navigation_tours'); $conn->executeUpdate('DELETE FROM navigation_countries'); } + if (isset($this->_usedModelSets['directorytree'])) { + $conn->executeUpdate('DELETE FROM File'); + $conn->executeUpdate('DELETE FROM Directory'); + } $this->_em->clear(); } From 75e5c40a50e2f8fc7147d4df44bb285df6ac3023 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 22 Sep 2010 23:01:08 +0200 Subject: [PATCH 031/119] DDC-742 - More tests on the issue about possible caching problem, could not verify however --- .../Tests/ORM/Functional/Ticket/DDC742Test.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC742Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC742Test.php index 3c19e0f3f..ad6585449 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC742Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC742Test.php @@ -11,6 +11,13 @@ class DDC742Test extends \Doctrine\Tests\OrmFunctionalTestCase protected function setUp() { parent::setUp(); + + if (\extension_loaded('memcache')) { + $this->_em->getMetadataFactory()->setCacheDriver(new \Doctrine\Common\Cache\MemcacheCache()); + } else if (\extension_loaded('apc')) { + $this->_em->getMetadataFactory()->setCacheDriver(new \Doctrine\Common\Cache\ApcCache()); + } + try { $this->_schemaTool->createSchema(array( $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC742User'), @@ -19,6 +26,10 @@ class DDC742Test extends \Doctrine\Tests\OrmFunctionalTestCase } catch(\Exception $e) { } + + // make sure classes will be deserialized from caches + $this->_em->getMetadataFactory()->setMetadataFor(__NAMESPACE__ . '\DDC742User', null); + $this->_em->getMetadataFactory()->setMetadataFor(__NAMESPACE__ . '\DDC742Comment', null); } public function testIssue() From 01ffa2dc9e2d2a4873563b7671bedd9b1727d580 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 23 Sep 2010 22:32:23 +0200 Subject: [PATCH 032/119] DDC-573 - Implement resetDQLPart() and resetDQLParts() methods on QueryBuilder. --- lib/Doctrine/ORM/QueryBuilder.php | 34 ++++++++++++++ tests/Doctrine/Tests/ORM/QueryBuilderTest.php | 44 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php index bcea7c000..27f65e14c 100644 --- a/lib/Doctrine/ORM/QueryBuilder.php +++ b/lib/Doctrine/ORM/QueryBuilder.php @@ -888,6 +888,40 @@ class QueryBuilder . (isset($options['post']) ? $options['post'] : ''); } + /** + * Reset DQL parts + * + * @param array $parts + * @return QueryBuilder + */ + public function resetDQLParts($parts = null) + { + if (is_null($parts)) { + $parts = array_keys($this->_dqlParts); + } + foreach ($parts as $part) { + $this->resetDQLPart($part); + } + return $this; + } + + /** + * Reset single DQL part + * + * @param string $part + * @return QueryBuilder; + */ + public function resetDQLPart($part) + { + if (is_array($this->_dqlParts[$part])) { + $this->_dqlParts[$part] = array(); + } else { + $this->_dqlParts[$part] = null; + } + $this->_state = self::STATE_DIRTY; + return $this; + } + /** * Gets a string representation of this QueryBuilder which corresponds to * the final DQL query being constructed. diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index e2430e8cc..1ad039bb9 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -525,4 +525,48 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase $this->assertValidQueryBuilder($qb, 'SELECT COUNT(e.id)'); } + + public function testResetDQLPart() + { + $qb = $this->_em->createQueryBuilder() + ->select('u') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->where('u.username = ?1')->orderBy('u.username'); + + $this->assertEquals('u.username = ?1', (string)$qb->getDQLPart('where')); + $this->assertEquals(1, count($qb->getDQLPart('orderBy'))); + + $qb->resetDqlPart('where')->resetDqlPart('orderBy'); + + $this->assertNull($qb->getDQLPart('where')); + $this->assertEquals(0, count($qb->getDQLPart('orderBy'))); + } + + public function testResetDQLParts() + { + $qb = $this->_em->createQueryBuilder() + ->select('u') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->where('u.username = ?1')->orderBy('u.username'); + + $qb->resetDQLParts(array('where', 'orderBy')); + + $this->assertEquals(1, count($qb->getDQLPart('select'))); + $this->assertNull($qb->getDQLPart('where')); + $this->assertEquals(0, count($qb->getDQLPart('orderBy'))); + } + + public function testResetAllDQLParts() + { + $qb = $this->_em->createQueryBuilder() + ->select('u') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->where('u.username = ?1')->orderBy('u.username'); + + $qb->resetDQLParts(); + + $this->assertEquals(0, count($qb->getDQLPart('select'))); + $this->assertNull($qb->getDQLPart('where')); + $this->assertEquals(0, count($qb->getDQLPart('orderBy'))); + } } \ No newline at end of file From 8f80c949230f84053432edc563f03ab7739532de Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 23 Sep 2010 23:10:31 +0200 Subject: [PATCH 033/119] DDC-514 - Implemented default for discriminator column --- .../ORM/Mapping/Driver/AnnotationDriver.php | 2 + lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 2 + .../ORM/Mapping/Driver/YamlDriver.php | 2 + .../ORM/Mapping/AbstractMappingDriverTest.php | 66 +++++++++++++++++-- .../Doctrine.Tests.ORM.Mapping.Animal.dcm.xml | 12 ++++ .../Doctrine.Tests.ORM.Mapping.Animal.dcm.yml | 6 ++ 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml create mode 100644 tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index e531a27f7..115d6dba9 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -179,6 +179,8 @@ class AnnotationDriver implements Driver 'type' => $discrColumnAnnot->type, 'length' => $discrColumnAnnot->length )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); } // Evaluate DiscriminatorMap annotation diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 7ffcd0001..91892e2b3 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -87,6 +87,8 @@ class XmlDriver extends AbstractFileDriver 'type' => (string)$discrColumn['type'], 'length' => (string)$discrColumn['length'] )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); } // Evaluate diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 21130c349..e714c50a0 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -79,6 +79,8 @@ class YamlDriver extends AbstractFileDriver 'type' => $discrColumn['type'], 'length' => $discrColumn['length'] )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); } // Evaluate discriminatorMap diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 12ccfb32f..8dd34a6c4 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -13,17 +13,22 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase { abstract protected function _loadDriver(); - public function testLoadMapping() + public function createClassMetadata($entityClassName) { - $className = 'Doctrine\Tests\ORM\Mapping\User'; $mappingDriver = $this->_loadDriver(); - $class = new ClassMetadata($className); - $mappingDriver->loadMetadataForClass($className, $class); + $class = new ClassMetadata($entityClassName); + $mappingDriver->loadMetadataForClass($entityClassName, $class); return $class; } + public function testLoadMapping() + { + $entityClassName = 'Doctrine\Tests\ORM\Mapping\User'; + return $this->createClassMetadata($entityClassName); + } + /** * @depends testLoadMapping * @param ClassMetadata $class @@ -269,6 +274,23 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase return $class; } + + /** + * @group DDC-514 + */ + public function testDiscriminatorColumnDefaults() + { + if (strpos(get_class($this), 'PHPMappingDriver') !== false) { + $this->markTestSkipped('PHP Mapping Drivers have no defaults.'); + } + + $class = $this->createClassMetadata('Doctrine\Tests\ORM\Mapping\Animal'); + + $this->assertEquals( + array('name' => 'dtype', 'type' => 'string', 'length' => 255, 'fieldName' => 'dtype'), + $class->discriminatorColumn + ); + } } /** @@ -459,3 +481,39 @@ class User )); } } + +/** + * @Entity + * @InheritanceType("SINGLE_TABLE") + * @DiscriminatorMap({"cat" = "Cat", "dog" = "Dog"}) + */ +abstract class Animal +{ + /** + * @Id @Column(type="string") @GeneratedValue + */ + public $id; + + public static function loadMetadata(ClassMetadataInfo $metadata) + { + + } +} + +/** @Entity */ +class Cat extends Animal +{ + public static function loadMetadata(ClassMetadataInfo $metadata) + { + + } +} + +/** @Entity */ +class Dog extends Animal +{ + public static function loadMetadata(ClassMetadataInfo $metadata) + { + + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml new file mode 100644 index 000000000..6c2a2356f --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml new file mode 100644 index 000000000..cd6ec292c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml @@ -0,0 +1,6 @@ +Doctrine\Tests\ORM\Mapping\Animal: + type: entity + inheritanceType: SINGLE_TABLE + discriminatorMap: + cat: Cat + dog: Dog \ No newline at end of file From 039293c27ad4183237c2ddd89f11deab6c7d6485 Mon Sep 17 00:00:00 2001 From: beberlei Date: Sat, 25 Sep 2010 12:12:19 +0200 Subject: [PATCH 034/119] DDC-816 - Fix output of warning message in update and drop schema-tool commands --- .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 4 ++-- .../Tools/Console/Command/SchemaTool/UpdateCommand.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index 6e4811b0e..df6167d64 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -70,8 +70,6 @@ EOT protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) { - $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL); - if ($input->getOption('dump-sql') === true) { $sqls = $schemaTool->getDropSchemaSql($metadatas); $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); @@ -80,6 +78,8 @@ EOT $schemaTool->dropSchema($metadatas); $output->write('Database schema dropped successfully!' . PHP_EOL); } else { + $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL); + $sqls = $schemaTool->getDropSchemaSql($metadatas); if (count($sqls)) { diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 0677347f9..f553a8c4b 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -75,10 +75,6 @@ EOT protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) { - $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL); - $output->write('Use the incremental update to detect changes during development and use' . PHP_EOL); - $output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL); - // Defining if update is complete or not (--complete not defined means $saveMode = true) $saveMode = ($input->getOption('complete') !== true); @@ -90,6 +86,10 @@ EOT $schemaTool->updateSchema($metadatas, $saveMode); $output->write('Database schema updated successfully!' . PHP_EOL); } else { + $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL); + $output->write('Use the incremental update to detect changes during development and use' . PHP_EOL); + $output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL); + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); if (count($sqls)) { From d2630ff54e25a2b877db7c49e9926acf46db1ca6 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 27 Sep 2010 21:03:12 +0200 Subject: [PATCH 035/119] DDC-819 - Fix bug with invalid parameter exception because of using isset instead of array_key_exists() --- lib/Doctrine/ORM/EntityRepository.php | 2 +- .../ORM/Functional/EntityRepositoryTest.php | 63 +++++++++++++++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 48ba9dc9d..b458a0aeb 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -185,7 +185,7 @@ class EntityRepository ); } - if ( ! isset($arguments[0])) { + if ( ! array_key_exists(0, $arguments)) { throw ORMException::findByRequiresParameter($method.$by); } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 15ad790f9..8916d0a54 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -19,7 +19,8 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase parent::setUp(); } - public function testBasicFinders() { + public function loadFixture() + { $user = new CmsUser; $user->name = 'Roman'; $user->username = 'romanb'; @@ -38,35 +39,58 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase unset($user2); $this->_em->clear(); + return $user1Id; + } + + public function testBasicFind() + { + $user1Id = $this->loadFixture(); $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $user = $repos->find($user1Id); $this->assertTrue($user instanceof CmsUser); $this->assertEquals('Roman', $user->name); $this->assertEquals('freak', $user->status); + } - $this->_em->clear(); + public function testFindByField() + { + $user1Id = $this->loadFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $users = $repos->findBy(array('status' => 'dev')); $this->assertEquals(1, count($users)); $this->assertTrue($users[0] instanceof CmsUser); $this->assertEquals('Guilherme', $users[0]->name); $this->assertEquals('dev', $users[0]->status); + } - $this->_em->clear(); + + public function testFindFieldByMagicCall() + { + $user1Id = $this->loadFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $users = $repos->findByStatus('dev'); $this->assertEquals(1, count($users)); $this->assertTrue($users[0] instanceof CmsUser); $this->assertEquals('Guilherme', $users[0]->name); $this->assertEquals('dev', $users[0]->status); - - $this->_em->clear(); + } + + public function testFindAll() + { + $user1Id = $this->loadFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $users = $repos->findAll(); $this->assertEquals(2, count($users)); + } - $this->_em->clear(); + public function testFindByAlias() + { + $user1Id = $this->loadFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $this->_em->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS'); @@ -74,8 +98,12 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $users = $repos->findAll(); $this->assertEquals(2, count($users)); + } + public function tearDown() + { $this->_em->getConfiguration()->setEntityNamespaces(array()); + parent::tearDown(); } /** @@ -150,5 +178,28 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->setExpectedException('Doctrine\ORM\OptimisticLockException'); $this->_em->find('Doctrine\Tests\Models\Cms\CmsUser', $userId, \Doctrine\DBAL\LockMode::OPTIMISTIC); } + + /** + * @group DDC-819 + */ + public function testFindMagicCallByNullValue() + { + $this->loadFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + $users = $repos->findByStatus(null); + $this->assertEquals(0, count($users)); + } + + /** + * @group DDC-819 + */ + public function testInvalidMagicCall() + { + $this->setExpectedException('BadMethodCallException'); + + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + $repos->foo(); + } } From 140ddf5098a7ffdf6bc3f3dd9d444c85fc09f8aa Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 27 Sep 2010 22:13:14 +0200 Subject: [PATCH 036/119] DDC-817 - Add possibility to query by owning side association join column ids and tests for plain entities, single- and joined table inheritance --- lib/Doctrine/ORM/EntityRepository.php | 5 +- lib/Doctrine/ORM/ORMException.php | 8 ++ .../ORM/Persisters/BasicEntityPersister.php | 13 +++ .../Functional/ClassTableInheritanceTest.php | 32 +++++++ .../ORM/Functional/EntityRepositoryTest.php | 91 ++++++++++++++++++- .../Functional/SingleTableInheritanceTest.php | 24 +++++ 6 files changed, 170 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index b458a0aeb..81680647c 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -185,13 +185,14 @@ class EntityRepository ); } - if ( ! array_key_exists(0, $arguments)) { + if ( !isset($arguments[0])) { + // we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL. throw ORMException::findByRequiresParameter($method.$by); } $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); - if ($this->_class->hasField($fieldName)) { + if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { return $this->$method(array($fieldName => $arguments[0])); } else { throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); diff --git a/lib/Doctrine/ORM/ORMException.php b/lib/Doctrine/ORM/ORMException.php index a4a593470..c84dec41e 100644 --- a/lib/Doctrine/ORM/ORMException.php +++ b/lib/Doctrine/ORM/ORMException.php @@ -78,6 +78,14 @@ class ORMException extends Exception ); } + public static function invalidFindByInverseAssociation($entityName, $associationFieldName) + { + return new self( + "You cannot search for the association field '".$entityName."#".$associationFieldName."', ". + "because it is the inverse side of an association. Find methods only work on owning side associations." + ); + } + public static function invalidResultCacheDriver() { return new self("Invalid result cache driver; it must implement \Doctrine\Common\Cache\Cache."); } diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index cc381e27b..51d5c5121 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1120,6 +1120,19 @@ class BasicEntityPersister $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'; } $conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform); + } else if (isset($this->_class->associationMappings[$field])) { + if (!$this->_class->associationMappings[$field]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field); + } + + if (isset($this->_class->associationMappings[$field]['inherited'])) { + $conditionSql .= $this->_getSQLTableAlias($this->_class->associationMappings[$field]['inherited']) . '.'; + } else { + $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'; + } + + + $conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name']; } else if ($assoc !== null) { if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) { $owningAssoc = $assoc['isOwningSide'] ? $assoc : $this->_em->getClassMetadata($assoc['targetEntity']) diff --git a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php index 86075e854..24a4e4ad7 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -353,4 +353,36 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($manager->getId(), $dqlManager->getId()); $this->assertEquals($person->getId(), $dqlManager->getSpouse()->getId()); } + + /** + * @group DDC-817 + */ + public function testFindByAssociation() + { + $manager = new CompanyManager(); + $manager->setName('gblanco'); + $manager->setSalary(1234); + $manager->setTitle('Awesome!'); + $manager->setDepartment('IT'); + + $person = new CompanyPerson(); + $person->setName('spouse'); + + $manager->setSpouse($person); + + $this->_em->persist($manager); + $this->_em->persist($person); + $this->_em->flush(); + $this->_em->clear(); + + $repos = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyManager'); + $pmanager = $repos->findOneBy(array('spouse' => $person->getId())); + + $this->assertEquals($manager->getId(), $pmanager->getId()); + + $repos = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyPerson'); + $pmanager = $repos->findOneBy(array('spouse' => $person->getId())); + + $this->assertEquals($manager->getId(), $pmanager->getId()); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 8916d0a54..d6c29e016 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsPhonenumber; +use Doctrine\Tests\Models\CMS\CmsAddress; require_once __DIR__ . '/../../TestInit.php'; @@ -187,8 +188,8 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->loadFixture(); $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + $this->setExpectedException('Doctrine\ORM\ORMException'); $users = $repos->findByStatus(null); - $this->assertEquals(0, count($users)); } /** @@ -201,5 +202,93 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $repos->foo(); } + + public function loadAssociatedFixture() + { + $address = new CmsAddress(); + $address->city = "Berlin"; + $address->country = "Germany"; + $address->street = "Foostreet"; + $address->zip = "12345"; + + $user = new CmsUser(); + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'freak'; + $user->setAddress($address); + + $this->_em->persist($user); + $this->_em->persist($address); + $this->_em->flush(); + $this->_em->clear(); + + return array($user->id, $address->id); + } + + /** + * @group DDC-817 + */ + public function testFindByAssociationKey_ExceptionOnInverseSide() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + $this->setExpectedException('Doctrine\ORM\ORMException', "You cannot search for the association field 'Doctrine\Tests\Models\CMS\CmsUser#address', because it is the inverse side of an association. Find methods only work on owning side associations."); + $user = $repos->findBy(array('address' => $addressId)); + } + + /** + * @group DDC-817 + */ + public function testFindOneByAssociationKey() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $address = $repos->findOneBy(array('user' => $userId)); + + $this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address); + $this->assertEquals($addressId, $address->id); + } + + /** + * @group DDC-817 + */ + public function testFindByAssociationKey() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $addresses = $repos->findBy(array('user' => $userId)); + + $this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsAddress', $addresses); + $this->assertEquals(1, count($addresses)); + $this->assertEquals($addressId, $addresses[0]->id); + } + + /** + * @group DDC-817 + */ + public function testFindAssociationByMagicCall() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $addresses = $repos->findByUser($userId); + + $this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsAddress', $addresses); + $this->assertEquals(1, count($addresses)); + $this->assertEquals($addressId, $addresses[0]->id); + } + + /** + * @group DDC-817 + */ + public function testFindOneAssociationByMagicCall() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $address = $repos->findOneByUser($userId); + + $this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address); + $this->assertEquals($addressId, $address->id); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php index fb9c93abf..fc303039f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -308,4 +308,28 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertNull($this->_em->find(get_class($this->fix), $this->fix->getId()), "Contract should not be present in the database anymore."); } + + /** + * @group DDC-817 + */ + public function testFindByAssociation() + { + $this->loadFullFixture(); + + $repos = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyContract"); + $contracts = $repos->findBy(array('salesPerson' => $this->salesPerson->getId())); + $this->assertEquals(3, count($contracts), "There should be 3 entities related to " . $this->salesPerson->getId() . " for 'Doctrine\Tests\Models\Company\CompanyContract'"); + + $repos = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyFixContract"); + $contracts = $repos->findBy(array('salesPerson' => $this->salesPerson->getId())); + $this->assertEquals(1, count($contracts), "There should be 1 entities related to " . $this->salesPerson->getId() . " for 'Doctrine\Tests\Models\Company\CompanyFixContract'"); + + $repos = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyFlexContract"); + $contracts = $repos->findBy(array('salesPerson' => $this->salesPerson->getId())); + $this->assertEquals(2, count($contracts), "There should be 2 entities related to " . $this->salesPerson->getId() . " for 'Doctrine\Tests\Models\Company\CompanyFlexContract'"); + + $repos = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyFlexUltraContract"); + $contracts = $repos->findBy(array('salesPerson' => $this->salesPerson->getId())); + $this->assertEquals(1, count($contracts), "There should be 1 entities related to " . $this->salesPerson->getId() . " for 'Doctrine\Tests\Models\Company\CompanyFlexUltraContract'"); + } } From 394469d4b7b9d9442ab15fdd0e4e81c186cd2fc5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 27 Sep 2010 23:22:52 +0200 Subject: [PATCH 037/119] DDC-525 - Single Table Inheritance fields of child entities ALWAYS have to be nullable, Schema-Tool now enforces this. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 3 +++ tests/Doctrine/Tests/Models/Company/CompanyFixContract.php | 2 +- tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php | 4 ++-- .../Tests/Models/Company/CompanyFlexUltraContract.php | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 13834ae96..d201d1829 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -299,6 +299,9 @@ class SchemaTool $options = array(); $options['length'] = isset($mapping['length']) ? $mapping['length'] : null; $options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true; + if ($class->isInheritanceTypeSingleTable() && count($class->parentClasses) > 0) { + $options['notnull'] = false; + } $options['platformOptions'] = array(); $options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false; diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php index 2bdba6850..9186fc3b1 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php @@ -8,7 +8,7 @@ namespace Doctrine\Tests\Models\Company; class CompanyFixContract extends CompanyContract { /** - * @column(type="integer", nullable=true) + * @column(type="integer") * @var int */ private $fixPrice = 0; diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php index c8f2fea1f..11f966f17 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php @@ -7,13 +7,13 @@ namespace Doctrine\Tests\Models\Company; class CompanyFlexContract extends CompanyContract { /** - * @column(type="integer", nullable=true) + * @column(type="integer") * @var int */ private $hoursWorked = 0; /** - * @column(type="integer", nullable=true) + * @column(type="integer") * @var int */ private $pricePerHour = 0; diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php index 9344bf36b..b9ad3d4c9 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php @@ -8,7 +8,7 @@ namespace Doctrine\Tests\Models\Company; class CompanyFlexUltraContract extends CompanyFlexContract { /** - * @column(type="integer", nullable=true) + * @column(type="integer") * @var int */ private $maxPrice = 0; From 3ad429a5aac5a5090e0c9e6e316e0225e0edb9b0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 28 Sep 2010 22:36:26 +0200 Subject: [PATCH 038/119] Fix bug in OrmFunctionalTestCase in combination with vendors that dont deferr foreign key checks (like MySQL) --- tests/Doctrine/Tests/OrmFunctionalTestCase.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 5bb8cbf58..c39798aa6 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -106,7 +106,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn = $this->sharedFixture['conn']; $this->_sqlLoggerStack->enabled = false; - + if (isset($this->_usedModelSets['cms'])) { $conn->executeUpdate('DELETE FROM cms_users_groups'); $conn->executeUpdate('DELETE FROM cms_groups'); @@ -116,7 +116,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM cms_articles'); $conn->executeUpdate('DELETE FROM cms_users'); } - + if (isset($this->_usedModelSets['ecommerce'])) { $conn->executeUpdate('DELETE FROM ecommerce_carts_products'); $conn->executeUpdate('DELETE FROM ecommerce_products_categories'); @@ -129,7 +129,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('UPDATE ecommerce_categories SET parent_id = NULL'); $conn->executeUpdate('DELETE FROM ecommerce_categories'); } - + if (isset($this->_usedModelSets['company'])) { $conn->executeUpdate('DELETE FROM company_contract_employees'); $conn->executeUpdate('DELETE FROM company_contracts'); @@ -144,7 +144,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM company_events'); $conn->executeUpdate('DELETE FROM company_organizations'); } - + if (isset($this->_usedModelSets['generic'])) { $conn->executeUpdate('DELETE FROM boolean_model'); $conn->executeUpdate('DELETE FROM date_time_model'); @@ -169,6 +169,8 @@ abstract class OrmFunctionalTestCase extends OrmTestCase } if (isset($this->_usedModelSets['directorytree'])) { $conn->executeUpdate('DELETE FROM File'); + // MySQL doesnt know deferred deletions therefore only executing the second query gives errors. + $conn->executeUpdate('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL'); $conn->executeUpdate('DELETE FROM Directory'); } From de236e0456c1e6d6f3c9ee4a0305d1edd61fe1a3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 30 Sep 2010 20:57:14 +0200 Subject: [PATCH 039/119] DDC-784 - Implement doctrine CLI for Windows and refactor CLI stuff in general --- bin/doctrine.bat | 9 +++ bin/doctrine.php | 46 +++++-------- build.xml | 1 + .../ORM/Tools/Console/ConsoleRunner.php | 69 +++++++++++++++++++ 4 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 bin/doctrine.bat create mode 100644 lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php diff --git a/bin/doctrine.bat b/bin/doctrine.bat new file mode 100644 index 000000000..a9e8ceefd --- /dev/null +++ b/bin/doctrine.bat @@ -0,0 +1,9 @@ +@echo off + +if "%PHPBIN%" == "" set PHPBIN=@php_bin@ +if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH +GOTO RUN +:USE_PEAR_PATH +set PHPBIN=%PHP_PEAR_PHP_BIN% +:RUN +"%PHPBIN%" "@bin_dir@\doctrine" %* diff --git a/bin/doctrine.php b/bin/doctrine.php index a94cdff67..701038488 100644 --- a/bin/doctrine.php +++ b/bin/doctrine.php @@ -1,4 +1,21 @@ . + */ require_once 'Doctrine/Common/ClassLoader.php'; @@ -19,7 +36,7 @@ if (file_exists($configFile)) { } require $configFile; - + foreach ($GLOBALS as $helperSetCandidate) { if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { $helperSet = $helperSetCandidate; @@ -30,29 +47,4 @@ if (file_exists($configFile)) { $helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); -$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\ORM\Version::VERSION); -$cli->setCatchExceptions(true); -$cli->setHelperSet($helperSet); -$cli->addCommands(array( - // DBAL Commands - new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), - new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), - - // ORM Commands - new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(), - new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(), - new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(), - new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(), - new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(), - new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(), - new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(), - new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(), - new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(), - new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(), - new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(), - new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(), - new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(), - new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(), - -)); -$cli->run(); +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet); diff --git a/build.xml b/build.xml index b8a3ac2ff..1bf835843 100644 --- a/build.xml +++ b/build.xml @@ -167,6 +167,7 @@ + diff --git a/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php b/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php new file mode 100644 index 000000000..f74713a7c --- /dev/null +++ b/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; + +class ConsoleRunner +{ + /** + * Run console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @return void + */ + static public function run(HelperSet $helperSet) + { + $cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION); + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + self::addCommands($cli); + $cli->run(); + } + + /** + * @param Application $cli + */ + static public function addCommands(Application $cli) + { + $cli->addCommands(array( + // DBAL Commands + new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + + // ORM Commands + new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(), + new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(), + new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(), + new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(), + )); + } +} \ No newline at end of file From 638c3df3a6fb648397d7b2bf3329c31d99cd401c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 30 Sep 2010 21:59:01 +0200 Subject: [PATCH 040/119] DDC-822 - Fix making queries with detached entities --- lib/Doctrine/ORM/Query.php | 10 +++++-- .../ORM/Functional/DetachedEntityTest.php | 29 +++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index f8338267b..57aacf77c 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -242,10 +242,14 @@ final class Query extends AbstractQuery } if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { - //TODO: Check that $value is MANAGED? - $values = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) { + $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $class = $this->_em->getClassMetadata(get_class($value)); + $idValuess = $class->getIdentifierValues($value); + } $sqlPositions = $paramMappings[$key]; - $sqlParams += array_combine((array)$sqlPositions, $values); + $sqlParams += array_combine((array)$sqlPositions, $idValues); } else { foreach ($paramMappings[$key] as $position) { $sqlParams[$position] = $value; diff --git a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php index eee01fadf..16f3ce602 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php @@ -105,8 +105,8 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase } catch (\Exception $expected) {} } - public function testUninitializedLazyAssociationsAreIgnoredOnMerge() { - //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + public function testUninitializedLazyAssociationsAreIgnoredOnMerge() + { $user = new CmsUser; $user->name = 'Guilherme'; $user->username = 'gblanco'; @@ -136,5 +136,30 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertFalse($managedAddress2->user === $detachedAddress2->user); $this->assertFalse($managedAddress2->user->__isInitialized__); } + + /** + * @group DDC-822 + */ + public function testUseDetachedEntityAsQueryParameter() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $this->_em->persist($user); + + $this->_em->flush(); + $this->_em->detach($user); + + $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1"; + $query = $this->_em->createQuery($dql); + $query->setParameter(1, $user); + + $newUser = $query->getSingleResult(); + + $this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $newUser); + $this->assertEquals('gblanco', $newUser->username); + } } From b05e1ad7ad7fe8bfb7253af790ca87199474658f Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 1 Oct 2010 21:05:54 +0200 Subject: [PATCH 041/119] Fix typo in last patch --- lib/Doctrine/ORM/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 57aacf77c..d428e2ddb 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -246,7 +246,7 @@ final class Query extends AbstractQuery $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); } else { $class = $this->_em->getClassMetadata(get_class($value)); - $idValuess = $class->getIdentifierValues($value); + $idValues = $class->getIdentifierValues($value); } $sqlPositions = $paramMappings[$key]; $sqlParams += array_combine((array)$sqlPositions, $idValues); From 394c67d482ccf1de41434f30741e058049bf9400 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 6 Oct 2010 22:18:48 +0200 Subject: [PATCH 042/119] Fix DDC-672 --- lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php index 1c140e1bf..1cc699610 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -89,11 +89,6 @@ class YamlExporter extends AbstractExporter $ids = array(); foreach ($fieldMappings as $name => $fieldMapping) { - if (isset($fieldMapping['length'])) { - $fieldMapping['type'] = $fieldMapping['type'] . '(' . $fieldMapping['length'] . ')'; - unset($fieldMapping['length']); - } - $fieldMapping['column'] = $fieldMapping['columnName']; unset( $fieldMapping['columnName'], From 7551bb376228759b9d40024332778a03825b06f6 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 6 Oct 2010 23:09:49 +0200 Subject: [PATCH 043/119] Fix notice due to wrong variable reference --- lib/Doctrine/ORM/UnitOfWork.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index e3cd01568..b6d0393d3 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -573,7 +573,7 @@ class UnitOfWork implements PropertyChangedListener $this->computeChangeSet($targetClass, $entry); } else if ($state == self::STATE_REMOVED) { return new InvalidArgumentException("Removed entity detected during flush: " - . self::objToStr($removedEntity).". Remove deleted entities from associations."); + . self::objToStr($entry).". Remove deleted entities from associations."); } else if ($state == self::STATE_DETACHED) { // Can actually not happen right now as we assume STATE_NEW, // so the exception will be raised from the DBAL layer (constraint violation). From b4aabf0ba6ca9406ab6ffc13e3cb151c741024ca Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 10 Oct 2010 17:13:23 +0200 Subject: [PATCH 044/119] DDC-831 - Fix docblock in ClassMetadataInfo --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 32ee9401d..077eef1bc 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -598,9 +598,10 @@ class ClassMetadataInfo /** * Gets the mapping of an association. * + * @see ClassMetadataInfo::$associationMappings * @param string $fieldName The field name that represents the association in * the object model. - * @return Doctrine\ORM\Mapping\AssociationMapping The mapping. + * @return array The mapping. */ public function getAssociationMapping($fieldName) { From 07016f6da5a301e544627a821ec1523872fe943c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 11 Oct 2010 20:11:23 +0200 Subject: [PATCH 045/119] DDC-834 - Commit fix for requesting references of classes that have subclasses. This is not possible, so we do an eager find instead. Yes this means there is yet another negative performance impact when using Inheritance STI and CTI. --- lib/Doctrine/ORM/EntityManager.php | 12 ++++++--- .../Functional/ClassTableInheritanceTest.php | 25 +++++++++++++++++++ .../Functional/SingleTableInheritanceTest.php | 17 +++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 0cd887fe1..abd89b0d4 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -352,11 +352,15 @@ class EntityManager if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { return $entity; } - if ( ! is_array($identifier)) { - $identifier = array($class->identifier[0] => $identifier); + if ($class->subClasses) { + $entity = $this->find($entityName, $identifier); + } else { + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); + } + $entity = $this->proxyFactory->getProxy($class->name, $identifier); + $this->unitOfWork->registerManaged($entity, $identifier, array()); } - $entity = $this->proxyFactory->getProxy($class->name, $identifier); - $this->unitOfWork->registerManaged($entity, $identifier, array()); return $entity; } diff --git a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php index 24a4e4ad7..072652577 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -385,4 +385,29 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($manager->getId(), $pmanager->getId()); } + + /** + * @group DDC-834 + */ + public function testGetReferenceEntityWithSubclasses() + { + $manager = new CompanyManager(); + $manager->setName('gblanco'); + $manager->setSalary(1234); + $manager->setTitle('Awesome!'); + $manager->setDepartment('IT'); + + $this->_em->persist($manager); + $this->_em->flush(); + $this->_em->clear(); + + $ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyPerson', $manager->getId()); + $this->assertNotType('Doctrine\ORM\Proxy\Proxy', $ref, "Cannot Request a proxy from a class that has subclasses."); + $this->assertType('Doctrine\Tests\Models\Company\CompanyPerson', $ref); + $this->assertType('Doctrine\Tests\Models\Company\CompanyEmployee', $ref, "Direct fetch of the reference has to load the child class Emplyoee directly."); + $this->_em->clear(); + + $ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyManager', $manager->getId()); + $this->assertType('Doctrine\ORM\Proxy\Proxy', $ref, "A proxy can be generated only if no subclasses exists for the requested reference."); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php index fc303039f..b399092a4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -332,4 +332,21 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $contracts = $repos->findBy(array('salesPerson' => $this->salesPerson->getId())); $this->assertEquals(1, count($contracts), "There should be 1 entities related to " . $this->salesPerson->getId() . " for 'Doctrine\Tests\Models\Company\CompanyFlexUltraContract'"); } + + /** + * @group DDC-834 + */ + public function testGetReferenceEntityWithSubclasses() + { + $this->loadFullFixture(); + + $ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyContract', $this->fix->getId()); + $this->assertNotType('Doctrine\ORM\Proxy\Proxy', $ref, "Cannot Request a proxy from a class that has subclasses."); + $this->assertType('Doctrine\Tests\Models\Company\CompanyContract', $ref); + $this->assertType('Doctrine\Tests\Models\Company\CompanyFixContract', $ref, "Direct fetch of the reference has to load the child class Emplyoee directly."); + $this->_em->clear(); + + $ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyFixContract', $this->fix->getId()); + $this->assertType('Doctrine\ORM\Proxy\Proxy', $ref, "A proxy can be generated only if no subclasses exists for the requested reference."); + } } From 89d0a52c4f6848ea6feafd006c6d4258904dbf07 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 11 Oct 2010 22:15:18 +0200 Subject: [PATCH 046/119] DDC-833 - Fix some nasty bug occouring when re-creating an entity that was a proxy before. Also found another nasty issue with refreshing entity that had an already loaded many-to-many or one-to-many association. --- .../ORM/Persisters/BasicEntityPersister.php | 3 + lib/Doctrine/ORM/UnitOfWork.php | 10 +- .../ORM/Functional/BasicFunctionalTest.php | 107 +++++++++++++++++- 3 files changed, 113 insertions(+), 7 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 51d5c5121..f7dacc53c 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -674,6 +674,9 @@ class BasicEntityPersister } } else if ($value instanceof PersistentCollection && $value->isInitialized()) { $value->setInitialized(false); + // no matter if dirty or non-dirty entities are already loaded, smoke them out! + // the beauty of it being, they are still in the identity map + $value->unwrap()->clear(); $newData[$field] = $value; } } diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index b6d0393d3..5b5782df8 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1929,14 +1929,12 @@ class UnitOfWork implements PropertyChangedListener } } else { // Inject collection - $reflField = $class->reflFields[$field]; - $pColl = new PersistentCollection( - $this->em, $targetClass, - //TODO: getValue might be superfluous once DDC-79 is implemented. - $reflField->getValue($entity) ?: new ArrayCollection - ); + $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection); $pColl->setOwner($entity, $assoc); + + $reflField = $class->reflFields[$field]; $reflField->setValue($entity, $pColl); + if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) { $pColl->setInitialized(false); } else { diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 97414ab4e..7a7da0c7a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\ORM\Tools\SchemaTool; +use Doctrine\ORM\Query; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsAddress; @@ -263,7 +264,111 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->refresh($user); $this->assertEquals('developer', $user->status); } - + + /** + * @group DDC-833 + */ + public function testRefreshResetsCollection() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + // Add a phonenumber + $ph1 = new CmsPhonenumber; + $ph1->phonenumber = "12345"; + $user->addPhonenumber($ph1); + + // Add a phonenumber + $ph2 = new CmsPhonenumber; + $ph2->phonenumber = "54321"; + + $this->_em->persist($user); + $this->_em->persist($ph1); + $this->_em->persist($ph2); + $this->_em->flush(); + + $user->addPhonenumber($ph2); + + $this->assertEquals(2, count($user->phonenumbers)); + $this->_em->refresh($user); + + $this->assertEquals(1, count($user->phonenumbers)); + } + + /** + * @group DDC-833 + */ + public function testDqlRefreshResetsCollection() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + // Add a phonenumber + $ph1 = new CmsPhonenumber; + $ph1->phonenumber = "12345"; + $user->addPhonenumber($ph1); + + // Add a phonenumber + $ph2 = new CmsPhonenumber; + $ph2->phonenumber = "54321"; + + $this->_em->persist($user); + $this->_em->persist($ph1); + $this->_em->persist($ph2); + $this->_em->flush(); + + $user->addPhonenumber($ph2); + + $this->assertEquals(2, count($user->phonenumbers)); + $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1"; + $user = $this->_em->createQuery($dql) + ->setParameter(1, $user->id) + ->setHint(Query::HINT_REFRESH, true) + ->getSingleResult(); + + $this->assertEquals(1, count($user->phonenumbers)); + } + + /** + * @group DDC-833 + */ + public function testCreateEntityOfProxy() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + // Add a phonenumber + $ph1 = new CmsPhonenumber; + $ph1->phonenumber = "12345"; + $user->addPhonenumber($ph1); + + // Add a phonenumber + $ph2 = new CmsPhonenumber; + $ph2->phonenumber = "54321"; + + $this->_em->persist($user); + $this->_em->persist($ph1); + $this->_em->persist($ph2); + $this->_em->flush(); + $this->_em->clear(); + + $userId = $user->id; + $user = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $user->id); + + $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1"; + $user = $this->_em->createQuery($dql) + ->setParameter(1, $userId) + ->getSingleResult(); + + $this->assertEquals(1, count($user->phonenumbers)); + } + public function testAddToCollectionDoesNotInitialize() { $user = new CmsUser; From 35860d9a94c57e750bfcfe993169fa8187a0eea4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 29 Oct 2010 13:14:35 +0200 Subject: [PATCH 047/119] Fix for DDC-839: Fetch joined collections are not initialized correctly. --- .../ORM/Internal/Hydration/ObjectHydrator.php | 1 + .../ManyToManyBasicAssociationTest.php | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index ca937059b..cb33a56a6 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -335,6 +335,7 @@ class ObjectHydrator extends AbstractHydrator } } else if ( ! $reflField->getValue($parentObject)) { $coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection); + $coll->setOwner($parentObject, $relation); $reflField->setValue($parentObject, $coll); $this->_uow->setOriginalEntityProperty($oid, $relationField, $coll); } diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index 1fee14767..eef376baf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -247,6 +247,34 @@ class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCa $this->assertEquals(0, count($newUser->getGroups())); } + /** + * @group DDC-839 + */ + public function testWorkWithDqlHydratedEmptyCollection() + { + $user = $this->addCmsUserGblancoWithGroups(0); + $group = new CmsGroup(); + $group->name = "Developers0"; + $this->_em->persist($group); + + $this->_em->flush(); + $this->_em->clear(); + + $newUser = $this->_em->createQuery('SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.groups g WHERE u.id = ?1') + ->setParameter(1, $user->getId()) + ->getSingleResult(); + $this->assertEquals(0, count($newUser->groups)); + $this->assertType('array', $newUser->groups->getMapping()); + + $newUser->addGroup($group); + + $this->_em->flush(); + $this->_em->clear(); + + $newUser = $this->_em->find(get_class($user), $user->getId()); + $this->assertEquals(1, count($newUser->groups)); + } + /** * @param int $groupCount * @return CmsUser From 0a8ff7a0302d224f9abf9f4c19e94de9693f7329 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 29 Oct 2010 16:46:21 +0200 Subject: [PATCH 048/119] Fix Testsuite to run with PHPUnit 3.5 --- .../Doctrine/Tests/OrmFunctionalTestCase.php | 22 ++++++++----------- .../Doctrine/Tests/OrmFunctionalTestSuite.php | 12 +--------- tests/Doctrine/Tests/TestInit.php | 2 -- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index c39798aa6..a115abc46 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -103,7 +103,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase */ protected function tearDown() { - $conn = $this->sharedFixture['conn']; + $conn = static::$_sharedConn; $this->_sqlLoggerStack->enabled = false; @@ -185,23 +185,19 @@ abstract class OrmFunctionalTestCase extends OrmTestCase { $forceCreateTables = false; - if ( ! isset($this->sharedFixture['conn'])) { - if ( ! isset(static::$_sharedConn)) { - static::$_sharedConn = TestUtil::getConnection(); - } + if ( ! isset(static::$_sharedConn)) { + static::$_sharedConn = TestUtil::getConnection(); - $this->sharedFixture['conn'] = static::$_sharedConn; - - if ($this->sharedFixture['conn']->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { + if (static::$_sharedConn->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { $forceCreateTables = true; } } if (isset($GLOBALS['DOCTRINE_MARK_SQL_LOGS'])) { - if (in_array($this->sharedFixture['conn']->getDatabasePlatform()->getName(), array("mysql", "postgresql"))) { - $this->sharedFixture['conn']->executeQuery('SELECT 1 /*' . get_class($this) . '*/'); - } else if ($this->sharedFixture['conn']->getDatabasePlatform()->getName() == "oracle") { - $this->sharedFixture['conn']->executeQuery('SELECT 1 /*' . get_class($this) . '*/ FROM dual'); + if (in_array(static::$_sharedConn->getDatabasePlatform()->getName(), array("mysql", "postgresql"))) { + static::$_sharedConn->executeQuery('SELECT 1 /*' . get_class($this) . '*/'); + } else if (static::$_sharedConn->getDatabasePlatform()->getName() == "oracle") { + static::$_sharedConn->executeQuery('SELECT 1 /*' . get_class($this) . '*/ FROM dual'); } } @@ -261,7 +257,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver()); - $conn = $this->sharedFixture['conn']; + $conn = static::$_sharedConn; $conn->getConfiguration()->setSQLLogger($this->_sqlLoggerStack); return \Doctrine\ORM\EntityManager::create($conn, $config); diff --git a/tests/Doctrine/Tests/OrmFunctionalTestSuite.php b/tests/Doctrine/Tests/OrmFunctionalTestSuite.php index d281ed785..8d4b8b058 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestSuite.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestSuite.php @@ -11,15 +11,5 @@ namespace Doctrine\Tests; */ class OrmFunctionalTestSuite extends OrmTestSuite { - protected function setUp() - { - if ( ! isset($this->sharedFixture['conn'])) { - $this->sharedFixture['conn'] = TestUtil::getConnection(); - } - } - - protected function tearDown() - { - $this->sharedFixture = null; - } + } \ No newline at end of file diff --git a/tests/Doctrine/Tests/TestInit.php b/tests/Doctrine/Tests/TestInit.php index cd3bf4daa..0445cf76b 100644 --- a/tests/Doctrine/Tests/TestInit.php +++ b/tests/Doctrine/Tests/TestInit.php @@ -6,8 +6,6 @@ namespace Doctrine\Tests; error_reporting(E_ALL | E_STRICT); -require_once 'PHPUnit/Framework.php'; -require_once 'PHPUnit/TextUI/TestRunner.php'; require_once __DIR__ . '/../../../lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php'; if (isset($GLOBALS['DOCTRINE_COMMON_PATH'])) { From bf79168952165baeebf883606282529bdbf64e28 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 30 Oct 2010 08:43:15 +0200 Subject: [PATCH 049/119] Added Testcase to verify failure --- .../ORM/Functional/Ticket/DDC832Test.php | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php new file mode 100644 index 000000000..cb35b9b14 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -0,0 +1,142 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832Like'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832JoinedIndex'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832JoinedTreeIndex'), + )); + } catch(\Exception $e) { + + } + } + + /** + * @group DDC-832 + */ + public function testQuotedTableBasicUpdate() + { + $like = new DDC832Like("test"); + $this->_em->persist($like); + $this->_em->flush(); + + $like->word = "test2"; + $this->_em->flush(); + } + + /** + * @group DDC-832 + */ + public function testQuotedTableBasicRemove() + { + $like = new DDC832Like("test"); + $this->_em->persist($like); + $this->_em->flush(); + + $this->_em->remove($like); + $this->_em->flush(); + } + + /** + * @group DDC-832 + */ + public function testQuotedTableJoinedUpdate() + { + $this->markTestIncomplete('Not written yet.'); + } + + /** + * @group DDC-832 + */ + public function testQuotedTableJoinedRemove() + { + $this->markTestIncomplete('Not written yet.'); + } +} + +/** + * @Entity + * @Table(name="`LIKE`") + */ +class DDC832Like +{ + /** + * @Id @Column(type="string") @GeneratedValue + */ + public $id; + + /** @Column(type="string") */ + public $word; + + /** + * @version + * @Column(type="integer") + */ + public $version; + + public function __construct($word) + { + $this->word = $word; + } +} + +/** + * @Entity + * @Table(name="`INDEX`") + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="discr", type="string") + * @DiscriminatorMap({"like" = "DDC832JoinedIndex", "fuzzy" = "DDC832JoinedTreeIndex"}) + */ +class DDC832JoinedIndex +{ + /** + * @Id @Column(type="string") @GeneratedValue + */ + public $id; + + /** @Column(type="string") */ + public $name; + + /** + * @version + * @Column(type="integer") + */ + public $version; + + public function __construct($name) + { + $this->name = $name; + } +} + +/** + * @Entity + * @Table(name="`TREE_INDEX`") + */ +class DDC832JoinedTreeIndex extends DDC832JoinedIndex +{ + /** @Column(type="integer") */ + public $lft; + /** @Column(type="integer") */ + public $rgt; + + public function __construct($name, $lft, $rgt) + { + $this->name = $name; + $this->lft = $lft; + $this->rgt = $rgt; + } +} \ No newline at end of file From 338476805debda72a255a836bec7c80b99841cf9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 30 Oct 2010 09:16:55 +0200 Subject: [PATCH 050/119] DDC-830 - Fix extended class in EntityGenerator --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 230ab98b9..f2fa583c8 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -1,7 +1,5 @@ () { $refl = new \ReflectionClass($this->_getClassToExtend()); - return $refl->getName(); + return '\\' . $refl->getName(); } private function _getClassName(ClassMetadataInfo $metadata) From aa2a80f3ff412e434600ed04b467feabd75265a5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 30 Oct 2010 12:35:22 +0200 Subject: [PATCH 051/119] DDC-787 - Fix table name casing in DatabaseDriver --- .../ORM/Mapping/Driver/DatabaseDriver.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index 5dbe6e431..dd53b0d3a 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -48,7 +48,7 @@ class DatabaseDriver implements Driver */ private $tables = null; - private $classes = array(); + private $classToTableNames = array(); /** * @var array @@ -73,11 +73,11 @@ class DatabaseDriver implements Driver } foreach ($this->_sm->listTableNames() as $tableName) { - $tables[strtolower($tableName)] = $this->_sm->listTableDetails($tableName); + $tables[$tableName] = $this->_sm->listTableDetails($tableName); } $this->tables = array(); - foreach ($tables AS $name => $table) { + foreach ($tables AS $tableName => $table) { /* @var $table Table */ if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { $foreignKeys = $table->getForeignKeys(); @@ -96,14 +96,16 @@ class DatabaseDriver implements Driver if ($pkColumns == $allForeignKeyColumns) { if (count($table->getForeignKeys()) > 2) { - throw new \InvalidArgumentException("ManyToMany table '" . $name . "' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver."); + throw new \InvalidArgumentException("ManyToMany table '" . $tableName . "' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver."); } - $this->manyToManyTables[$name] = $table; + $this->manyToManyTables[$tableName] = $table; } else { - $className = Inflector::classify($name); - $this->tables[$name] = $table; - $this->classes[$className] = $name; + // lower-casing is necessary because of Oracle Uppercase Tablenames, + // assumption is lower-case + underscore separated. + $className = Inflector::classify(strtolower($tableName)); + $this->tables[$tableName] = $table; + $this->classToTableNames[$className] = $tableName; } } } @@ -115,11 +117,11 @@ class DatabaseDriver implements Driver { $this->reverseEngineerMappingFromDatabase(); - if (!isset($this->classes[$className])) { + if (!isset($this->classToTableNames[$className])) { throw new \InvalidArgumentException("Unknown class " . $className); } - $tableName = Inflector::tableize($className); + $tableName = $this->classToTableNames[$className]; $metadata->name = $className; $metadata->table['name'] = $tableName; @@ -183,7 +185,7 @@ class DatabaseDriver implements Driver foreach ($this->manyToManyTables AS $manyTable) { foreach ($manyTable->getForeignKeys() AS $foreignKey) { - if ($tableName == strtolower($foreignKey->getForeignTableName())) { + if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) { $myFk = $foreignKey; foreach ($manyTable->getForeignKeys() AS $foreignKey) { if ($foreignKey != $myFk) { @@ -269,6 +271,6 @@ class DatabaseDriver implements Driver { $this->reverseEngineerMappingFromDatabase(); - return array_keys($this->classes); + return array_keys($this->classToTableNames); } } \ No newline at end of file From b5c5ec3c699e1d80dd4151ad94ea429c4ef2d274 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 30 Oct 2010 13:24:50 +0200 Subject: [PATCH 052/119] DBAL-16 - Update Symfony\Component\Console to latest version to fix upstream bug --- .../Symfony/Component/Console/Application.php | 1409 ++++++++--------- .../Component/Console/Command/Command.php | 926 ++++++----- .../Component/Console/Command/HelpCommand.php | 80 +- .../Component/Console/Command/ListCommand.php | 57 +- .../Component/Console/Helper/DialogHelper.php | 171 +- .../Console/Helper/FormatterHelper.php | 111 +- .../Component/Console/Helper/Helper.php | 45 +- .../Console/Helper/HelperInterface.php | 42 +- .../Component/Console/Helper/HelperSet.php | 146 +- .../Component/Console/Input/ArgvInput.php | 409 +++-- .../Component/Console/Input/ArrayInput.php | 244 ++- .../Symfony/Component/Console/Input/Input.php | 317 ++-- .../Component/Console/Input/InputArgument.php | 198 ++- .../Console/Input/InputDefinition.php | 892 +++++------ .../Console/Input/InputInterface.php | 62 +- .../Component/Console/Input/InputOption.php | 281 ++-- .../Component/Console/Input/StringInput.php | 95 +- .../Console/Output/ConsoleOutput.php | 26 +- .../Component/Console/Output/NullOutput.php | 24 +- .../Component/Console/Output/Output.php | 355 ++--- .../Console/Output/OutputInterface.php | 47 +- .../Component/Console/Output/StreamOutput.php | 136 +- .../Symfony/Component/Console/Shell.php | 178 +-- .../Console/Tester/ApplicationTester.php | 153 +- .../Console/Tester/CommandTester.php | 153 +- 25 files changed, 3170 insertions(+), 3387 deletions(-) diff --git a/lib/vendor/Symfony/Component/Console/Application.php b/lib/vendor/Symfony/Component/Console/Application.php index af488e347..850dbee20 100644 --- a/lib/vendor/Symfony/Component/Console/Application.php +++ b/lib/vendor/Symfony/Component/Console/Application.php @@ -19,7 +19,7 @@ use Symfony\Component\Console\Helper\FormatterHelper; use Symfony\Component\Console\Helper\DialogHelper; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -40,765 +40,704 @@ use Symfony\Component\Console\Helper\DialogHelper; * $app->addCommand(new SimpleCommand()); * $app->run(); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class Application { - protected $commands; - protected $aliases; - protected $application; - protected $wantHelps = false; - protected $runningCommand; - protected $name; - protected $version; - protected $catchExceptions; - protected $autoExit; - protected $definition; - protected $helperSet; + protected $commands; + protected $aliases; + protected $wantHelps = false; + protected $runningCommand; + protected $name; + protected $version; + protected $catchExceptions; + protected $autoExit; + protected $definition; + protected $helperSet; - /** - * Constructor. - * - * @param string $name The name of the application - * @param string $version The version of the application - */ - public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') - { - $this->name = $name; - $this->version = $version; - $this->catchExceptions = true; - $this->autoExit = true; - $this->commands = array(); - $this->aliases = array(); - $this->helperSet = new HelperSet(array( - new FormatterHelper(), - new DialogHelper(), - )); - - $this->addCommand(new HelpCommand()); - $this->addCommand(new ListCommand()); - - $this->definition = new InputDefinition(array( - new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), - - new InputOption('--help', '-h', InputOption::PARAMETER_NONE, 'Display this help message.'), - new InputOption('--quiet', '-q', InputOption::PARAMETER_NONE, 'Do not output any message.'), - new InputOption('--verbose', '-v', InputOption::PARAMETER_NONE, 'Increase verbosity of messages.'), - new InputOption('--version', '-V', InputOption::PARAMETER_NONE, 'Display this program version.'), - new InputOption('--color', '-c', InputOption::PARAMETER_NONE, 'Force ANSI color output.'), - new InputOption('--no-interaction', '-n', InputOption::PARAMETER_NONE, 'Do not ask any interactive question.'), - )); - } - - /** - * Runs the current application. - * - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * - * @return integer 0 if everything went fine, or an error code - */ - public function run(InputInterface $input = null, OutputInterface $output = null) - { - if (null === $input) + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') { - $input = new ArgvInput(); + $this->name = $name; + $this->version = $version; + $this->catchExceptions = true; + $this->autoExit = true; + $this->commands = array(); + $this->aliases = array(); + $this->helperSet = new HelperSet(array( + new FormatterHelper(), + new DialogHelper(), + )); + + $this->addCommand(new HelpCommand()); + $this->addCommand(new ListCommand()); + + $this->definition = new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::PARAMETER_NONE, 'Display this help message.'), + new InputOption('--quiet', '-q', InputOption::PARAMETER_NONE, 'Do not output any message.'), + new InputOption('--verbose', '-v', InputOption::PARAMETER_NONE, 'Increase verbosity of messages.'), + new InputOption('--version', '-V', InputOption::PARAMETER_NONE, 'Display this program version.'), + new InputOption('--ansi', '-a', InputOption::PARAMETER_NONE, 'Force ANSI output.'), + new InputOption('--no-interaction', '-n', InputOption::PARAMETER_NONE, 'Do not ask any interactive question.'), + )); } - if (null === $output) + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + */ + public function run(InputInterface $input = null, OutputInterface $output = null) { - $output = new ConsoleOutput(); - } - - try - { - $statusCode = $this->doRun($input, $output); - } - catch (\Exception $e) - { - if (!$this->catchExceptions) - { - throw $e; - } - - $this->renderException($e, $output); - $statusCode = $e->getCode(); - - $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; - } - - if ($this->autoExit) - { - // @codeCoverageIgnoreStart - exit($statusCode); - // @codeCoverageIgnoreEnd - } - else - { - return $statusCode; - } - } - - /** - * Runs the current application. - * - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * - * @return integer 0 if everything went fine, or an error code - */ - public function doRun(InputInterface $input, OutputInterface $output) - { - $name = $input->getFirstArgument('command'); - - if (true === $input->hasParameterOption(array('--color', '-c'))) - { - $output->setDecorated(true); - } - - if (true === $input->hasParameterOption(array('--help', '-H'))) - { - if (!$name) - { - $name = 'help'; - $input = new ArrayInput(array('command' => 'help')); - } - else - { - $this->wantHelps = true; - } - } - - if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) - { - $input->setInteractive(false); - } - - if (true === $input->hasParameterOption(array('--quiet', '-q'))) - { - $output->setVerbosity(Output::VERBOSITY_QUIET); - } - elseif (true === $input->hasParameterOption(array('--verbose', '-v'))) - { - $output->setVerbosity(Output::VERBOSITY_VERBOSE); - } - - if (true === $input->hasParameterOption(array('--version', '-V'))) - { - $output->writeln($this->getLongVersion()); - - return 0; - } - - if (!$name) - { - $name = 'list'; - $input = new ArrayInput(array('command' => 'list')); - } - - // the command name MUST be the first element of the input - $command = $this->findCommand($name); - - $this->runningCommand = $command; - $statusCode = $command->run($input, $output); - $this->runningCommand = null; - - return is_numeric($statusCode) ? $statusCode : 0; - } - - /** - * Set a helper set to be used with the command. - * - * @param HelperSet $helperSet The helper set - */ - public function setHelperSet(HelperSet $helperSet) - { - $this->helperSet = $helperSet; - } - - /** - * Get the helper set associated with the command - * - * @return HelperSet The HelperSet isntance associated with this command - */ - public function getHelperSet() - { - return $this->helperSet; - } - - /** - * Gets the InputDefinition related to this Application. - * - * @return InputDefinition The InputDefinition instance - */ - public function getDefinition() - { - return $this->definition; - } - - /** - * Gets the help message. - * - * @return string A help message. - */ - public function getHelp() - { - $messages = array( - $this->getLongVersion(), - '', - 'Usage:', - sprintf(" [options] command [arguments]\n"), - 'Options:', - ); - - foreach ($this->definition->getOptions() as $option) - { - $messages[] = sprintf(' %-29s %s %s', - '--'.$option->getName().'', - $option->getShortcut() ? '-'.$option->getShortcut().'' : ' ', - $option->getDescription() - ); - } - - return implode("\n", $messages); - } - - /** - * Sets whether to catch exceptions or not during commands execution. - * - * @param Boolean $boolean Whether to catch exceptions or not during commands execution - */ - public function setCatchExceptions($boolean) - { - $this->catchExceptions = (Boolean) $boolean; - } - - /** - * Sets whether to automatically exit after a command execution or not. - * - * @param Boolean $boolean Whether to automatically exit after a command execution or not - */ - public function setAutoExit($boolean) - { - $this->autoExit = (Boolean) $boolean; - } - - /** - * Gets the name of the application. - * - * @return string The application name - */ - public function getName() - { - return $this->name; - } - - /** - * Sets the application name. - * - * @param string $name The application name - */ - public function setName($name) - { - $this->name = $name; - } - - /** - * Gets the application version. - * - * @return string The application version - */ - public function getVersion() - { - return $this->version; - } - - /** - * Sets the application version. - * - * @param string $version The application version - */ - public function setVersion($version) - { - $this->version = $version; - } - - /** - * Returns the long version of the application. - * - * @return string The long application version - */ - public function getLongVersion() - { - if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) - { - return sprintf('%s version %s', $this->getName(), $this->getVersion()); - } - else - { - return 'Console Tool'; - } - } - - /** - * Registers a new command. - * - * @param string $name The command name - * - * @return Command The newly created command - */ - public function register($name) - { - return $this->addCommand(new Command($name)); - } - - /** - * Adds an array of command objects. - * - * @param array $commands An array of commands - */ - public function addCommands(array $commands) - { - foreach ($commands as $command) - { - $this->addCommand($command); - } - } - - /** - * Adds a command object. - * - * If a command with the same name already exists, it will be overridden. - * - * @param Command $command A Command object - * - * @return Command The registered command - */ - public function addCommand(Command $command) - { - $command->setApplication($this); - - $this->commands[$command->getFullName()] = $command; - - foreach ($command->getAliases() as $alias) - { - $this->aliases[$alias] = $command; - } - - return $command; - } - - /** - * Returns a registered command by name or alias. - * - * @param string $name The command name or alias - * - * @return Command A Command object - */ - public function getCommand($name) - { - if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) - { - throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); - } - - $command = isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; - - if ($this->wantHelps) - { - $this->wantHelps = false; - - $helpCommand = $this->getCommand('help'); - $helpCommand->setCommand($command); - - return $helpCommand; - } - - return $command; - } - - /** - * Returns true if the command exists, false otherwise - * - * @param string $name The command name or alias - * - * @return Boolean true if the command exists, false otherwise - */ - public function hasCommand($name) - { - return isset($this->commands[$name]) || isset($this->aliases[$name]); - } - - /** - * Returns an array of all unique namespaces used by currently registered commands. - * - * It does not returns the global namespace which always exists. - * - * @return array An array of namespaces - */ - public function getNamespaces() - { - $namespaces = array(); - foreach ($this->commands as $command) - { - if ($command->getNamespace()) - { - $namespaces[$command->getNamespace()] = true; - } - } - - return array_keys($namespaces); - } - - /** - * Finds a registered namespace by a name or an abbreviation. - * - * @return string A registered namespace - */ - public function findNamespace($namespace) - { - $abbrevs = static::getAbbreviations($this->getNamespaces()); - - if (!isset($abbrevs[$namespace])) - { - throw new \InvalidArgumentException(sprintf('There are no commands defined in the "%s" namespace.', $namespace)); - } - - if (count($abbrevs[$namespace]) > 1) - { - throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$namespace]))); - } - - return $abbrevs[$namespace][0]; - } - - /** - * Finds a command by name or alias. - * - * Contrary to getCommand, this command tries to find the best - * match if you give it an abbreviation of a name or alias. - * - * @param string $name A command name or a command alias - * - * @return Command A Command instance - */ - public function findCommand($name) - { - // namespace - $namespace = ''; - if (false !== $pos = strrpos($name, ':')) - { - $namespace = $this->findNamespace(substr($name, 0, $pos)); - $name = substr($name, $pos + 1); - } - - $fullName = $namespace ? $namespace.':'.$name : $name; - - // name - $commands = array(); - foreach ($this->commands as $command) - { - if ($command->getNamespace() == $namespace) - { - $commands[] = $command->getName(); - } - } - - $abbrevs = static::getAbbreviations($commands); - if (isset($abbrevs[$name]) && 1 == count($abbrevs[$name])) - { - return $this->getCommand($namespace ? $namespace.':'.$abbrevs[$name][0] : $abbrevs[$name][0]); - } - - if (isset($abbrevs[$name]) && count($abbrevs[$name]) > 1) - { - $suggestions = $this->getAbbreviationSuggestions(array_map(function ($command) use ($namespace) { return $namespace.':'.$command; }, $abbrevs[$name])); - - throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $fullName, $suggestions)); - } - - // aliases - $abbrevs = static::getAbbreviations(array_keys($this->aliases)); - if (!isset($abbrevs[$fullName])) - { - throw new \InvalidArgumentException(sprintf('Command "%s" is not defined.', $fullName)); - } - - if (count($abbrevs[$fullName]) > 1) - { - throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $fullName, $this->getAbbreviationSuggestions($abbrevs[$fullName]))); - } - - return $this->getCommand($abbrevs[$fullName][0]); - } - - /** - * Gets the commands (registered in the given namespace if provided). - * - * The array keys are the full names and the values the command instances. - * - * @param string $namespace A namespace name - * - * @return array An array of Command instances - */ - public function getCommands($namespace = null) - { - if (null === $namespace) - { - return $this->commands; - } - - $commands = array(); - foreach ($this->commands as $name => $command) - { - if ($namespace === $command->getNamespace()) - { - $commands[$name] = $command; - } - } - - return $commands; - } - - /** - * Returns an array of possible abbreviations given a set of names. - * - * @param array An array of names - * - * @return array An array of abbreviations - */ - static public function getAbbreviations($names) - { - $abbrevs = array(); - foreach ($names as $name) - { - for ($len = strlen($name) - 1; $len > 0; --$len) - { - $abbrev = substr($name, 0, $len); - if (!isset($abbrevs[$abbrev])) - { - $abbrevs[$abbrev] = array($name); - } - else - { - $abbrevs[$abbrev][] = $name; - } - } - } - - // Non-abbreviations always get entered, even if they aren't unique - foreach ($names as $name) - { - $abbrevs[$name] = array($name); - } - - return $abbrevs; - } - - /** - * Returns a text representation of the Application. - * - * @param string $namespace An optional namespace name - * - * @return string A string representing the Application - */ - public function asText($namespace = null) - { - $commands = $namespace ? $this->getCommands($this->findNamespace($namespace)) : $this->commands; - - $messages = array($this->getHelp(), ''); - if ($namespace) - { - $messages[] = sprintf("Available commands for the \"%s\" namespace:", $namespace); - } - else - { - $messages[] = 'Available commands:'; - } - - $width = 0; - foreach ($commands as $command) - { - $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; - } - $width += 2; - - // add commands by namespace - foreach ($this->sortCommands($commands) as $space => $commands) - { - if (!$namespace && '_global' !== $space) - { - $messages[] = ''.$space.''; - } - - foreach ($commands as $command) - { - $aliases = $command->getAliases() ? ' ('.implode(', ', $command->getAliases()).')' : ''; - - $messages[] = sprintf(" %-${width}s %s%s", ($command->getNamespace() ? ':' : '').$command->getName(), $command->getDescription(), $aliases); - } - } - - return implode("\n", $messages); - } - - /** - * Returns an XML representation of the Application. - * - * @param string $namespace An optional namespace name - * @param Boolean $asDom Whether to return a DOM or an XML string - * - * @return string|DOMDocument An XML string representing the Application - */ - public function asXml($namespace = null, $asDom = false) - { - $commands = $namespace ? $this->getCommands($this->findNamespace($namespace)) : $this->commands; - - $dom = new \DOMDocument('1.0', 'UTF-8'); - $dom->formatOutput = true; - $dom->appendChild($xml = $dom->createElement('symfony')); - - $xml->appendChild($commandsXML = $dom->createElement('commands')); - - if ($namespace) - { - $commandsXML->setAttribute('namespace', $namespace); - } - else - { - $xml->appendChild($namespacesXML = $dom->createElement('namespaces')); - } - - // add commands by namespace - foreach ($this->sortCommands($commands) as $space => $commands) - { - if (!$namespace) - { - $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); - $namespaceArrayXML->setAttribute('id', $space); - } - - foreach ($commands as $command) - { - if (!$namespace) - { - $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); - $commandXML->appendChild($dom->createTextNode($command->getName())); + if (null === $input) { + $input = new ArgvInput(); } - $commandXML = new \DOMDocument('1.0', 'UTF-8'); - $commandXML->formatOutput = true; - $commandXML->loadXML($command->asXml()); - $node = $commandXML->getElementsByTagName('command')->item(0); - $node = $dom->importNode($node, true); + if (null === $output) { + $output = new ConsoleOutput(); + } - $commandsXML->appendChild($node); - } + try { + $statusCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + $this->renderException($e, $output); + $statusCode = $e->getCode(); + + $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; + } + + if ($this->autoExit) { + if ($statusCode > 255) { + $statusCode = 255; + } + // @codeCoverageIgnoreStart + exit($statusCode); + // @codeCoverageIgnoreEnd + } else { + return $statusCode; + } } - return $asDom ? $dom : $dom->saveXml(); - } - - /** - * Renders a catched exception. - * - * @param Exception $e An exception instance - * @param OutputInterface $output An OutputInterface instance - */ - public function renderException($e, $output) - { - $strlen = function ($string) + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) { - return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); - }; + $name = $this->getCommandName($input); - $title = sprintf(' [%s] ', get_class($e)); - $len = $strlen($title); - $lines = array(); - foreach (explode("\n", $e->getMessage()) as $line) - { - $lines[] = sprintf(' %s ', $line); - $len = max($strlen($line) + 4, $len); + if (true === $input->hasParameterOption(array('--ansi', '-a'))) { + $output->setDecorated(true); + } + + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(Output::VERBOSITY_QUIET); + } elseif (true === $input->hasParameterOption(array('--verbose', '-v'))) { + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + } + + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + if (!$name) { + $name = 'list'; + $input = new ArrayInput(array('command' => 'list')); + } + + // the command name MUST be the first element of the input + $command = $this->findCommand($name); + + $this->runningCommand = $command; + $statusCode = $command->run($input, $output); + $this->runningCommand = null; + + return is_numeric($statusCode) ? $statusCode : 0; } - $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', $len - $strlen($title))); - - foreach ($lines as $line) + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + */ + public function setHelperSet(HelperSet $helperSet) { - $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + $this->helperSet = $helperSet; } - $messages[] = str_repeat(' ', $len); - - $output->writeln("\n"); - foreach ($messages as $message) + /** + * Get the helper set associated with the command + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() { - $output->writeln("$message"); - } - $output->writeln("\n"); - - if (null !== $this->runningCommand) - { - $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); - $output->writeln("\n"); + return $this->helperSet; } - if (Output::VERBOSITY_VERBOSE === $output->getVerbosity()) + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() { - $output->writeln('Exception trace:'); - - // exception related properties - $trace = $e->getTrace(); - array_unshift($trace, array( - 'function' => '', - 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', - 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', - 'args' => array(), - )); - - for ($i = 0, $count = count($trace); $i < $count; $i++) - { - $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; - $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; - $function = $trace[$i]['function']; - $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; - $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; - - $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); - } - - $output->writeln("\n"); - } - } - - private function sortCommands($commands) - { - $namespacedCommands = array(); - foreach ($commands as $name => $command) - { - $key = $command->getNamespace() ? $command->getNamespace() : '_global'; - - if (!isset($namespacedCommands[$key])) - { - $namespacedCommands[$key] = array(); - } - - $namespacedCommands[$key][$name] = $command; - } - ksort($namespacedCommands); - - foreach ($namespacedCommands as $name => &$commands) - { - ksort($commands); + return $this->definition; } - return $namespacedCommands; - } + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + $messages = array( + $this->getLongVersion(), + '', + 'Usage:', + sprintf(" [options] command [arguments]\n"), + 'Options:', + ); - private function getAbbreviationSuggestions($abbrevs) - { - return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); - } + foreach ($this->definition->getOptions() as $option) { + $messages[] = sprintf(' %-29s %s %s', + '--'.$option->getName().'', + $option->getShortcut() ? '-'.$option->getShortcut().'' : ' ', + $option->getDescription() + ); + } + + return implode("\n", $messages); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param Boolean $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (Boolean) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param Boolean $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (Boolean) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } else { + return 'Console Tool'; + } + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->addCommand(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->addCommand($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + */ + public function addCommand(Command $command) + { + $command->setApplication($this); + + $this->commands[$command->getFullName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->aliases[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws \InvalidArgumentException When command name given does not exist + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->getCommand('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise + * + * @param string $name The command name or alias + * + * @return Boolean true if the command exists, false otherwise + */ + public function hasCommand($name) + { + return isset($this->commands[$name]) || isset($this->aliases[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->commands as $command) { + if ($command->getNamespace()) { + $namespaces[$command->getNamespace()] = true; + } + } + + return array_keys($namespaces); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @return string A registered namespace + * + * @throws \InvalidArgumentException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $abbrevs = static::getAbbreviations($this->getNamespaces()); + + if (!isset($abbrevs[$namespace])) { + throw new \InvalidArgumentException(sprintf('There are no commands defined in the "%s" namespace.', $namespace)); + } + + if (count($abbrevs[$namespace]) > 1) { + throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$namespace]))); + } + + return $abbrevs[$namespace][0]; + } + + /** + * Finds a command by name or alias. + * + * Contrary to getCommand, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws \InvalidArgumentException When command name is incorrect or ambiguous + */ + public function findCommand($name) + { + // namespace + $namespace = ''; + if (false !== $pos = strrpos($name, ':')) { + $namespace = $this->findNamespace(substr($name, 0, $pos)); + $name = substr($name, $pos + 1); + } + + $fullName = $namespace ? $namespace.':'.$name : $name; + + // name + $commands = array(); + foreach ($this->commands as $command) { + if ($command->getNamespace() == $namespace) { + $commands[] = $command->getName(); + } + } + + $abbrevs = static::getAbbreviations($commands); + if (isset($abbrevs[$name]) && 1 == count($abbrevs[$name])) { + return $this->getCommand($namespace ? $namespace.':'.$abbrevs[$name][0] : $abbrevs[$name][0]); + } + + if (isset($abbrevs[$name]) && count($abbrevs[$name]) > 1) { + $suggestions = $this->getAbbreviationSuggestions(array_map(function ($command) use ($namespace) { return $namespace.':'.$command; }, $abbrevs[$name])); + + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $fullName, $suggestions)); + } + + // aliases + $abbrevs = static::getAbbreviations(array_keys($this->aliases)); + if (!isset($abbrevs[$fullName])) { + throw new \InvalidArgumentException(sprintf('Command "%s" is not defined.', $fullName)); + } + + if (count($abbrevs[$fullName]) > 1) { + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $fullName, $this->getAbbreviationSuggestions($abbrevs[$fullName]))); + } + + return $this->getCommand($abbrevs[$fullName][0]); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return array An array of Command instances + */ + public function getCommands($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $command->getNamespace()) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + static public function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name) - 1; $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + if (!isset($abbrevs[$abbrev])) { + $abbrevs[$abbrev] = array($name); + } else { + $abbrevs[$abbrev][] = $name; + } + } + } + + // Non-abbreviations always get entered, even if they aren't unique + foreach ($names as $name) { + $abbrevs[$name] = array($name); + } + + return $abbrevs; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * + * @return string A string representing the Application + */ + public function asText($namespace = null) + { + $commands = $namespace ? $this->getCommands($this->findNamespace($namespace)) : $this->commands; + + $messages = array($this->getHelp(), ''); + if ($namespace) { + $messages[] = sprintf("Available commands for the \"%s\" namespace:", $namespace); + } else { + $messages[] = 'Available commands:'; + } + + $width = 0; + foreach ($commands as $command) { + $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; + } + $width += 2; + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace && '_global' !== $space) { + $messages[] = ''.$space.''; + } + + foreach ($commands as $command) { + $aliases = $command->getAliases() ? ' ('.implode(', ', $command->getAliases()).')' : ''; + + $messages[] = sprintf(" %-${width}s %s%s", ($command->getNamespace() ? ':' : '').$command->getName(), $command->getDescription(), $aliases); + } + } + + return implode("\n", $messages); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the Application + */ + public function asXml($namespace = null, $asDom = false) + { + $commands = $namespace ? $this->getCommands($this->findNamespace($namespace)) : $this->commands; + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($xml = $dom->createElement('symfony')); + + $xml->appendChild($commandsXML = $dom->createElement('commands')); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } else { + $xml->appendChild($namespacesXML = $dom->createElement('namespaces')); + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $space); + } + + foreach ($commands as $command) { + if (!$namespace) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($command->getName())); + } + + $node = $command->asXml(true)->getElementsByTagName('command')->item(0); + $node = $dom->importNode($node, true); + + $commandsXML->appendChild($node); + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + /** + * Renders a catched exception. + * + * @param Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $strlen = function ($string) + { + return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); + }; + + $title = sprintf(' [%s] ', get_class($e)); + $len = $strlen($title); + $lines = array(); + foreach (explode("\n", $e->getMessage()) as $line) { + $lines[] = sprintf(' %s ', $line); + $len = max($strlen($line) + 4, $len); + } + + $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', $len - $strlen($title))); + + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + } + + $messages[] = str_repeat(' ', $len); + + $output->writeln("\n"); + foreach ($messages as $message) { + $output->writeln(''.$message.''); + } + $output->writeln("\n"); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); + $output->writeln("\n"); + } + + if (Output::VERBOSITY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Exception trace:'); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; $i++) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); + } + + $output->writeln("\n"); + } + } + + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument('command'); + } + + protected function sortCommands($commands) + { + $namespacedCommands = array(); + foreach ($commands as $name => $command) { + $key = $command->getNamespace() ? $command->getNamespace() : '_global'; + + if (!isset($namespacedCommands[$key])) { + $namespacedCommands[$key] = array(); + } + + $namespacedCommands[$key][$name] = $command; + } + ksort($namespacedCommands); + + foreach ($namespacedCommands as $name => &$commands) { + ksort($commands); + } + + return $namespacedCommands; + } + + protected function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } } diff --git a/lib/vendor/Symfony/Component/Console/Command/Command.php b/lib/vendor/Symfony/Component/Console/Command/Command.php index 779b5c89d..5f04baa54 100644 --- a/lib/vendor/Symfony/Component/Console/Command/Command.php +++ b/lib/vendor/Symfony/Component/Console/Command/Command.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Application; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -21,510 +21,492 @@ use Symfony\Component\Console\Application; /** * Base class for all commands. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class Command { - protected $name; - protected $namespace; - protected $aliases; - protected $definition; - protected $help; - protected $application; - protected $description; - protected $ignoreValidationErrors; - protected $formatter; - protected $applicationDefinitionMerged; - protected $code; + protected $name; + protected $namespace; + protected $aliases; + protected $definition; + protected $help; + protected $application; + protected $description; + protected $ignoreValidationErrors; + protected $applicationDefinitionMerged; + protected $code; - /** - * Constructor. - * - * @param string $name The name of the command - */ - public function __construct($name = null) - { - $this->definition = new InputDefinition(); - $this->ignoreValidationErrors = false; - $this->applicationDefinitionMerged = false; - $this->aliases = array(); - - if (null !== $name) + /** + * Constructor. + * + * @param string $name The name of the command + * + * @throws \LogicException When the command name is empty + */ + public function __construct($name = null) { - $this->setName($name); + $this->definition = new InputDefinition(); + $this->ignoreValidationErrors = false; + $this->applicationDefinitionMerged = false; + $this->aliases = array(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new \LogicException('The command name cannot be empty.'); + } } - $this->configure(); - - if (!$this->name) + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + */ + public function setApplication(Application $application = null) { - throw new \LogicException('The command name cannot be empty.'); - } - } - - /** - * Sets the application instance for this command. - * - * @param Application $application An Application instance - */ - public function setApplication(Application $application = null) - { - $this->application = $application; - } - - /** - * Configures the current command. - */ - protected function configure() - { - } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * - * @return integer 0 if everything went fine, or an error code - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - throw new \LogicException('You must override the execute() method in the concrete command class.'); - } - - /** - * Interacts with the user. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - } - - /** - * Initializes the command just after the input has been validated. - * - * This is mainly useful when a lot of commands extends one main command - * where some things need to be initialized based on the input arguments and options. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - protected function initialize(InputInterface $input, OutputInterface $output) - { - } - - /** - * Runs the command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - public function run(InputInterface $input, OutputInterface $output) - { - // add the application arguments and options - $this->mergeApplicationDefinition(); - - // bind the input against the command specific arguments/options - try - { - $input->bind($this->definition); - } - catch (\Exception $e) - { - if (!$this->ignoreValidationErrors) - { - throw $e; - } + $this->application = $application; } - $this->initialize($input, $output); - - if ($input->isInteractive()) + /** + * Configures the current command. + */ + protected function configure() { - $this->interact($input, $output); } - $input->validate(); - - if ($this->code) + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \LogicException When this abstract class is not implemented + */ + protected function execute(InputInterface $input, OutputInterface $output) { - return call_user_func($this->code, $input, $output); - } - else - { - return $this->execute($input, $output); - } - } - - /** - * Sets the code to execute when running this command. - * - * @param \Closure $code A \Closure - * - * @return Command The current instance - */ - public function setCode(\Closure $code) - { - $this->code = $code; - - return $this; - } - - /** - * Merges the application definition with the command definition. - */ - protected function mergeApplicationDefinition() - { - if (null === $this->application || true === $this->applicationDefinitionMerged) - { - return; + throw new \LogicException('You must override the execute() method in the concrete command class.'); } - $this->definition->setArguments(array_merge( - $this->application->getDefinition()->getArguments(), - $this->definition->getArguments() - )); - - $this->definition->addOptions($this->application->getDefinition()->getOptions()); - - $this->applicationDefinitionMerged = true; - } - - /** - * Sets an array of argument and option instances. - * - * @param array|Definition $definition An array of argument and option instances or a definition instance - * - * @return Command The current instance - */ - public function setDefinition($definition) - { - if ($definition instanceof InputDefinition) + /** + * Interacts with the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) { - $this->definition = $definition; - } - else - { - $this->definition->setDefinition($definition); } - $this->applicationDefinitionMerged = false; - - return $this; - } - - /** - * Gets the InputDefinition attached to this Command. - * - * @return InputDefinition $definition An InputDefinition instance - */ - public function getDefinition() - { - return $this->definition; - } - - /** - * Adds an argument. - * - * @param string $name The argument name - * @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) - * - * @return Command The current instance - */ - public function addArgument($name, $mode = null, $description = '', $default = null) - { - $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); - - return $this; - } - - /** - * Adds an option. - * - * @param string $name The option name - * @param string $shortcut The shortcut (can be null) - * @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE) - * - * @return Command The current instance - */ - public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) - { - $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); - - return $this; - } - - /** - * Sets the name of the command. - * - * This method can set both the namespace and the name if - * you separate them by a colon (:) - * - * $command->setName('foo:bar'); - * - * @param string $name The command name - * - * @return Command The current instance - */ - public function setName($name) - { - if (false !== $pos = strrpos($name, ':')) + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) { - $namespace = substr($name, 0, $pos); - $name = substr($name, $pos + 1); - } - else - { - $namespace = $this->namespace; } - if (!$name) + /** + * Runs the command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + public function run(InputInterface $input, OutputInterface $output) { - throw new \InvalidArgumentException('A command name cannot be empty'); + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (\Exception $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + $input->validate(); + + if ($this->code) { + return call_user_func($this->code, $input, $output); + } else { + return $this->execute($input, $output); + } } - $this->namespace = $namespace; - $this->name = $name; - - return $this; - } - - /** - * Returns the command namespace. - * - * @return string The command namespace - */ - public function getNamespace() - { - return $this->namespace; - } - - /** - * Returns the command name - * - * @return string The command name - */ - public function getName() - { - return $this->name; - } - - /** - * Returns the fully qualified command name. - * - * @return string The fully qualified command name - */ - public function getFullName() - { - return $this->getNamespace() ? $this->getNamespace().':'.$this->getName() : $this->getName(); - } - - /** - * Sets the description for the command. - * - * @param string $description The description for the command - * - * @return Command The current instance - */ - public function setDescription($description) - { - $this->description = $description; - - return $this; - } - - /** - * Returns the description for the command. - * - * @return string The description for the command - */ - public function getDescription() - { - return $this->description; - } - - /** - * Sets the help for the command. - * - * @param string $help The help for the command - * - * @return Command The current instance - */ - public function setHelp($help) - { - $this->help = $help; - - return $this; - } - - /** - * Returns the help for the command. - * - * @return string The help for the command - */ - public function getHelp() - { - return $this->help; - } - - /** - * Returns the processed help for the command replacing the %command.name% and - * %command.full_name% patterns with the real values dynamically. - * - * @return string The processed help for the command - */ - public function getProcessedHelp() - { - $name = $this->namespace.':'.$this->name; - - $placeholders = array( - '%command.name%', - '%command.full_name%' - ); - $replacements = array( - $name, - $_SERVER['PHP_SELF'].' '.$name - ); - - return str_replace($placeholders, $replacements, $this->getHelp()); - } - - /** - * Sets the aliases for the command. - * - * @param array $aliases An array of aliases for the command - * - * @return Command The current instance - */ - public function setAliases($aliases) - { - $this->aliases = $aliases; - - return $this; - } - - /** - * Returns the aliases for the command. - * - * @return array An array of aliases for the command - */ - public function getAliases() - { - return $this->aliases; - } - - /** - * Returns the synopsis for the command. - * - * @return string The synopsis - */ - public function getSynopsis() - { - return sprintf('%s %s', $this->getFullName(), $this->definition->getSynopsis()); - } - - /** - * Gets a helper instance by name. - * - * @param string $name The helper name - * - * @return mixed The helper value - * - * @throws \InvalidArgumentException if the helper is not defined - */ - protected function getHelper($name) - { - return $this->application->getHelperSet()->get($name); - } - - /** - * Gets a helper instance by name. - * - * @param string $name The helper name - * - * @return mixed The helper value - * - * @throws \InvalidArgumentException if the helper is not defined - */ - public function __get($name) - { - return $this->application->getHelperSet()->get($name); - } - - /** - * Returns a text representation of the command. - * - * @return string A string representing the command - */ - public function asText() - { - $messages = array( - 'Usage:', - ' '.$this->getSynopsis(), - '', - ); - - if ($this->getAliases()) + /** + * Sets the code to execute when running this command. + * + * @param \Closure $code A \Closure + * + * @return Command The current instance + */ + public function setCode(\Closure $code) { - $messages[] = 'Aliases: '.implode(', ', $this->getAliases()).''; + $this->code = $code; + + return $this; } - $messages[] = $this->definition->asText(); - - if ($help = $this->getProcessedHelp()) + /** + * Merges the application definition with the command definition. + */ + protected function mergeApplicationDefinition() { - $messages[] = 'Help:'; - $messages[] = ' '.implode("\n ", explode("\n", $help))."\n"; + if (null === $this->application || true === $this->applicationDefinitionMerged) { + return; + } + + $this->definition->setArguments(array_merge( + $this->application->getDefinition()->getArguments(), + $this->definition->getArguments() + )); + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; } - return implode("\n", $messages); - } - - /** - * Returns an XML representation of the command. - * - * @param Boolean $asDom Whether to return a DOM or an XML string - * - * @return string|DOMDocument An XML string representing the command - */ - public function asXml($asDom = false) - { - $dom = new \DOMDocument('1.0', 'UTF-8'); - $dom->formatOutput = true; - $dom->appendChild($commandXML = $dom->createElement('command')); - $commandXML->setAttribute('id', $this->getFullName()); - $commandXML->setAttribute('namespace', $this->getNamespace() ? $this->getNamespace() : '_global'); - $commandXML->setAttribute('name', $this->getName()); - - $commandXML->appendChild($usageXML = $dom->createElement('usage')); - $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), ''))); - - $commandXML->appendChild($descriptionXML = $dom->createElement('description')); - $descriptionXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $this->getDescription())))); - - $commandXML->appendChild($helpXML = $dom->createElement('help')); - $help = $this->help; - $helpXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $help)))); - - $commandXML->appendChild($aliasesXML = $dom->createElement('aliases')); - foreach ($this->getAliases() as $alias) + /** + * Sets an array of argument and option instances. + * + * @param array|Definition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + */ + public function setDefinition($definition) { - $aliasesXML->appendChild($aliasXML = $dom->createElement('alias')); - $aliasXML->appendChild($dom->createTextNode($alias)); + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; } - $definition = $this->definition->asXml(true); - $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true)); - $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true)); + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } - return $asDom ? $dom : $dom->saveXml(); - } + /** + * Adds an argument. + * + * @param string $name The argument name + * @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE) + * + * @return Command The current instance + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws \InvalidArgumentException When command name given is empty + */ + public function setName($name) + { + if (false !== $pos = strrpos($name, ':')) { + $namespace = substr($name, 0, $pos); + $name = substr($name, $pos + 1); + } else { + $namespace = $this->namespace; + } + + if (!$name) { + throw new \InvalidArgumentException('A command name cannot be empty.'); + } + + $this->namespace = $namespace; + $this->name = $name; + + return $this; + } + + /** + * Returns the command namespace. + * + * @return string The command namespace + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Returns the command name + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the fully qualified command name. + * + * @return string The fully qualified command name + */ + public function getFullName() + { + return $this->getNamespace() ? $this->getNamespace().':'.$this->getName() : $this->getName(); + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->namespace.':'.$this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%' + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name + ); + + return str_replace($placeholders, $replacements, $this->getHelp()); + } + + /** + * Sets the aliases for the command. + * + * @param array $aliases An array of aliases for the command + * + * @return Command The current instance + */ + public function setAliases($aliases) + { + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @return string The synopsis + */ + public function getSynopsis() + { + return sprintf('%s %s', $this->getFullName(), $this->definition->getSynopsis()); + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws \InvalidArgumentException if the helper is not defined + */ + protected function getHelper($name) + { + return $this->application->getHelperSet()->get($name); + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws \InvalidArgumentException if the helper is not defined + */ + public function __get($name) + { + return $this->application->getHelperSet()->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + */ + public function asText() + { + $messages = array( + 'Usage:', + ' '.$this->getSynopsis(), + '', + ); + + if ($this->getAliases()) { + $messages[] = 'Aliases: '.implode(', ', $this->getAliases()).''; + } + + $messages[] = $this->definition->asText(); + + if ($help = $this->getProcessedHelp()) { + $messages[] = 'Help:'; + $messages[] = ' '.implode("\n ", explode("\n", $help))."\n"; + } + + return implode("\n", $messages); + } + + /** + * Returns an XML representation of the command. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the command + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($commandXML = $dom->createElement('command')); + $commandXML->setAttribute('id', $this->getFullName()); + $commandXML->setAttribute('namespace', $this->getNamespace() ? $this->getNamespace() : '_global'); + $commandXML->setAttribute('name', $this->getName()); + + $commandXML->appendChild($usageXML = $dom->createElement('usage')); + $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), ''))); + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $this->getDescription())))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $help = $this->help; + $helpXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $help)))); + + $commandXML->appendChild($aliasesXML = $dom->createElement('aliases')); + foreach ($this->getAliases() as $alias) { + $aliasesXML->appendChild($aliasXML = $dom->createElement('alias')); + $aliasXML->appendChild($dom->createTextNode($alias)); + } + + $definition = $this->definition->asXml(true); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true)); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true)); + + return $asDom ? $dom : $dom->saveXml(); + } } diff --git a/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php b/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php index 3e93be671..5e0c30739 100644 --- a/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php +++ b/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\Output; use Symfony\Component\Console\Command\Command; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -21,63 +21,57 @@ use Symfony\Component\Console\Command\Command; /** * HelpCommand displays the help for a given command. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class HelpCommand extends Command { - protected $command; + protected $command; - /** - * @see Command - */ - protected function configure() - { - $this->ignoreValidationErrors = true; + /** + * @see Command + */ + protected function configure() + { + $this->ignoreValidationErrors = true; - $this - ->setDefinition(array( - new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), - new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'), - )) - ->setName('help') - ->setAliases(array('?')) - ->setDescription('Displays help for a command') - ->setHelp(<<setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'), + )) + ->setName('help') + ->setAliases(array('?')) + ->setDescription('Displays help for a command') + ->setHelp(<<help command displays help for a given command: - ./symfony help test:all + ./symfony help list You can also output the help as XML by using the --xml option: - ./symfony help --xml test:all + ./symfony help --xml list EOF - ); - } - - public function setCommand(Command $command) - { - $this->command = $command; - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - if (null === $this->command) - { - $this->command = $this->application->getCommand($input->getArgument('command_name')); + ); } - if ($input->getOption('xml')) + public function setCommand(Command $command) { - $output->writeln($this->command->asXml(), Output::OUTPUT_RAW); + $this->command = $command; } - else + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) { - $output->writeln($this->command->asText()); + if (null === $this->command) { + $this->command = $this->application->getCommand($input->getArgument('command_name')); + } + + if ($input->getOption('xml')) { + $output->writeln($this->command->asXml(), Output::OUTPUT_RAW); + } else { + $output->writeln($this->command->asText()); + } } - } } diff --git a/lib/vendor/Symfony/Component/Console/Command/ListCommand.php b/lib/vendor/Symfony/Component/Console/Command/ListCommand.php index a73fee078..f180e4124 100644 --- a/lib/vendor/Symfony/Component/Console/Command/ListCommand.php +++ b/lib/vendor/Symfony/Component/Console/Command/ListCommand.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\Output; use Symfony\Component\Console\Command\Command; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -21,25 +21,23 @@ use Symfony\Component\Console\Command\Command; /** * ListCommand displays the list of all available commands for the application. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class ListCommand extends Command { - /** - * @see Command - */ - protected function configure() - { - $this - ->setDefinition(array( - new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), - new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'), - )) - ->setName('list') - ->setDescription('Lists commands') - ->setHelp(<<setDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'), + )) + ->setName('list') + ->setDescription('Lists commands') + ->setHelp(<<list command lists all commands: ./symfony list @@ -52,21 +50,18 @@ You can also output the information as XML by using the --xml ./symfony list --xml EOF - ); - } + ); + } - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - if ($input->getOption('xml')) + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) { - $output->writeln($this->application->asXml($input->getArgument('namespace')), Output::OUTPUT_RAW); + if ($input->getOption('xml')) { + $output->writeln($this->application->asXml($input->getArgument('namespace')), Output::OUTPUT_RAW); + } else { + $output->writeln($this->application->asText($input->getArgument('namespace'))); + } } - else - { - $output->writeln($this->application->asText($input->getArgument('namespace'))); - } - } } diff --git a/lib/vendor/Symfony/Component/Console/Helper/DialogHelper.php b/lib/vendor/Symfony/Component/Console/Helper/DialogHelper.php index d1e01b790..25c2b04a7 100644 --- a/lib/vendor/Symfony/Component/Console/Helper/DialogHelper.php +++ b/lib/vendor/Symfony/Component/Console/Helper/DialogHelper.php @@ -5,7 +5,7 @@ namespace Symfony\Component\Console\Helper; use Symfony\Component\Console\Output\OutputInterface; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -16,104 +16,95 @@ use Symfony\Component\Console\Output\OutputInterface; /** * The Dialog class provides helpers to interact with the user. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class DialogHelper extends Helper { - /** - * Asks a question to the user. - * - * @param OutputInterface $output - * @param string|array $question The question to ask - * @param string $default The default answer if none is given by the user - * - * @param string The user answer - */ - public function ask(OutputInterface $output, $question, $default = null) - { - // @codeCoverageIgnoreStart - $output->writeln($question); - - $ret = trim(fgets(STDIN)); - - return $ret ? $ret : $default; - // @codeCoverageIgnoreEnd - } - - /** - * Asks a confirmation to the user. - * - * The question will be asked until the user answer by nothing, yes, or no. - * - * @param OutputInterface $output - * @param string|array $question The question to ask - * @param Boolean $default The default answer if the user enters nothing - * - * @param Boolean true if the user has confirmed, false otherwise - */ - public function askConfirmation(OutputInterface $output, $question, $default = true) - { - // @codeCoverageIgnoreStart - $answer = 'z'; - while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) + /** + * Asks a question to the user. + * + * @param OutputInterface $output + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * + * @return string The user answer + */ + public function ask(OutputInterface $output, $question, $default = null) { - $answer = $this->ask($output, $question); + // @codeCoverageIgnoreStart + $output->writeln($question); + + $ret = trim(fgets(STDIN)); + + return $ret ? $ret : $default; + // @codeCoverageIgnoreEnd } - if (false === $default) + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answer by nothing, yes, or no. + * + * @param OutputInterface $output + * @param string|array $question The question to ask + * @param Boolean $default The default answer if the user enters nothing + * + * @return Boolean true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) { - return $answer && 'y' == strtolower($answer[0]); - } - else - { - return !$answer || 'y' == strtolower($answer[0]); - } - // @codeCoverageIgnoreEnd - } + // @codeCoverageIgnoreStart + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } - /** - * Asks for a value and validates the response. - * - * @param OutputInterface $output - * @param string|array $question - * @param Closure $validator - * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) - * - * @return mixed - */ - public function askAndValidate(OutputInterface $output, $question, \Closure $validator, $attempts = false) - { - // @codeCoverageIgnoreStart - $error = null; - while (false === $attempts || $attempts--) - { - if (null !== $error) - { - $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); - } - - $value = $this->ask($output, $question, null); - - try - { - return $validator($value); - } - catch (\Exception $error) - { - } + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } else { + return !$answer || 'y' == strtolower($answer[0]); + } + // @codeCoverageIgnoreEnd } - throw $error; - // @codeCoverageIgnoreEnd - } + /** + * Asks for a value and validates the response. + * + * @param OutputInterface $output + * @param string|array $question + * @param Closure $validator + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * + * @return mixed + * + * @throws \Exception When any of the validator returns an error + */ + public function askAndValidate(OutputInterface $output, $question, \Closure $validator, $attempts = false) + { + // @codeCoverageIgnoreStart + $error = null; + while (false === $attempts || $attempts--) { + if (null !== $error) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } - /** - * Returns the helper's canonical name - */ - public function getName() - { - return 'dialog'; - } + $value = $this->ask($output, $question, null); + + try { + return $validator($value); + } catch (\Exception $error) { + } + } + + throw $error; + // @codeCoverageIgnoreEnd + } + + /** + * Returns the helper's canonical name + */ + public function getName() + { + return 'dialog'; + } } diff --git a/lib/vendor/Symfony/Component/Console/Helper/FormatterHelper.php b/lib/vendor/Symfony/Component/Console/Helper/FormatterHelper.php index 172d7c6df..baa2bc1a9 100644 --- a/lib/vendor/Symfony/Component/Console/Helper/FormatterHelper.php +++ b/lib/vendor/Symfony/Component/Console/Helper/FormatterHelper.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Helper; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,76 +14,69 @@ namespace Symfony\Component\Console\Helper; /** * The Formatter class provides helpers to format messages. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class FormatterHelper extends Helper { - /** - * Formats a message within a section. - * - * @param string $section The section name - * @param string $message The message - * @param string $style The style to apply to the section - */ - public function formatSection($section, $message, $style = 'info') - { - return sprintf("<%s>[%s] %s", $style, $section, $style, $message); - } - - /** - * Formats a message as a block of text. - * - * @param string|array $messages The message to write in the block - * @param string $style The style to apply to the whole block - * @param Boolean $large Whether to return a large block - * - * @return string The formatter message - */ - public function formatBlock($messages, $style, $large = false) - { - if (!is_array($messages)) + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + */ + public function formatSection($section, $message, $style = 'info') { - $messages = array($messages); + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); } - $len = 0; - $lines = array(); - foreach ($messages as $message) + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param Boolean $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) { - $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); - $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + if (!is_array($messages)) { + $messages = array($messages); + } + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $this->strlen($line)); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + foreach ($messages as &$message) { + $message = sprintf('<%s>%s', $style, $message, $style); + } + + return implode("\n", $messages); } - $messages = $large ? array(str_repeat(' ', $len)) : array(); - foreach ($lines as $line) + protected function strlen($string) { - $messages[] = $line.str_repeat(' ', $len - $this->strlen($line)); - } - if ($large) - { - $messages[] = str_repeat(' ', $len); + return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); } - foreach ($messages as &$message) + /** + * Returns the helper's canonical name + */ + public function getName() { - $message = sprintf('<%s>%s', $style, $message, $style); + return 'formatter'; } - - return implode("\n", $messages); - } - - protected function strlen($string) - { - return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); - } - - /** - * Returns the helper's canonical name - */ - public function getName() - { - return 'formatter'; - } } diff --git a/lib/vendor/Symfony/Component/Console/Helper/Helper.php b/lib/vendor/Symfony/Component/Console/Helper/Helper.php index ccd0e4f92..28a8d991f 100644 --- a/lib/vendor/Symfony/Component/Console/Helper/Helper.php +++ b/lib/vendor/Symfony/Component/Console/Helper/Helper.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Helper; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,32 +14,29 @@ namespace Symfony\Component\Console\Helper; /** * Helper is the base class for all helper classes. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ abstract class Helper implements HelperInterface { - protected - $helperSet = null; + protected $helperSet = null; - /** - * Sets the helper set associated with this helper. - * - * @param HelperSet $helperSet A HelperSet instance - */ - public function setHelperSet(HelperSet $helperSet = null) - { - $this->helperSet = $helperSet; - } + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } - /** - * Gets the helper set associated with this helper. - * - * @return HelperSet A HelperSet instance - */ - public function getHelperSet() - { - return $this->helperSet; - } + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } } diff --git a/lib/vendor/Symfony/Component/Console/Helper/HelperInterface.php b/lib/vendor/Symfony/Component/Console/Helper/HelperInterface.php index d0217feb7..7430e0713 100644 --- a/lib/vendor/Symfony/Component/Console/Helper/HelperInterface.php +++ b/lib/vendor/Symfony/Component/Console/Helper/HelperInterface.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Helper; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,30 +14,28 @@ namespace Symfony\Component\Console\Helper; /** * HelperInterface is the interface all helpers must implement. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ interface HelperInterface { - /** - * Sets the helper set associated with this helper. - * - * @param HelperSet $helperSet A HelperSet instance - */ - function setHelperSet(HelperSet $helperSet = null); + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + function setHelperSet(HelperSet $helperSet = null); - /** - * Gets the helper set associated with this helper. - * - * @return HelperSet A HelperSet instance - */ - function getHelperSet(); + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + function getHelperSet(); - /** - * Returns the canonical name of this helper. - * - * @return string The canonical name - */ - function getName(); + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + function getName(); } diff --git a/lib/vendor/Symfony/Component/Console/Helper/HelperSet.php b/lib/vendor/Symfony/Component/Console/Helper/HelperSet.php index 10b86299a..b8b412f79 100644 --- a/lib/vendor/Symfony/Component/Console/Helper/HelperSet.php +++ b/lib/vendor/Symfony/Component/Console/Helper/HelperSet.php @@ -5,7 +5,7 @@ namespace Symfony\Component\Console\Helper; use Symfony\Component\Console\Command\Command; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -16,89 +16,87 @@ use Symfony\Component\Console\Command\Command; /** * HelperSet represents a set of helpers to be used with a command. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class HelperSet { - protected - $helpers = array(), - $command = null; + protected $helpers; + protected $command; - public function __construct(array $helpers = array()) - { - foreach ($helpers as $alias => $helper) + /** + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) { - $this->set($helper, is_int($alias) ? null : $alias); - } - } - - /** - * Sets a helper. - * - * @param HelperInterface $value The helper instance - * @param string $alias An alias - */ - public function set(HelperInterface $helper, $alias = null) - { - $this->helpers[$helper->getName()] = $helper; - if (null !== $alias) - { - $this->helpers[$alias] = $helper; + $this->helpers = array(); + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } } - $helper->setHelperSet($this); - } - - /** - * Returns true if the helper if defined. - * - * @param string $name The helper name - * - * @return Boolean true if the helper is defined, false otherwise - */ - public function has($name) - { - return isset($this->helpers[$name]); - } - - /** - * Gets a helper value. - * - * @param string $name The helper name - * - * @return HelperInterface The helper instance - * - * @throws \InvalidArgumentException if the helper is not defined - */ - public function get($name) - { - if (!$this->has($name)) + /** + * Sets a helper. + * + * @param HelperInterface $value The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) { - throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); } - return $this->helpers[$name]; - } + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return Boolean true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } - /** - * Sets the command associated with this helper set. - * - * @param Command $command A Command instance - */ - public function setCommand(Command $command = null) - { - $this->command = $command; - } + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws \InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } - /** - * Gets the command associated with this helper set. - * - * @return Command A Command instance - */ - public function getCommand() - { - return $this->command; - } + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php b/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php index ca049c865..beb6fc436 100644 --- a/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php +++ b/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -20,7 +20,7 @@ namespace Symfony\Component\Console\Input; * * By default, the `$_SERVER['argv']` array is used for the input values. * - * This can be overriden by explicitly passing the input values in the constructor: + * This can be overridden by explicitly passing the input values in the constructor: * * $input = new ArgvInput($_SERVER['argv']); * @@ -31,254 +31,225 @@ namespace Symfony\Component\Console\Input; * the same rules as the argv one. It's almost always better to use the * `StringInput` when you want to provide your own input. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier * * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 */ class ArgvInput extends Input { - protected $tokens; - protected $parsed; + protected $tokens; + protected $parsed; - /** - * Constructor. - * - * @param array $argv An array of parameters from the CLI (in the argv format) - * @param InputDefinition $definition A InputDefinition instance - */ - public function __construct(array $argv = null, InputDefinition $definition = null) - { - if (null === $argv) + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) { - $argv = $_SERVER['argv']; + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the program name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); } - // strip the program name - array_shift($argv); - - $this->tokens = $argv; - - parent::__construct($definition); - } - - /** - * Processes command line arguments. - */ - protected function parse() - { - $this->parsed = $this->tokens; - while (null !== ($token = array_shift($this->parsed))) + /** + * Processes command line arguments. + */ + protected function parse() { - if ('--' === substr($token, 0, 2)) - { - $this->parseLongOption($token); - } - elseif ('-' === $token[0]) - { - $this->parseShortOption($token); - } - else - { - $this->parseArgument($token); - } - } - } - - /** - * Parses a short option. - * - * @param string $token The current token. - */ - protected function parseShortOption($token) - { - $name = substr($token, 1); - - if (strlen($name) > 1) - { - if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptParameter()) - { - // an option with a value (with no space) - $this->addShortOption($name[0], substr($name, 1)); - } - else - { - $this->parseShortOptionSet($name); - } - } - else - { - $this->addShortOption($name, null); - } - } - - /** - * Parses a short option set. - * - * @param string $token The current token - */ - protected function parseShortOptionSet($name) - { - $len = strlen($name); - for ($i = 0; $i < $len; $i++) - { - if (!$this->definition->hasShortcut($name[$i])) - { - throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); - } - - $option = $this->definition->getOptionForShortcut($name[$i]); - if ($option->acceptParameter()) - { - $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); - - break; - } - else - { - $this->addLongOption($option->getName(), true); - } - } - } - - /** - * Parses a long option. - * - * @param string $token The current token - */ - protected function parseLongOption($token) - { - $name = substr($token, 2); - - if (false !== $pos = strpos($name, '=')) - { - $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); - } - else - { - $this->addLongOption($name, null); - } - } - - /** - * Parses an argument. - * - * @param string $token The current token - */ - protected function parseArgument($token) - { - if (!$this->definition->hasArgument(count($this->arguments))) - { - throw new \RuntimeException('Too many arguments.'); + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ('--' === substr($token, 0, 2)) { + $this->parseLongOption($token); + } elseif ('-' === $token[0]) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } } - $this->arguments[$this->definition->getArgument(count($this->arguments))->getName()] = $token; - } - - /** - * Adds a short option value. - * - * @param string $shortcut The short option key - * @param mixed $value The value for the option - */ - protected function addShortOption($shortcut, $value) - { - if (!$this->definition->hasShortcut($shortcut)) + /** + * Parses a short option. + * + * @param string $token The current token. + */ + protected function parseShortOption($token) { - throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptParameter()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } } - $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); - } - - /** - * Adds a long option value. - * - * @param string $name The long option key - * @param mixed $value The value for the option - */ - protected function addLongOption($name, $value) - { - if (!$this->definition->hasOption($name)) + /** + * Parses a short option set. + * + * @param string $token The current token + * + * @throws \RuntimeException When option given doesn't exist + */ + protected function parseShortOptionSet($name) { - throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + $len = strlen($name); + for ($i = 0; $i < $len; $i++) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptParameter()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), true); + } + } } - $option = $this->definition->getOption($name); - - if (null === $value && $option->acceptParameter()) + /** + * Parses a long option. + * + * @param string $token The current token + */ + protected function parseLongOption($token) { - // if option accepts an optional or mandatory argument - // let's see if there is one provided - $next = array_shift($this->parsed); - if ('-' !== $next[0]) - { - $value = $next; - } - else - { - array_unshift($this->parsed, $next); - } + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } } - if (null === $value) + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws \RuntimeException When too many arguments are given + */ + protected function parseArgument($token) { - if ($option->isParameterRequired()) - { - throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); - } + if (!$this->definition->hasArgument(count($this->arguments))) { + throw new \RuntimeException('Too many arguments.'); + } - $value = $option->isParameterOptional() ? $option->getDefault() : true; + $this->arguments[$this->definition->getArgument(count($this->arguments))->getName()] = $token; } - $this->options[$name] = $value; - } - - /** - * Returns the first argument from the raw parameters (not parsed). - * - * @return string The value of the first argument or null otherwise - */ - public function getFirstArgument() - { - foreach ($this->tokens as $token) + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + protected function addShortOption($shortcut, $value) { - if ($token && '-' === $token[0]) - { - continue; - } + if (!$this->definition->hasShortcut($shortcut)) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } - return $token; - } - } - - /** - * Returns true if the raw parameters (not parsed) contains a value. - * - * This method is to be used to introspect the input parameters - * before it has been validated. It must be used carefully. - * - * @param string|array $values The value(s) to look for in the raw parameters (can be an array) - * - * @return Boolean true if the value is contained in the raw parameters - */ - public function hasParameterOption($values) - { - if (!is_array($values)) - { - $values = array($values); + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); } - foreach ($this->tokens as $v) + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + protected function addLongOption($name, $value) { - if (in_array($v, $values)) - { - return true; - } + if (!$this->definition->hasOption($name)) { + throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value && $option->acceptParameter()) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if ('-' !== $next[0]) { + $value = $next; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isParameterRequired()) { + throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isParameterOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; } - return false; - } + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contains a value. + * + * This method is to be used to introspect the input parameters + * before it has been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + if (!is_array($values)) { + $values = array($values); + } + + foreach ($this->tokens as $v) { + if (in_array($v, $values)) { + return true; + } + } + + return false; + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php b/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php index 4e16f5e82..786d3ad71 100644 --- a/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php +++ b/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -18,157 +18,145 @@ namespace Symfony\Component\Console\Input; * * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class ArrayInput extends Input { - protected $parameters; + protected $parameters; - /** - * Constructor. - * - * @param array $param An array of parameters - * @param InputDefinition $definition A InputDefinition instance - */ - public function __construct(array $parameters, InputDefinition $definition = null) - { - $this->parameters = $parameters; - - parent::__construct($definition); - } - - /** - * Returns the first argument from the raw parameters (not parsed). - * - * @return string The value of the first argument or null otherwise - */ - public function getFirstArgument() - { - foreach ($this->parameters as $key => $value) + /** + * Constructor. + * + * @param array $param An array of parameters + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $parameters, InputDefinition $definition = null) { - if ($key && '-' === $key[0]) - { - continue; - } + $this->parameters = $parameters; - return $value; - } - } - - /** - * Returns true if the raw parameters (not parsed) contains a value. - * - * This method is to be used to introspect the input parameters - * before it has been validated. It must be used carefully. - * - * @param string|array $value The values to look for in the raw parameters (can be an array) - * - * @return Boolean true if the value is contained in the raw parameters - */ - public function hasParameterOption($values) - { - if (!is_array($values)) - { - $values = array($values); + parent::__construct($definition); } - foreach ($this->parameters as $k => $v) + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() { - if (!is_int($k)) - { - $v = $k; - } + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } - if (in_array($v, $values)) - { - return true; - } + return $value; + } } - return false; - } - - /** - * Processes command line arguments. - */ - protected function parse() - { - foreach ($this->parameters as $key => $value) + /** + * Returns true if the raw parameters (not parsed) contains a value. + * + * This method is to be used to introspect the input parameters + * before it has been validated. It must be used carefully. + * + * @param string|array $value The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) { - if ('--' === substr($key, 0, 2)) - { - $this->addLongOption(substr($key, 2), $value); - } - elseif ('-' === $key[0]) - { - $this->addShortOption(substr($key, 1), $value); - } - else - { - $this->addArgument($key, $value); - } - } - } + if (!is_array($values)) { + $values = array($values); + } - /** - * Adds a short option value. - * - * @param string $shortcut The short option key - * @param mixed $value The value for the option - */ - protected function addShortOption($shortcut, $value) - { - if (!$this->definition->hasShortcut($shortcut)) - { - throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; } - $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); - } - - /** - * Adds a long option value. - * - * @param string $name The long option key - * @param mixed $value The value for the option - */ - protected function addLongOption($name, $value) - { - if (!$this->definition->hasOption($name)) + /** + * Processes command line arguments. + */ + protected function parse() { - throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + foreach ($this->parameters as $key => $value) { + if ('--' === substr($key, 0, 2)) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } } - $option = $this->definition->getOption($name); - - if (null === $value) + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + protected function addShortOption($shortcut, $value) { - if ($option->isParameterRequired()) - { - throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); - } + if (!$this->definition->hasShortcut($shortcut)) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } - $value = $option->isParameterOptional() ? $option->getDefault() : true; + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); } - $this->options[$name] = $value; - } - - /** - * Adds an argument value. - * - * @param string $name The argument name - * @param mixed $value The value for the argument - */ - protected function addArgument($name, $value) - { - if (!$this->definition->hasArgument($name)) + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + * @throws \InvalidArgumentException When a required value is missing + */ + protected function addLongOption($name, $value) { - throw new \RuntimeException(sprintf('The "%s" argument does not exist.', $name)); + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isParameterRequired()) { + throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isParameterOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; } - $this->arguments[$name] = $value; - } + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + protected function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/Input.php b/lib/vendor/Symfony/Component/Console/Input/Input.php index 1a07bc15d..9819b188e 100644 --- a/lib/vendor/Symfony/Component/Console/Input/Input.php +++ b/lib/vendor/Symfony/Component/Console/Input/Input.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -20,179 +20,180 @@ namespace Symfony\Component\Console\Input; * * `StringInput`: The input is provided as a string * * `ArrayInput`: The input is provided as an array * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ abstract class Input implements InputInterface { - protected $definition; - protected $options; - protected $arguments; - protected $interactive = true; + protected $definition; + protected $options; + protected $arguments; + protected $interactive = true; - /** - * Constructor. - * - * @param InputDefinition $definition A InputDefinition instance - */ - public function __construct(InputDefinition $definition = null) - { - if (null === $definition) + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) { - $this->definition = new InputDefinition(); - } - else - { - $this->bind($definition); - $this->validate(); - } - } - - /** - * Binds the current Input instance with the given arguments and options. - * - * @param InputDefinition $definition A InputDefinition instance - */ - public function bind(InputDefinition $definition) - { - $this->arguments = array(); - $this->options = array(); - $this->definition = $definition; - - $this->parse(); - } - - /** - * Processes command line arguments. - */ - abstract protected function parse(); - - public function validate() - { - if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) - { - throw new \RuntimeException('Not enough arguments.'); - } - } - - public function isInteractive() - { - return $this->interactive; - } - - public function setInteractive($interactive) - { - $this->interactive = (Boolean) $interactive; - } - - /** - * Returns the argument values. - * - * @return array An array of argument values - */ - public function getArguments() - { - return array_merge($this->definition->getArgumentDefaults(), $this->arguments); - } - - /** - * Returns the argument value for a given argument name. - * - * @param string $name The argument name - * - * @return mixed The argument value - */ - public function getArgument($name) - { - if (!$this->definition->hasArgument($name)) - { - throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } } - return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); - } - - /** - * Sets an argument value by name. - * - * @param string $name The argument name - * @param string $value The argument value - */ - public function setArgument($name, $value) - { - if (!$this->definition->hasArgument($name)) + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) { - throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); } - $this->arguments[$name] = $value; - } + /** + * Processes command line arguments. + */ + abstract protected function parse(); - /** - * Returns true if an InputArgument object exists by name or position. - * - * @param string|integer $name The InputArgument name or position - * - * @return Boolean true if the InputArgument object exists, false otherwise - */ - public function hasArgument($name) - { - return $this->definition->hasArgument($name); - } - - /** - * Returns the options values. - * - * @return array An array of option values - */ - public function getOptions() - { - return array_merge($this->definition->getOptionDefaults(), $this->options); - } - - /** - * Returns the option value for a given option name. - * - * @param string $name The option name - * - * @return mixed The option value - */ - public function getOption($name) - { - if (!$this->definition->hasOption($name)) + /** + * @throws \RuntimeException When not enough arguments are given + */ + public function validate() { - throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) { + throw new \RuntimeException('Not enough arguments.'); + } } - return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); - } - - /** - * Sets an option value by name. - * - * @param string $name The option name - * @param string $value The option value - */ - public function setOption($name, $value) - { - if (!$this->definition->hasOption($name)) + public function isInteractive() { - throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + return $this->interactive; } - $this->options[$name] = $value; - } + public function setInteractive($interactive) + { + $this->interactive = (Boolean) $interactive; + } - /** - * Returns true if an InputOption object exists by name. - * - * @param string $name The InputOption name - * - * @return Boolean true if the InputOption object exists, false otherwise - */ - public function hasOption($name) - { - return $this->definition->hasOption($name); - } + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/InputArgument.php b/lib/vendor/Symfony/Component/Console/Input/InputArgument.php index 6bb8c1a8d..f96eecb6b 100644 --- a/lib/vendor/Symfony/Component/Console/Input/InputArgument.php +++ b/lib/vendor/Symfony/Component/Console/Input/InputArgument.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,121 +14,115 @@ namespace Symfony\Component\Console\Input; /** * Represents a command line argument. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class InputArgument { - const REQUIRED = 1; - const OPTIONAL = 2; - const IS_ARRAY = 4; + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; - protected $name; - protected $mode; - protected $default; - protected $description; + protected $name; + protected $mode; + protected $default; + protected $description; - /** - * Constructor. - * - * @param string $name The argument name - * @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (for self::OPTIONAL mode only) - */ - public function __construct($name, $mode = null, $description = '', $default = null) - { - if (null === $mode) + /** + * Constructor. + * + * @param string $name The argument name + * @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws \InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) { - $mode = self::OPTIONAL; - } - else if (is_string($mode) || $mode > 7) - { - throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + if (null === $mode) { + $mode = self::OPTIONAL; + } else if (is_string($mode) || $mode > 7) { + throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); } - $this->name = $name; - $this->mode = $mode; - $this->description = $description; - - $this->setDefault($default); - } - - /** - * Returns the argument name. - * - * @return string The argument name - */ - public function getName() - { - return $this->name; - } - - /** - * Returns true if the argument is required. - * - * @return Boolean true if parameter mode is self::REQUIRED, false otherwise - */ - public function isRequired() - { - return self::REQUIRED === (self::REQUIRED & $this->mode); - } - - /** - * Returns true if the argument can take multiple values. - * - * @return Boolean true if mode is self::IS_ARRAY, false otherwise - */ - public function isArray() - { - return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); - } - - /** - * Sets the default value. - * - * @param mixed $default The default value - */ - public function setDefault($default = null) - { - if (self::REQUIRED === $this->mode && null !== $default) + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() { - throw new \LogicException('Cannot set a default value except for Parameter::OPTIONAL mode.'); + return $this->name; } - if ($this->isArray()) + /** + * Returns true if the argument is required. + * + * @return Boolean true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() { - if (null === $default) - { - $default = array(); - } - else if (!is_array($default)) - { - throw new \LogicException('A default value for an array argument must be an array.'); - } + return self::REQUIRED === (self::REQUIRED & $this->mode); } - $this->default = $default; - } + /** + * Returns true if the argument can take multiple values. + * + * @return Boolean true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } - /** - * Returns the default value. - * - * @return mixed The default value - */ - public function getDefault() - { - return $this->default; - } + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new \LogicException('Cannot set a default value except for Parameter::OPTIONAL mode.'); + } - /** - * Returns the description text. - * - * @return string The description text - */ - public function getDescription() - { - return $this->description; - } + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } else if (!is_array($default)) { + throw new \LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php b/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php index 6f1efd20b..cc737440d 100644 --- a/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php +++ b/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -21,487 +21,451 @@ namespace Symfony\Component\Console\Input; * new InputOption('foo', 'f', InputOption::PARAMETER_REQUIRED), * )); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class InputDefinition { - protected $arguments; - protected $requiredCount; - protected $hasAnArrayArgument = false; - protected $hasOptional; - protected $options; - protected $shortcuts; + protected $arguments; + protected $requiredCount; + protected $hasAnArrayArgument = false; + protected $hasOptional; + protected $options; + protected $shortcuts; - /** - * Constructor. - * - * @param array $definition An array of InputArgument and InputOption instance - */ - public function __construct(array $definition = array()) - { - $this->setDefinition($definition); - } - - public function setDefinition(array $definition) - { - $arguments = array(); - $options = array(); - foreach ($definition as $item) + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = array()) { - if ($item instanceof InputOption) - { - $options[] = $item; - } - else - { - $arguments[] = $item; - } + $this->setDefinition($definition); } - $this->setArguments($arguments); - $this->setOptions($options); - } - - /** - * Sets the InputArgument objects. - * - * @param array $arguments An array of InputArgument objects - */ - public function setArguments($arguments = array()) - { - $this->arguments = array(); - $this->requiredCount = 0; - $this->hasOptional = false; - $this->addArguments($arguments); - } - - /** - * Add an array of InputArgument objects. - * - * @param array $arguments An array of InputArgument objects - */ - public function addArguments($arguments = array()) - { - if (null !== $arguments) + public function setDefinition(array $definition) { - foreach ($arguments as $argument) - { - $this->addArgument($argument); - } - } - } - - /** - * Add an InputArgument object. - * - * @param InputArgument $argument An InputArgument object - */ - public function addArgument(InputArgument $argument) - { - if (isset($this->arguments[$argument->getName()])) - { - throw new \LogicException(sprintf('An argument with name "%s" already exist.', $argument->getName())); - } - - if ($this->hasAnArrayArgument) - { - throw new \LogicException('Cannot add an argument after an array argument.'); - } - - if ($argument->isRequired() && $this->hasOptional) - { - throw new \LogicException('Cannot add a required argument after an optional one.'); - } - - if ($argument->isArray()) - { - $this->hasAnArrayArgument = true; - } - - if ($argument->isRequired()) - { - ++$this->requiredCount; - } - else - { - $this->hasOptional = true; - } - - $this->arguments[$argument->getName()] = $argument; - } - - /** - * Returns an InputArgument by name or by position. - * - * @param string|integer $name The InputArgument name or position - * - * @return InputArgument An InputArgument object - */ - public function getArgument($name) - { - $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; - - if (!$this->hasArgument($name)) - { - throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); - } - - return $arguments[$name]; - } - - /** - * Returns true if an InputArgument object exists by name or position. - * - * @param string|integer $name The InputArgument name or position - * - * @return Boolean true if the InputArgument object exists, false otherwise - */ - public function hasArgument($name) - { - $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; - - return isset($arguments[$name]); - } - - /** - * Gets the array of InputArgument objects. - * - * @return array An array of InputArgument objects - */ - public function getArguments() - { - return $this->arguments; - } - - /** - * Returns the number of InputArguments. - * - * @return integer The number of InputArguments - */ - public function getArgumentCount() - { - return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); - } - - /** - * Returns the number of required InputArguments. - * - * @return integer The number of required InputArguments - */ - public function getArgumentRequiredCount() - { - return $this->requiredCount; - } - - /** - * Gets the default values. - * - * @return array An array of default values - */ - public function getArgumentDefaults() - { - $values = array(); - foreach ($this->arguments as $argument) - { - $values[$argument->getName()] = $argument->getDefault(); - } - - return $values; - } - - /** - * Sets the InputOption objects. - * - * @param array $options An array of InputOption objects - */ - public function setOptions($options = array()) - { - $this->options = array(); - $this->shortcuts = array(); - $this->addOptions($options); - } - - /** - * Add an array of InputOption objects. - * - * @param array $options An array of InputOption objects - */ - public function addOptions($options = array()) - { - foreach ($options as $option) - { - $this->addOption($option); - } - } - - /** - * Add an InputOption object. - * - * @param InputOption $option An InputOption object - */ - public function addOption(InputOption $option) - { - if (isset($this->options[$option->getName()])) - { - throw new \LogicException(sprintf('An option named "%s" already exist.', $option->getName())); - } - else if (isset($this->shortcuts[$option->getShortcut()])) - { - throw new \LogicException(sprintf('An option with shortcut "%s" already exist.', $option->getShortcut())); - } - - $this->options[$option->getName()] = $option; - if ($option->getShortcut()) - { - $this->shortcuts[$option->getShortcut()] = $option->getName(); - } - } - - /** - * Returns an InputOption by name. - * - * @param string $name The InputOption name - * - * @return InputOption A InputOption object - */ - public function getOption($name) - { - if (!$this->hasOption($name)) - { - throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); - } - - return $this->options[$name]; - } - - /** - * Returns true if an InputOption object exists by name. - * - * @param string $name The InputOption name - * - * @return Boolean true if the InputOption object exists, false otherwise - */ - public function hasOption($name) - { - return isset($this->options[$name]); - } - - /** - * Gets the array of InputOption objects. - * - * @return array An array of InputOption objects - */ - public function getOptions() - { - return $this->options; - } - - /** - * Returns true if an InputOption object exists by shortcut. - * - * @param string $name The InputOption shortcut - * - * @return Boolean true if the InputOption object exists, false otherwise - */ - public function hasShortcut($name) - { - return isset($this->shortcuts[$name]); - } - - /** - * Gets an InputOption by shortcut. - * - * @return InputOption An InputOption object - */ - public function getOptionForShortcut($shortcut) - { - return $this->getOption($this->shortcutToName($shortcut)); - } - - /** - * Gets an array of default values. - * - * @return array An array of all default values - */ - public function getOptionDefaults() - { - $values = array(); - foreach ($this->options as $option) - { - $values[$option->getName()] = $option->getDefault(); - } - - return $values; - } - - /** - * Returns the InputOption name given a shortcut. - * - * @param string $shortcut The shortcut - * - * @return string The InputOption name - */ - protected function shortcutToName($shortcut) - { - if (!isset($this->shortcuts[$shortcut])) - { - throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); - } - - return $this->shortcuts[$shortcut]; - } - - /** - * Gets the synopsis. - * - * @return string The synopsis - */ - public function getSynopsis() - { - $elements = array(); - foreach ($this->getOptions() as $option) - { - $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; - $elements[] = sprintf('['.($option->isParameterRequired() ? '%s--%s="..."' : ($option->isParameterOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); - } - - foreach ($this->getArguments() as $argument) - { - $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : '')); - - if ($argument->isArray()) - { - $elements[] = sprintf('... [%sN]', $argument->getName()); - } - } - - return implode(' ', $elements); - } - - /** - * Returns a textual representation of the InputDefinition. - * - * @return string A string representing the InputDefinition - */ - public function asText() - { - // find the largest option or argument name - $max = 0; - foreach ($this->getOptions() as $option) - { - $max = strlen($option->getName()) + 2 > $max ? strlen($option->getName()) + 2 : $max; - } - foreach ($this->getArguments() as $argument) - { - $max = strlen($argument->getName()) > $max ? strlen($argument->getName()) : $max; - } - ++$max; - - $text = array(); - - if ($this->getArguments()) - { - $text[] = 'Arguments:'; - foreach ($this->getArguments() as $argument) - { - if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) - { - $default = sprintf(' (default: %s)', is_array($argument->getDefault()) ? str_replace("\n", '', var_export($argument->getDefault(), true)): $argument->getDefault()); - } - else - { - $default = ''; + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } } - $text[] = sprintf(" %-${max}s %s%s", $argument->getName(), $argument->getDescription(), $default); - } - - $text[] = ''; + $this->setArguments($arguments); + $this->setOptions($options); } - if ($this->getOptions()) + /** + * Sets the InputArgument objects. + * + * @param array $arguments An array of InputArgument objects + */ + public function setArguments($arguments = array()) { - $text[] = 'Options:'; + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } - foreach ($this->getOptions() as $option) - { - if ($option->acceptParameter() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) - { - $default = sprintf(' (default: %s)', is_array($option->getDefault()) ? str_replace("\n", '', print_r($option->getDefault(), true)): $option->getDefault()); + /** + * Add an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } } - else - { - $default = ''; + } + + /** + * Add an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws \LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new \LogicException(sprintf('An argument with name "%s" already exist.', $argument->getName())); } - $multiple = $option->isArray() ? ' (multiple values allowed)' : ''; - $text[] = sprintf(' %-'.$max.'s %s%s%s%s', '--'.$option->getName().'', $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', $option->getDescription(), $default, $multiple); - } - - $text[] = ''; - } - - return implode("\n", $text); - } - - /** - * Returns an XML representation of the InputDefinition. - * - * @param Boolean $asDom Whether to return a DOM or an XML string - * - * @return string|DOMDocument An XML string representing the InputDefinition - */ - public function asXml($asDom = false) - { - $dom = new \DOMDocument('1.0', 'UTF-8'); - $dom->formatOutput = true; - $dom->appendChild($definitionXML = $dom->createElement('definition')); - - $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); - foreach ($this->getArguments() as $argument) - { - $argumentsXML->appendChild($argumentXML = $dom->createElement('argument')); - $argumentXML->setAttribute('name', $argument->getName()); - $argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); - $argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); - $argumentXML->appendChild($descriptionXML = $dom->createElement('description')); - $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); - - $argumentXML->appendChild($defaultsXML = $dom->createElement('defaults')); - $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : ($argument->getDefault() ? array($argument->getDefault()) : array()); - foreach ($defaults as $default) - { - $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); - $defaultXML->appendChild($dom->createTextNode($default)); - } - } - - $definitionXML->appendChild($optionsXML = $dom->createElement('options')); - foreach ($this->getOptions() as $option) - { - $optionsXML->appendChild($optionXML = $dom->createElement('option')); - $optionXML->setAttribute('name', '--'.$option->getName()); - $optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); - $optionXML->setAttribute('accept_parameter', $option->acceptParameter() ? 1 : 0); - $optionXML->setAttribute('is_parameter_required', $option->isParameterRequired() ? 1 : 0); - $optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); - $optionXML->appendChild($descriptionXML = $dom->createElement('description')); - $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); - - if ($option->acceptParameter()) - { - $optionXML->appendChild($defaultsXML = $dom->createElement('defaults')); - $defaults = is_array($option->getDefault()) ? $option->getDefault() : ($option->getDefault() ? array($option->getDefault()) : array()); - foreach ($defaults as $default) - { - $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); - $defaultXML->appendChild($dom->createTextNode($default)); + if ($this->hasAnArrayArgument) { + throw new \LogicException('Cannot add an argument after an array argument.'); } - } + + if ($argument->isRequired() && $this->hasOptional) { + throw new \LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; } - return $asDom ? $dom : $dom->saveXml(); - } + /** + * Returns an InputArgument by name or by position. + * + * @param string|integer $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + if (!$this->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return array An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return integer The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return integer The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param array $options An array of InputOption objects + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Add an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Add an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws \LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()])) { + throw new \LogicException(sprintf('An option named "%s" already exist.', $option->getName())); + } else if (isset($this->shortcuts[$option->getShortcut()])) { + throw new \LogicException(sprintf('An option with shortcut "%s" already exist.', $option->getShortcut())); + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + $this->shortcuts[$option->getShortcut()] = $option->getName(); + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return array An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws \InvalidArgumentException When option given does not exist + */ + protected function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @return string The synopsis + */ + public function getSynopsis() + { + $elements = array(); + foreach ($this->getOptions() as $option) { + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('['.($option->isParameterRequired() ? '%s--%s="..."' : ($option->isParameterOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); + } + + foreach ($this->getArguments() as $argument) { + $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : '')); + + if ($argument->isArray()) { + $elements[] = sprintf('... [%sN]', $argument->getName()); + } + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + */ + public function asText() + { + // find the largest option or argument name + $max = 0; + foreach ($this->getOptions() as $option) { + $max = strlen($option->getName()) + 2 > $max ? strlen($option->getName()) + 2 : $max; + } + foreach ($this->getArguments() as $argument) { + $max = strlen($argument->getName()) > $max ? strlen($argument->getName()) : $max; + } + ++$max; + + $text = array(); + + if ($this->getArguments()) { + $text[] = 'Arguments:'; + foreach ($this->getArguments() as $argument) { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' (default: %s)', is_array($argument->getDefault()) ? str_replace("\n", '', var_export($argument->getDefault(), true)): $argument->getDefault()); + } else { + $default = ''; + } + + $text[] = sprintf(" %-${max}s %s%s", $argument->getName(), $argument->getDescription(), $default); + } + + $text[] = ''; + } + + if ($this->getOptions()) { + $text[] = 'Options:'; + + foreach ($this->getOptions() as $option) { + if ($option->acceptParameter() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' (default: %s)', is_array($option->getDefault()) ? str_replace("\n", '', print_r($option->getDefault(), true)): $option->getDefault()); + } else { + $default = ''; + } + + $multiple = $option->isArray() ? ' (multiple values allowed)' : ''; + $text[] = sprintf(' %-'.$max.'s %s%s%s%s', '--'.$option->getName().'', $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', $option->getDescription(), $default, $multiple); + } + + $text[] = ''; + } + + return implode("\n", $text); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the InputDefinition + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($this->getArguments() as $argument) { + $argumentsXML->appendChild($argumentXML = $dom->createElement('argument')); + $argumentXML->setAttribute('name', $argument->getName()); + $argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $argumentXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $argumentXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : ($argument->getDefault() ? array($argument->getDefault()) : array()); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($this->getOptions() as $option) { + $optionsXML->appendChild($optionXML = $dom->createElement('option')); + $optionXML->setAttribute('name', '--'.$option->getName()); + $optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + $optionXML->setAttribute('accept_parameter', $option->acceptParameter() ? 1 : 0); + $optionXML->setAttribute('is_parameter_required', $option->isParameterRequired() ? 1 : 0); + $optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $optionXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptParameter()) { + $optionXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($option->getDefault()) ? $option->getDefault() : ($option->getDefault() ? array($option->getDefault()) : array()); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $asDom ? $dom : $dom->saveXml(); + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/InputInterface.php b/lib/vendor/Symfony/Component/Console/Input/InputInterface.php index 738698e88..a0f1aa0dc 100644 --- a/lib/vendor/Symfony/Component/Console/Input/InputInterface.php +++ b/lib/vendor/Symfony/Component/Console/Input/InputInterface.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,45 +14,43 @@ namespace Symfony\Component\Console\Input; /** * InputInterface is the interface implemented by all input classes. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ interface InputInterface { - /** - * Returns the first argument from the raw parameters (not parsed). - * - * @return string The value of the first argument or null otherwise - */ - function getFirstArgument(); + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + function getFirstArgument(); - /** - * Returns true if the raw parameters (not parsed) contains a value. - * - * This method is to be used to introspect the input parameters - * before it has been validated. It must be used carefully. - * - * @param string $value The value to look for in the raw parameters - * - * @return Boolean true if the value is contained in the raw parameters - */ - function hasParameterOption($value); + /** + * Returns true if the raw parameters (not parsed) contains a value. + * + * This method is to be used to introspect the input parameters + * before it has been validated. It must be used carefully. + * + * @param string $value The value to look for in the raw parameters + * + * @return Boolean true if the value is contained in the raw parameters + */ + function hasParameterOption($value); - /** - * Binds the current Input instance with the given arguments and options. - * - * @param InputDefinition $definition A InputDefinition instance - */ - function bind(InputDefinition $definition); + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + function bind(InputDefinition $definition); - function validate(); + function validate(); - function getArguments(); + function getArguments(); - function getArgument($name); + function getArgument($name); - function getOptions(); + function getOptions(); - function getOption($name); + function getOption($name); } diff --git a/lib/vendor/Symfony/Component/Console/Input/InputOption.php b/lib/vendor/Symfony/Component/Console/Input/InputOption.php index 0f9505da1..ca76c826f 100644 --- a/lib/vendor/Symfony/Component/Console/Input/InputOption.php +++ b/lib/vendor/Symfony/Component/Console/Input/InputOption.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,178 +14,165 @@ namespace Symfony\Component\Console\Input; /** * Represents a command line option. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class InputOption { - const PARAMETER_NONE = 1; - const PARAMETER_REQUIRED = 2; - const PARAMETER_OPTIONAL = 4; - const PARAMETER_IS_ARRAY = 8; + const PARAMETER_NONE = 1; + const PARAMETER_REQUIRED = 2; + const PARAMETER_OPTIONAL = 4; + const PARAMETER_IS_ARRAY = 8; - protected $name; - protected $shortcut; - protected $mode; - protected $default; - protected $description; + protected $name; + protected $shortcut; + protected $mode; + protected $default; + protected $description; - /** - * Constructor. - * - * @param string $name The option name - * @param string $shortcut The shortcut (can be null) - * @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE) - */ - public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) - { - if ('--' === substr($name, 0, 2)) + /** + * Constructor. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE) + * + * @throws \InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) { - $name = substr($name, 2); + if ('--' === substr($name, 0, 2)) { + $name = substr($name, 2); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if ('-' === $shortcut[0]) { + $shortcut = substr($shortcut, 1); + } + } + + if (null === $mode) { + $mode = self::PARAMETER_NONE; + } else if (!is_int($mode) || $mode > 15) { + throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptParameter()) { + throw new \InvalidArgumentException('Impossible to have an option mode PARAMETER_IS_ARRAY if the option does not accept a parameter.'); + } + + $this->setDefault($default); } - if (empty($shortcut)) + /** + * Returns the shortcut. + * + * @return string The shortcut + */ + public function getShortcut() { - $shortcut = null; + return $this->shortcut; } - if (null !== $shortcut) + /** + * Returns the name. + * + * @return string The name + */ + public function getName() { - if ('-' === $shortcut[0]) - { - $shortcut = substr($shortcut, 1); - } + return $this->name; } - if (null === $mode) + /** + * Returns true if the option accept a parameter. + * + * @return Boolean true if parameter mode is not self::PARAMETER_NONE, false otherwise + */ + public function acceptParameter() { - $mode = self::PARAMETER_NONE; - } - else if (!is_int($mode) || $mode > 15) - { - throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + return $this->isParameterRequired() || $this->isParameterOptional(); } - $this->name = $name; - $this->shortcut = $shortcut; - $this->mode = $mode; - $this->description = $description; - - if ($this->isArray() && !$this->acceptParameter()) + /** + * Returns true if the option requires a parameter. + * + * @return Boolean true if parameter mode is self::PARAMETER_REQUIRED, false otherwise + */ + public function isParameterRequired() { - throw new \InvalidArgumentException('Impossible to have an option mode PARAMETER_IS_ARRAY if the option does not accept a parameter.'); + return self::PARAMETER_REQUIRED === (self::PARAMETER_REQUIRED & $this->mode); } - $this->setDefault($default); - } - - /** - * Returns the shortcut. - * - * @return string The shortcut - */ - public function getShortcut() - { - return $this->shortcut; - } - - /** - * Returns the name. - * - * @return string The name - */ - public function getName() - { - return $this->name; - } - - /** - * Returns true if the option accept a parameter. - * - * @return Boolean true if parameter mode is not self::PARAMETER_NONE, false otherwise - */ - public function acceptParameter() - { - return $this->isParameterRequired() || $this->isParameterOptional(); - } - - /** - * Returns true if the option requires a parameter. - * - * @return Boolean true if parameter mode is self::PARAMETER_REQUIRED, false otherwise - */ - public function isParameterRequired() - { - return self::PARAMETER_REQUIRED === (self::PARAMETER_REQUIRED & $this->mode); - } - - /** - * Returns true if the option takes an optional parameter. - * - * @return Boolean true if parameter mode is self::PARAMETER_OPTIONAL, false otherwise - */ - public function isParameterOptional() - { - return self::PARAMETER_OPTIONAL === (self::PARAMETER_OPTIONAL & $this->mode); - } - - /** - * Returns true if the option can take multiple values. - * - * @return Boolean true if mode is self::PARAMETER_IS_ARRAY, false otherwise - */ - public function isArray() - { - return self::PARAMETER_IS_ARRAY === (self::PARAMETER_IS_ARRAY & $this->mode); - } - - /** - * Sets the default value. - * - * @param mixed $default The default value - */ - public function setDefault($default = null) - { - if (self::PARAMETER_NONE === (self::PARAMETER_NONE & $this->mode) && null !== $default) + /** + * Returns true if the option takes an optional parameter. + * + * @return Boolean true if parameter mode is self::PARAMETER_OPTIONAL, false otherwise + */ + public function isParameterOptional() { - throw new \LogicException('Cannot set a default value when using Option::PARAMETER_NONE mode.'); + return self::PARAMETER_OPTIONAL === (self::PARAMETER_OPTIONAL & $this->mode); } - if ($this->isArray()) + /** + * Returns true if the option can take multiple values. + * + * @return Boolean true if mode is self::PARAMETER_IS_ARRAY, false otherwise + */ + public function isArray() { - if (null === $default) - { - $default = array(); - } - elseif (!is_array($default)) - { - throw new \LogicException('A default value for an array option must be an array.'); - } + return self::PARAMETER_IS_ARRAY === (self::PARAMETER_IS_ARRAY & $this->mode); } - $this->default = $this->acceptParameter() ? $default : false; - } + /** + * Sets the default value. + * + * @param mixed $default The default value + */ + public function setDefault($default = null) + { + if (self::PARAMETER_NONE === (self::PARAMETER_NONE & $this->mode) && null !== $default) { + throw new \LogicException('Cannot set a default value when using Option::PARAMETER_NONE mode.'); + } - /** - * Returns the default value. - * - * @return mixed The default value - */ - public function getDefault() - { - return $this->default; - } + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array option must be an array.'); + } + } - /** - * Returns the description text. - * - * @return string The description text - */ - public function getDescription() - { - return $this->description; - } + $this->default = $this->acceptParameter() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } } diff --git a/lib/vendor/Symfony/Component/Console/Input/StringInput.php b/lib/vendor/Symfony/Component/Console/Input/StringInput.php index 199f8d6e0..bc8cc2cb5 100644 --- a/lib/vendor/Symfony/Component/Console/Input/StringInput.php +++ b/lib/vendor/Symfony/Component/Console/Input/StringInput.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Input; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -18,63 +18,54 @@ namespace Symfony\Component\Console\Input; * * $input = new StringInput('foo --bar="foobar"'); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class StringInput extends ArgvInput { - const REGEX_STRING = '([^ ]+?)(?: |(?tokens = $this->tokenize($input); - } - - protected function tokenize($input) - { - $input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input); - - $tokens = array(); - $length = strlen($input); - $cursor = 0; - while ($cursor < $length) + /** + * Constructor. + * + * @param string $input An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct($input, InputDefinition $definition = null) { - if (preg_match('/\s+/A', $input, $match, null, $cursor)) - { - } - elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) - { - $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); - } - elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) - { - $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); - } - elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) - { - $tokens[] = stripcslashes($match[1]); - } - else - { - // should never happen - // @codeCoverageIgnoreStart - throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); - // @codeCoverageIgnoreEnd - } + parent::__construct(array(), $definition); - $cursor += strlen($match[0]); + $this->tokens = $this->tokenize($input); } - return $tokens; - } + /** + * @throws \InvalidArgumentException When unable to parse input (should never happen) + */ + protected function tokenize($input) + { + $input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input); + + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + // @codeCoverageIgnoreStart + throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + // @codeCoverageIgnoreEnd + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } } diff --git a/lib/vendor/Symfony/Component/Console/Output/ConsoleOutput.php b/lib/vendor/Symfony/Component/Console/Output/ConsoleOutput.php index aeaa1db73..9aa479126 100644 --- a/lib/vendor/Symfony/Component/Console/Output/ConsoleOutput.php +++ b/lib/vendor/Symfony/Component/Console/Output/ConsoleOutput.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Output; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -22,20 +22,18 @@ namespace Symfony\Component\Console\Output; * * $output = new StreamOutput(fopen('php://stdout', 'w')); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class ConsoleOutput extends StreamOutput { - /** - * Constructor. - * - * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) - * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) - */ - public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null) - { - parent::__construct(fopen('php://stdout', 'w'), $verbosity, $decorated); - } + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null) + { + parent::__construct(fopen('php://stdout', 'w'), $verbosity, $decorated); + } } diff --git a/lib/vendor/Symfony/Component/Console/Output/NullOutput.php b/lib/vendor/Symfony/Component/Console/Output/NullOutput.php index 05928ce83..695ca0eab 100644 --- a/lib/vendor/Symfony/Component/Console/Output/NullOutput.php +++ b/lib/vendor/Symfony/Component/Console/Output/NullOutput.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Output; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -16,19 +16,17 @@ namespace Symfony\Component\Console\Output; * * $output = new NullOutput(); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class NullOutput extends Output { - /** - * Writes a message to the output. - * - * @param string $message A message to write to the output - * @param Boolean $newline Whether to add a newline or not - */ - public function doWrite($message, $newline) - { - } + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + public function doWrite($message, $newline) + { + } } diff --git a/lib/vendor/Symfony/Component/Console/Output/Output.php b/lib/vendor/Symfony/Component/Console/Output/Output.php index 101e46b8e..f4542f456 100644 --- a/lib/vendor/Symfony/Component/Console/Output/Output.php +++ b/lib/vendor/Symfony/Component/Console/Output/Output.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Output; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -20,211 +20,212 @@ namespace Symfony\Component\Console\Output; * * verbose: -v (more output - debug) * * quiet: -q (no output) * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ abstract class Output implements OutputInterface { - const VERBOSITY_QUIET = 0; - const VERBOSITY_NORMAL = 1; - const VERBOSITY_VERBOSE = 2; + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; - const OUTPUT_NORMAL = 0; - const OUTPUT_RAW = 1; - const OUTPUT_PLAIN = 2; + const OUTPUT_NORMAL = 0; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; - protected $verbosity; - protected $decorated; + protected $verbosity; + protected $decorated; - static protected $styles = array( - 'error' => array('bg' => 'red', 'fg' => 'white'), - 'info' => array('fg' => 'green'), - 'comment' => array('fg' => 'yellow'), - 'question' => array('bg' => 'cyan', 'fg' => 'black'), - ); - static protected $options = array('bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8); - static protected $foreground = array('black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37); - static protected $background = array('black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47); + static protected $styles = array( + 'error' => array('bg' => 'red', 'fg' => 'white'), + 'info' => array('fg' => 'green'), + 'comment' => array('fg' => 'yellow'), + 'question' => array('bg' => 'cyan', 'fg' => 'black'), + ); + static protected $options = array('bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8); + static protected $foreground = array('black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37); + static protected $background = array('black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47); - /** - * Constructor. - * - * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) - * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) - */ - public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null) - { - $this->decorated = (Boolean) $decorated; - $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; - } - - /** - * Sets a new style. - * - * @param string $name The style name - * @param array $options An array of options - */ - static public function setStyle($name, $options = array()) - { - static::$styles[strtolower($name)] = $options; - } - - /** - * Sets the decorated flag. - * - * @param Boolean $decorated Whether to decorated the messages or not - */ - public function setDecorated($decorated) - { - $this->decorated = (Boolean) $decorated; - } - - /** - * Gets the decorated flag. - * - * @return Boolean true if the output will decorate messages, false otherwise - */ - public function isDecorated() - { - return $this->decorated; - } - - /** - * Sets the verbosity of the output. - * - * @param integer $level The level of verbosity - */ - public function setVerbosity($level) - { - $this->verbosity = (int) $level; - } - - /** - * Gets the current verbosity of the output. - * - * @return integer The current level of verbosity - */ - public function getVerbosity() - { - return $this->verbosity; - } - - /** - * Writes a message to the output and adds a newline at the end. - * - * @param string|array $messages The message as an array of lines of a single string - * @param integer $type The type of output - */ - public function writeln($messages, $type = 0) - { - $this->write($messages, true, $type); - } - - /** - * Writes a message to the output. - * - * @param string|array $messages The message as an array of lines of a single string - * @param Boolean $newline Whether to add a newline or not - * @param integer $type The type of output - */ - public function write($messages, $newline = false, $type = 0) - { - if (self::VERBOSITY_QUIET === $this->verbosity) + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null) { - return; + $this->decorated = (Boolean) $decorated; + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; } - if (!is_array($messages)) + /** + * Sets a new style. + * + * @param string $name The style name + * @param array $options An array of options + */ + static public function setStyle($name, $options = array()) { - $messages = array($messages); + static::$styles[strtolower($name)] = $options; } - foreach ($messages as $message) + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorated the messages or not + */ + public function setDecorated($decorated) { - switch ($type) - { - case Output::OUTPUT_NORMAL: - $message = $this->format($message); - break; - case Output::OUTPUT_RAW: - break; - case Output::OUTPUT_PLAIN: - $message = strip_tags($this->format($message)); - break; - default: - throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); - } - - $this->doWrite($message, $newline); - } - } - - /** - * Writes a message to the output. - * - * @param string $message A message to write to the output - * @param Boolean $newline Whether to add a newline or not - */ - abstract public function doWrite($message, $newline); - - /** - * Formats a message according to the given styles. - * - * @param string $message The message to style - * - * @return string The styled message - */ - protected function format($message) - { - $message = preg_replace_callback('#<([a-z][a-z0-9\-_]+)>#i', array($this, 'replaceStartStyle'), $message); - - return preg_replace_callback('##i', array($this, 'replaceEndStyle'), $message); - } - - protected function replaceStartStyle($match) - { - if (!$this->decorated) - { - return ''; + $this->decorated = (Boolean) $decorated; } - if (!isset(static::$styles[strtolower($match[1])])) + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + */ + public function isDecorated() { - throw new \InvalidArgumentException(sprintf('Unknown style "%s".', $match[1])); + return $this->decorated; } - $parameters = static::$styles[strtolower($match[1])]; - $codes = array(); - - if (isset($parameters['fg'])) + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + */ + public function setVerbosity($level) { - $codes[] = static::$foreground[$parameters['fg']]; + $this->verbosity = (int) $level; } - if (isset($parameters['bg'])) + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + */ + public function getVerbosity() { - $codes[] = static::$background[$parameters['bg']]; + return $this->verbosity; } - foreach (static::$options as $option => $value) + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $type The type of output + */ + public function writeln($messages, $type = 0) { - if (isset($parameters[$option]) && $parameters[$option]) - { - $codes[] = $value; - } + $this->write($messages, true, $type); } - return "\033[".implode(';', $codes)."m"; - } - - protected function replaceEndStyle($match) - { - if (!$this->decorated) + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + */ + public function write($messages, $newline = false, $type = 0) { - return ''; + if (self::VERBOSITY_QUIET === $this->verbosity) { + return; + } + + if (!is_array($messages)) { + $messages = array($messages); + } + + foreach ($messages as $message) { + switch ($type) { + case Output::OUTPUT_NORMAL: + $message = $this->format($message); + break; + case Output::OUTPUT_RAW: + break; + case Output::OUTPUT_PLAIN: + $message = strip_tags($this->format($message)); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); + } + + $this->doWrite($message, $newline); + } } - return "\033[0m"; - } + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + abstract public function doWrite($message, $newline); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + protected function format($message) + { + $message = preg_replace_callback('#<([a-z][a-z0-9\-_=;]+)>#i', array($this, 'replaceStartStyle'), $message); + + return preg_replace_callback('##i', array($this, 'replaceEndStyle'), $message); + } + + /** + * @throws \InvalidArgumentException When style is unknown + */ + protected function replaceStartStyle($match) + { + if (!$this->decorated) { + return ''; + } + + if (isset(static::$styles[strtolower($match[1])])) { + $parameters = static::$styles[strtolower($match[1])]; + } else { + // bg=blue;fg=red + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($match[1]), $matches, PREG_SET_ORDER)) { + throw new \InvalidArgumentException(sprintf('Unknown style "%s".', $match[1])); + } + + $parameters = array(); + foreach ($matches as $match) { + $parameters[$match[1]] = $match[2]; + } + } + + $codes = array(); + + if (isset($parameters['fg'])) { + $codes[] = static::$foreground[$parameters['fg']]; + } + + if (isset($parameters['bg'])) { + $codes[] = static::$background[$parameters['bg']]; + } + + foreach (static::$options as $option => $value) { + if (isset($parameters[$option]) && $parameters[$option]) { + $codes[] = $value; + } + } + + return "\033[".implode(';', $codes).'m'; + } + + protected function replaceEndStyle($match) + { + if (!$this->decorated) { + return ''; + } + + return "\033[0m"; + } } diff --git a/lib/vendor/Symfony/Component/Console/Output/OutputInterface.php b/lib/vendor/Symfony/Component/Console/Output/OutputInterface.php index b12ed0786..289f4b428 100644 --- a/lib/vendor/Symfony/Component/Console/Output/OutputInterface.php +++ b/lib/vendor/Symfony/Component/Console/Output/OutputInterface.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Output; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -14,31 +14,32 @@ namespace Symfony\Component\Console\Output; /** * OutputInterface is the interface implemented by all Output classes. * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ interface OutputInterface { - /** - * Writes a message to the output. - * - * @param string|array $messages The message as an array of lines of a single string - * @param integer $type The type of output - */ - public function write($messages, $type = 0); + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + */ + function write($messages, $newline = false, $type = 0); - /** - * Sets the verbosity of the output. - * - * @param integer $level The level of verbosity - */ - public function setVerbosity($level); + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + */ + function setVerbosity($level); - /** - * Sets the decorated flag. - * - * @param Boolean $decorated Whether to decorated the messages or not - */ - public function setDecorated($decorated); + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorated the messages or not + */ + function setDecorated($decorated); } diff --git a/lib/vendor/Symfony/Component/Console/Output/StreamOutput.php b/lib/vendor/Symfony/Component/Console/Output/StreamOutput.php index c490187d9..55805c729 100644 --- a/lib/vendor/Symfony/Component/Console/Output/StreamOutput.php +++ b/lib/vendor/Symfony/Component/Console/Output/StreamOutput.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Console\Output; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -22,88 +22,84 @@ namespace Symfony\Component\Console\Output; * * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); * - * @package symfony - * @subpackage console - * @author Fabien Potencier + * @author Fabien Potencier */ class StreamOutput extends Output { - protected $stream; + protected $stream; - /** - * Constructor. - * - * @param mixed $stream A stream resource - * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) - * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) - */ - public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null) - { - if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) + /** + * Constructor. + * + * @param mixed $stream A stream resource + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * + * @throws \InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null) { - throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport($decorated); + } + + parent::__construct($verbosity, $decorated); } - $this->stream = $stream; - - if (null === $decorated) + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() { - $decorated = $this->hasColorSupport($decorated); + return $this->stream; } - parent::__construct($verbosity, $decorated); - } - - /** - * Gets the stream attached to this StreamOutput instance. - * - * @return resource A stream resource - */ - public function getStream() - { - return $this->stream; - } - - /** - * Writes a message to the output. - * - * @param string $message A message to write to the output - * @param Boolean $newline Whether to add a newline or not - */ - public function doWrite($message, $newline) - { - if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + * + * @throws \RuntimeException When unable to write output (should never happen) + */ + public function doWrite($message, $newline) { - // @codeCoverageIgnoreStart - // should never happen - throw new \RuntimeException('Unable to write output.'); - // @codeCoverageIgnoreEnd + if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + // @codeCoverageIgnoreStart + // should never happen + throw new \RuntimeException('Unable to write output.'); + // @codeCoverageIgnoreEnd + } + + flush(); } - flush(); - } - - /** - * Returns true if the stream supports colorization. - * - * Colorization is disabled if not supported by the stream: - * - * - windows without ansicon - * - non tty consoles - * - * @return Boolean true if the stream supports colorization, false otherwise - */ - protected function hasColorSupport() - { - // @codeCoverageIgnoreStart - if (DIRECTORY_SEPARATOR == '\\') + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - windows without ansicon + * - non tty consoles + * + * @return Boolean true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() { - return false !== getenv('ANSICON'); + // @codeCoverageIgnoreStart + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON'); + } else { + return function_exists('posix_isatty') && @posix_isatty($this->stream); + } + // @codeCoverageIgnoreEnd } - else - { - return function_exists('posix_isatty') && @posix_isatty($this->stream); - } - // @codeCoverageIgnoreEnd - } } diff --git a/lib/vendor/Symfony/Component/Console/Shell.php b/lib/vendor/Symfony/Component/Console/Shell.php index 32a8f1604..278572795 100644 --- a/lib/vendor/Symfony/Component/Console/Shell.php +++ b/lib/vendor/Symfony/Component/Console/Shell.php @@ -7,7 +7,7 @@ use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\ConsoleOutput; /* - * This file is part of the symfony framework. + * This file is part of the Symfony framework. * * (c) Fabien Potencier * @@ -21,118 +21,108 @@ use Symfony\Component\Console\Output\ConsoleOutput; * This class only works with a PHP compiled with readline support * (either --with-readline or --with-libedit) * - * @package symfony - * @subpackage cli - * @author Fabien Potencier + * @author Fabien Potencier */ class Shell { - protected $application; - protected $history; - protected $output; + protected $application; + protected $history; + protected $output; - /** - * Constructor. - * - * If there is no readline support for the current PHP executable - * a \RuntimeException exception is thrown. - * - * @param Application $application An application instance - */ - public function __construct(Application $application) - { - if (!function_exists('readline')) + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + * + * @throws \RuntimeException When Readline extension is not enabled + */ + public function __construct(Application $application) { - throw new \RuntimeException('Unable to start the shell as the Readline extension is not enabled.'); + if (!function_exists('readline')) { + throw new \RuntimeException('Unable to start the shell as the Readline extension is not enabled.'); + } + + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); } - $this->application = $application; - $this->history = getenv('HOME').'/.history_'.$application->getName(); - $this->output = new ConsoleOutput(); - } - - /** - * Runs the shell. - */ - public function run() - { - $this->application->setAutoExit(false); - $this->application->setCatchExceptions(true); - - readline_read_history($this->history); - readline_completion_function(array($this, 'autocompleter')); - - $this->output->writeln($this->getHeader()); - while (true) + /** + * Runs the shell. + */ + public function run() { - $command = readline($this->application->getName().' > '); + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); - if (false === $command) - { - $this->output->writeln("\n"); + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); - break; - } + $this->output->writeln($this->getHeader()); + while (true) { + $command = readline($this->application->getName().' > '); - readline_add_history($command); - readline_write_history($this->history); + if (false === $command) { + $this->output->writeln("\n"); - if (0 !== $ret = $this->application->run(new StringInput($command), $this->output)) - { - $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); - } - } - } + break; + } - /** - * Tries to return autocompletion for the current entered text. - * - * @param string $text The last segment of the entered text - * @param integer $position The current position - */ - protected function autocompleter($text, $position) - { - $info = readline_info(); - $text = substr($info['line_buffer'], 0, $info['end']); + readline_add_history($command); + readline_write_history($this->history); - if ($info['point'] !== $info['end']) - { - return true; + if (0 !== $ret = $this->application->run(new StringInput($command), $this->output)) { + $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); + } + } } - // task name? - if (false === strpos($text, ' ') || !$text) + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * @param integer $position The current position + */ + protected function autocompleter($text, $position) { - return array_keys($this->application->getCommands()); + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->getCommands()); + } + + // options and arguments? + try { + $command = $this->application->findCommand(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; } - // options and arguments? - try + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() { - $command = $this->application->findCommand(substr($text, 0, strpos($text, ' '))); - } - catch (\Exception $e) - { - return true; - } - - $list = array('--help'); - foreach ($command->getDefinition()->getOptions() as $option) - { - $list[] = '--'.$option->getName(); - } - - return $list; - } - - /** - * Returns the shell header. - * - * @return string The header string - */ - protected function getHeader() - { - return <<{$this->application->getName()} shell ({$this->application->getVersion()}). @@ -142,5 +132,5 @@ or list to get a list available commands. To exit the shell, type ^D. EOF; - } + } } diff --git a/lib/vendor/Symfony/Component/Console/Tester/ApplicationTester.php b/lib/vendor/Symfony/Component/Console/Tester/ApplicationTester.php index 166d24d89..b7092470b 100644 --- a/lib/vendor/Symfony/Component/Console/Tester/ApplicationTester.php +++ b/lib/vendor/Symfony/Component/Console/Tester/ApplicationTester.php @@ -6,87 +6,96 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\StreamOutput; +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * @author Fabien Potencier + */ class ApplicationTester { - protected $application; - protected $display; - protected $input; - protected $output; + protected $application; + protected $display; + protected $input; + protected $output; - /** - * Constructor. - * - * @param Application $application A Application instance to test. - */ - public function __construct(Application $application) - { - $this->application = $application; - } - - /** - * Executes the application. - * - * Available options: - * - * * interactive: Sets the input interactive flag - * * decorated: Sets the output decorated flag - * * verbosity: Sets the output verbosity flag - * - * @param array $input An array of arguments and options - * @param array $options An array of options - */ - public function run(array $input, $options = array()) - { - $this->input = new ArrayInput($input); - if (isset($options['interactive'])) + /** + * Constructor. + * + * @param Application $application A Application instance to test. + */ + public function __construct(Application $application) { - $this->input->setInteractive($options['interactive']); + $this->application = $application; } - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + */ + public function run(array $input, $options = array()) { - $this->output->setDecorated($options['decorated']); - } - if (isset($options['verbosity'])) - { - $this->output->setVerbosity($options['verbosity']); + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + $ret = $this->application->run($this->input, $this->output); + + rewind($this->output->getStream()); + + return $this->display = stream_get_contents($this->output->getStream()); } - $ret = $this->application->run($this->input, $this->output); + /** + * Gets the display returned by the last execution of the application. + * + * @return string The display + */ + public function getDisplay() + { + return $this->display; + } - rewind($this->output->getStream()); + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } - return $this->display = stream_get_contents($this->output->getStream()); - } - - /** - * Gets the display returned by the last execution of the application. - * - * @return string The display - */ - public function getDisplay() - { - return $this->display; - } - - /** - * Gets the input instance used by the last execution of the application. - * - * @return InputInterface The current input instance - */ - public function getInput() - { - return $this->input; - } - - /** - * Gets the output instance used by the last execution of the application. - * - * @return OutputInterface The current output instance - */ - public function getOutput() - { - return $this->output; - } + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } } diff --git a/lib/vendor/Symfony/Component/Console/Tester/CommandTester.php b/lib/vendor/Symfony/Component/Console/Tester/CommandTester.php index 8e0443e3d..8c971c0c0 100644 --- a/lib/vendor/Symfony/Component/Console/Tester/CommandTester.php +++ b/lib/vendor/Symfony/Component/Console/Tester/CommandTester.php @@ -6,87 +6,96 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\StreamOutput; +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * @author Fabien Potencier + */ class CommandTester { - protected $command; - protected $display; - protected $input; - protected $output; + protected $command; + protected $display; + protected $input; + protected $output; - /** - * Constructor. - * - * @param Command $command A Command instance to test. - */ - public function __construct(Command $command) - { - $this->command = $command; - } - - /** - * Executes the command. - * - * Available options: - * - * * interactive: Sets the input interactive flag - * * decorated: Sets the output decorated flag - * * verbosity: Sets the output verbosity flag - * - * @param array $input An array of arguments and options - * @param array $options An array of options - */ - public function execute(array $input, array $options = array()) - { - $this->input = new ArrayInput(array_merge($input, array('command' => $this->command->getFullName()))); - if (isset($options['interactive'])) + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) { - $this->input->setInteractive($options['interactive']); + $this->command = $command; } - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) + /** + * Executes the command. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + */ + public function execute(array $input, array $options = array()) { - $this->output->setDecorated($options['decorated']); - } - if (isset($options['verbosity'])) - { - $this->output->setVerbosity($options['verbosity']); + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + $ret = $this->command->run($this->input, $this->output); + + rewind($this->output->getStream()); + + return $this->display = stream_get_contents($this->output->getStream()); } - $ret = $this->command->run($this->input, $this->output); + /** + * Gets the display returned by the last execution of the command. + * + * @return string The display + */ + public function getDisplay() + { + return $this->display; + } - rewind($this->output->getStream()); + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } - return $this->display = stream_get_contents($this->output->getStream()); - } - - /** - * Gets the display returned by the last execution of the command. - * - * @return string The display - */ - public function getDisplay() - { - return $this->display; - } - - /** - * Gets the input instance used by the last execution of the command. - * - * @return InputInterface The current input instance - */ - public function getInput() - { - return $this->input; - } - - /** - * Gets the output instance used by the last execution of the command. - * - * @return OutputInterface The current output instance - */ - public function getOutput() - { - return $this->output; - } + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } } From 515ef3366588de3ff922b62c29368880751b6afe Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 30 Oct 2010 19:33:20 +0200 Subject: [PATCH 053/119] Fix quoting in BasicEntityPersister::_updateTable and BasicEntityPersister::delete. Added 6 tests for quoting of table names in different update, delete and inheritance scenario combinations --- .../ORM/Persisters/BasicEntityPersister.php | 13 ++++-- .../Persisters/JoinedSubclassPersister.php | 27 +++++++---- .../ORM/Functional/Ticket/DDC832Test.php | 46 +++++++++++++++++-- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index f7dacc53c..371e67d49 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -273,7 +273,10 @@ class BasicEntityPersister $updateData = $this->_prepareUpdateData($entity); $tableName = $this->_class->table['name']; if (isset($updateData[$tableName]) && $updateData[$tableName]) { - $this->_updateTable($entity, $tableName, $updateData[$tableName], $this->_class->isVersioned); + $this->_updateTable( + $entity, $this->_class->getQuotedTableName($this->_platform), + $updateData[$tableName], $this->_class->isVersioned + ); } } @@ -282,11 +285,11 @@ class BasicEntityPersister * The UPDATE can optionally be versioned, which requires the entity to have a version field. * * @param object $entity The entity object being updated. - * @param string $tableName The name of the table to apply the UPDATE on. + * @param string $quotedTableName The quoted name of the table to apply the UPDATE on. * @param array $updateData The map of columns to update (column => value). * @param boolean $versioned Whether the UPDATE should be versioned. */ - protected final function _updateTable($entity, $tableName, array $updateData, $versioned = false) + protected final function _updateTable($entity, $quotedTableName, array $updateData, $versioned = false) { $set = $params = $types = array(); @@ -322,7 +325,7 @@ class BasicEntityPersister $types[] = $this->_class->fieldMappings[$versionField]['type']; } - $sql = "UPDATE $tableName SET " . implode(', ', $set) + $sql = "UPDATE $quotedTableName SET " . implode(', ', $set) . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; $result = $this->_conn->executeUpdate($sql, $params, $types); @@ -386,7 +389,7 @@ class BasicEntityPersister $this->deleteJoinTableRecords($identifier); $id = array_combine($this->_class->getIdentifierColumnNames(), $identifier); - $this->_conn->delete($this->_class->table['name'], $id); + $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); } /** diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 357497bb2..ec94bbddb 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -35,9 +35,18 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister /** * Map that maps column names to the table names that own them. * This is mainly a temporary cache, used during a single request. + * + * @var array */ private $_owningTableMap = array(); + /** + * Map of table to quoted table names. + * + * @var array + */ + private $_quotedTableMap = array(); + /** * {@inheritdoc} */ @@ -74,18 +83,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister */ public function getOwningTable($fieldName) { - if ( ! isset($this->_owningTableMap[$fieldName])) { + if (!isset($this->_owningTableMap[$fieldName])) { if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { - $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( - $this->_class->associationMappings[$fieldName]['inherited'] - )->table['name']; + $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { - $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( - $this->_class->fieldMappings[$fieldName]['inherited'] - )->table['name']; + $cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']); } else { - $this->_owningTableMap[$fieldName] = $this->_class->table['name']; + $cm = $this->_class; } + $this->_owningTableMap[$fieldName] = $cm->table['name']; + $this->_quotedTableMap[$cm->table['name']] = $cm->getQuotedTableName($this->_platform); } return $this->_owningTableMap[$fieldName]; @@ -191,12 +198,12 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister if ($updateData) { foreach ($updateData as $tableName => $data) { - $this->_updateTable($entity, $tableName, $data, $isVersioned && $versionedTable == $tableName); + $this->_updateTable($entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName); } // Make sure the table with the version column is updated even if no columns on that // table were affected. if ($isVersioned && ! isset($updateData[$versionedTable])) { - $this->_updateTable($entity, $versionedTable, array(), true); + $this->_updateTable($entity, $this->_quotedTableMap[$versionedTable], array(), true); } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php index cb35b9b14..8277cdd2f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -15,9 +15,9 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase parent::setUp(); try { $this->_schemaTool->createSchema(array( - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832Like'), $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832JoinedIndex'), $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832JoinedTreeIndex'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832Like'), )); } catch(\Exception $e) { @@ -55,7 +55,12 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testQuotedTableJoinedUpdate() { - $this->markTestIncomplete('Not written yet.'); + $index = new DDC832JoinedIndex("test"); + $this->_em->persist($index); + $this->_em->flush(); + + $index->name = "asdf"; + $this->_em->flush(); } /** @@ -63,7 +68,38 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testQuotedTableJoinedRemove() { - $this->markTestIncomplete('Not written yet.'); + $index = new DDC832JoinedIndex("test"); + $this->_em->persist($index); + $this->_em->flush(); + + $this->_em->remove($index); + $this->_em->flush(); + } + + /** + * @group DDC-832 + */ + public function testQuotedTableJoinedChildUpdate() + { + $index = new DDC832JoinedTreeIndex("test", 1, 2); + $this->_em->persist($index); + $this->_em->flush(); + + $index->name = "asdf"; + $this->_em->flush(); + } + + /** + * @group DDC-832 + */ + public function testQuotedTableJoinedChildRemove() + { + $index = new DDC832JoinedTreeIndex("test", 1, 2); + $this->_em->persist($index); + $this->_em->flush(); + + $this->_em->remove($index); + $this->_em->flush(); } } @@ -74,7 +110,7 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase class DDC832Like { /** - * @Id @Column(type="string") @GeneratedValue + * @Id @Column(type="integer") @GeneratedValue */ public $id; @@ -103,7 +139,7 @@ class DDC832Like class DDC832JoinedIndex { /** - * @Id @Column(type="string") @GeneratedValue + * @Id @Column(type="integer") @GeneratedValue */ public $id; From 97eeb437b2de44c338b0ebbe9de25209468c7a0c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 30 Oct 2010 19:54:36 +0200 Subject: [PATCH 054/119] DDC-832 - Fix regression introduced with last commit. --- lib/Doctrine/ORM/Persisters/BasicEntityPersister.php | 10 ---------- .../ORM/Persisters/JoinedSubclassPersister.php | 5 +++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 371e67d49..af45218f9 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -392,16 +392,6 @@ class BasicEntityPersister $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); } - /** - * Gets the ClassMetadata instance of the entity class this persister is used for. - * - * @return Doctrine\ORM\Mapping\ClassMetadata - */ - public function getClassMetadata() - { - return $this->_class; - } - /** * Prepares the changeset of an entity for database insertion (UPDATE). * diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index ec94bbddb..4972ee6b2 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -193,7 +193,8 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister $updateData = $this->_prepareUpdateData($entity); if ($isVersioned = $this->_class->isVersioned) { - $versionedTable = $this->_getVersionedClassMetadata()->table['name']; + $versionedClass = $this->_getVersionedClassMetadata(); + $versionedTable = $versionedClass->table['name']; } if ($updateData) { @@ -203,7 +204,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // Make sure the table with the version column is updated even if no columns on that // table were affected. if ($isVersioned && ! isset($updateData[$versionedTable])) { - $this->_updateTable($entity, $this->_quotedTableMap[$versionedTable], array(), true); + $this->_updateTable($entity, $versionedClass->getQuotedTableName($this->_platform), array(), true); } } } From 23795605fc0b0ccf82e74f9b6213e400a3509098 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 31 Oct 2010 07:06:53 +0100 Subject: [PATCH 055/119] DDC-758 - Fix bugs with adding and removing elements from a cascade merge Collection. This fix leads to a significant hit in merge performance of collections since they have to be initialized to the current database state, leading to an additional sql query being executed + hydration. --- lib/Doctrine/ORM/PersistentCollection.php | 2 +- lib/Doctrine/ORM/UnitOfWork.php | 11 +- .../ORM/Functional/DetachedEntityTest.php | 28 ++- .../ORM/Functional/Ticket/DDC758Test.php | 184 ++++++++++++++++++ 4 files changed, 214 insertions(+), 11 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC758Test.php diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 6c09e9697..bf7c6da1e 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -196,7 +196,7 @@ final class PersistentCollection implements Collection * Initializes the collection by loading its contents from the database * if the collection is not yet initialized. */ - private function initialize() + public function initialize() { if ( ! $this->initialized && $this->association) { if ($this->isDirty) { diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 5b5782df8..90541a890 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1437,7 +1437,14 @@ class UnitOfWork implements PropertyChangedListener $prop->setValue($managedCopy, $managedCol); $this->originalEntityData[$oid][$name] = $managedCol; } - $managedCol->setInitialized($assoc2['isCascadeMerge']); + if ($assoc2['isCascadeMerge']) { + $managedCol->initialize(); + $managedCol->takeSnapshot(); + if (!$managedCol->isEmpty()) { + $managedCol->unwrap()->clear(); + $managedCol->setDirty(true); + } + } } } if ($class->isChangeTrackingNotify()) { @@ -1456,7 +1463,7 @@ class UnitOfWork implements PropertyChangedListener if ($assoc['type'] & ClassMetadata::TO_ONE) { $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); } else { - $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->unwrap()->add($managedCopy); + $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->add($managedCopy); if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { $class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy); } diff --git a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php index 16f3ce602..a993606cf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php @@ -54,7 +54,7 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase $user->status = 'developer'; $ph1 = new CmsPhonenumber; - $ph1->phonenumber = 1234; + $ph1->phonenumber = "1234"; $user->addPhonenumber($ph1); $this->_em->persist($user); @@ -68,24 +68,36 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase unset($user); $user = unserialize($serialized); + + $this->assertEquals(1, count($user->getPhonenumbers()), "Pre-Condition: 1 Phonenumber"); $ph2 = new CmsPhonenumber; - $ph2->phonenumber = 56789; + $ph2->phonenumber = "56789"; $user->addPhonenumber($ph2); - $this->assertEquals(2, count($user->getPhonenumbers())); + $oldPhonenumbers = $user->getPhonenumbers(); + $this->assertEquals(2, count($oldPhonenumbers), "Pre-Condition: 2 Phonenumbers"); $this->assertFalse($this->_em->contains($user)); $this->_em->persist($ph2); - + // Merge back in $user = $this->_em->merge($user); // merge cascaded to phonenumbers + $this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $user->phonenumbers[0]->user); + $this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $user->phonenumbers[1]->user); + $im = $this->_em->getUnitOfWork()->getIdentityMap(); $this->_em->flush(); - $this->assertTrue($this->_em->contains($user)); - $this->assertEquals(2, count($user->getPhonenumbers())); + $this->assertTrue($this->_em->contains($user), "Failed to assert that merged user is contained inside EntityManager persistence context."); $phonenumbers = $user->getPhonenumbers(); - $this->assertTrue($this->_em->contains($phonenumbers[0])); - $this->assertTrue($this->_em->contains($phonenumbers[1])); + $this->assertNotSame($oldPhonenumbers, $phonenumbers, "Merge should replace the Detached Collection with a new PersistentCollection."); + $this->assertEquals(2, count($phonenumbers), "Failed to assert that two phonenumbers are contained in the merged users phonenumber collection."); + + $this->assertType('Doctrine\Tests\Models\CMS\CmsPhonenumber', $phonenumbers[1]); + $this->assertTrue($this->_em->contains($phonenumbers[1]), "Failed to assert that second phonenumber in collection is contained inside EntityManager persistence context."); + + $this->assertType('Doctrine\Tests\Models\CMS\CmsPhonenumber', $phonenumbers[0]); + $this->assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($phonenumbers[0])); + $this->assertTrue($this->_em->contains($phonenumbers[0]), "Failed to assert that first phonenumber in collection is contained inside EntityManager persistence context."); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC758Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC758Test.php new file mode 100644 index 000000000..60a0ff89c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC758Test.php @@ -0,0 +1,184 @@ +useModelSet("cms"); + + parent::setUp(); + } + + /** + * Helper method to set cascade to merge only + */ + private function setCascadeMergeFor($class) + { + $metadata = $this->_em->getMetadataFactory()->getMetaDataFor($class); + foreach ($metadata->associationMappings as $key => $associationMapping) { + $metadata->associationMappings[$key]["isCascadePersist"] = false; + $metadata->associationMappings[$key]["isCascadeMerge"] = true; + $metadata->associationMappings[$key]["isCascadeRemove"] = false; + $metadata->associationMappings[$key]["isCascadeDetach"] = false; + } + } + + /** + * Test that changing associations on detached entities and then cascade merging them + * causes the database to be updated with the new associations. + * This specifically tests adding new associations. + */ + public function testManyToManyMergeAssociationAdds() + { + $this->setCascadeMergeFor('Doctrine\Tests\Models\CMS\CmsUser'); + $this->setCascadeMergeFor('Doctrine\Tests\Models\CMS\CmsGroup'); + + // Put entities in the database + $cmsUser = new CmsUser(); + $cmsUser->username = "dave"; + $cmsUser->name = "Dave Keen"; + $cmsUser->status = "testing"; + + $group1 = new CmsGroup(); + $group1->name = "Group 1"; + + $group2 = new CmsGroup(); + $group2->name = "Group 2"; + + $this->_em->persist($cmsUser); + $this->_em->persist($group1); + $this->_em->persist($group2); + $this->_em->flush(); + + $cmsUserId = $cmsUser->id; + $group1Id = $group1->id; + $group2Id = $group2->id; + + $this->_em->clear(); + + // Now create detached versions of the entities with some new associations. + $cmsUser = new CmsUser(); + $cmsUser->id = $cmsUserId; + $cmsUser->username = "dave"; + $cmsUser->name = "Dave Keen"; + $cmsUser->status = "testing"; + $cmsUser->groups = new ArrayCollection(); + + $group1 = new CmsGroup(); + $group1->id = $group1Id; + $group1->name = "Group 1"; + $group1->users = new ArrayCollection(); + + $group2 = new CmsGroup(); + $group2->id = $group2Id; + $group2->name = "Group 2"; + $group2->users = new ArrayCollection(); + + $cmsUser->addGroup($group1); + $cmsUser->addGroup($group2); + + // Cascade merge of cmsUser followed by a flush should add in the birectional new many-to-many associations between the user and the groups + $this->_em->merge($cmsUser); + $this->_em->flush(); + + $this->_em->clear(); + + $cmsUsers = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser')->findAll(); + $cmsGroups = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsGroup')->findAll(); + + // Check the entities are in the database + $this->assertEquals(1, sizeof($cmsUsers)); + $this->assertEquals(2, sizeof($cmsGroups)); + + // Check the associations between the entities are now in the database + $this->assertEquals(2, sizeof($cmsUsers[0]->groups)); + $this->assertEquals(1, sizeof($cmsGroups[0]->users)); + $this->assertEquals(1, sizeof($cmsGroups[1]->users)); + + $this->assertSame($cmsUsers[0]->groups[0], $cmsGroups[0]); + $this->assertSame($cmsUsers[0]->groups[1], $cmsGroups[1]); + $this->assertSame($cmsGroups[0]->users[0], $cmsUsers[0]); + $this->assertSame($cmsGroups[1]->users[0], $cmsUsers[0]); + } + + /** + * Test that changing associations on detached entities and then cascade merging them causes the + * database to be updated with the new associations. + */ + public function testManyToManyMergeAssociationRemoves() + { + $this->setCascadeMergeFor('Doctrine\Tests\Models\CMS\CmsUser'); + $this->setCascadeMergeFor('Doctrine\Tests\Models\CMS\CmsGroup'); + + $cmsUser = new CmsUser(); + $cmsUser->username = "dave"; + $cmsUser->name = "Dave Keen"; + $cmsUser->status = "testing"; + + $group1 = new CmsGroup(); + $group1->name = "Group 1"; + + $group2 = new CmsGroup(); + $group2->name = "Group 2"; + + $cmsUser->addGroup($group1); + $cmsUser->addGroup($group2); + + $this->_em->persist($cmsUser); + $this->_em->persist($group1); + $this->_em->persist($group2); + $this->_em->flush(); + + $cmsUserId = $cmsUser->id; + $group1Id = $group1->id; + $group2Id = $group2->id; + + $this->_em->clear(); + + // Now create detached versions of the entities with NO associations. + $cmsUser = new CmsUser(); + $cmsUser->id = $cmsUserId; + $cmsUser->username = "dave"; + $cmsUser->name = "Dave Keen"; + $cmsUser->status = "testing"; + $cmsUser->groups = new ArrayCollection(); + + $group1 = new CmsGroup(); + $group1->id = $group1Id; + $group1->name = "Group 1"; + $group1->users = new ArrayCollection(); + + $group2 = new CmsGroup(); + $group2->id = $group2Id; + $group2->name = "Group 2"; + $group2->users = new ArrayCollection(); + + // Cascade merge of cmsUser followed by a flush should result in the association array collection being empty + $this->_em->merge($cmsUser); + $this->_em->flush(); + + $this->_em->clear(); + + $cmsUsers = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser')->findAll(); + $cmsGroups = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsGroup')->findAll(); + + // Check the entities are in the database + $this->assertEquals(1, sizeof($cmsUsers)); + $this->assertEquals(2, sizeof($cmsGroups)); + + // Check the associations between the entities are now in the database + $this->assertEquals(0, sizeof($cmsUsers[0]->groups)); + $this->assertEquals(0, sizeof($cmsGroups[0]->users)); + $this->assertEquals(0, sizeof($cmsGroups[1]->users)); + } +} \ No newline at end of file From 008601f2ead374a6be9bcbdeb300d110db0569a3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 31 Oct 2010 07:23:58 +0100 Subject: [PATCH 056/119] DDC-758 - Respect notify change tracking. --- lib/Doctrine/ORM/UnitOfWork.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 90541a890..1261a4cd5 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1443,6 +1443,9 @@ class UnitOfWork implements PropertyChangedListener if (!$managedCol->isEmpty()) { $managedCol->unwrap()->clear(); $managedCol->setDirty(true); + if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) { + $this->scheduleForDirtyCheck($managedCol); + } } } } From 692c35e7e2b72b28697532cb677f3625f74179ab Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 31 Oct 2010 08:13:59 +0100 Subject: [PATCH 057/119] DDC-826, DDC-841, DDC-671 - Added another testcase to verify mapped superclass + association dql alias generation --- .../Tests/ORM/Query/SelectSqlGenerationTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 2e0ff090d..642637116 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -832,6 +832,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase $config->setCustomNumericFunctions(array()); } + + /** + * @group DDC-826 + */ + public function testMappedSuperclassAssociationJoin() + { + $this->assertSqlGeneration( + 'SELECT f FROM Doctrine\Tests\Models\DirectoryTree\File f JOIN f.parentDirectory d WHERE f.id = ?1', + 'SELECT f0_.id AS id0, f0_.extension AS extension1, f0_.name AS name2 FROM File f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' + ); + } } From 9211bc2f4ebfeadbc075d3d646bb950bbea5d894 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 31 Oct 2010 09:20:33 +0100 Subject: [PATCH 058/119] DDC-832 - Fix regression in testsuite due to SchemaTool not being able to handle reserved word table names. --- .../Tests/ORM/Functional/Ticket/DDC832Test.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php index 8277cdd2f..c598a8194 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -24,6 +24,16 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase } } + public function tearDown() + { + /* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */ + $platform = $this->_em->getConnection()->getDatabasePlatform(); + $sm = $this->_em->getConnection()->getSchemaManager(); + $sm->dropTable($platform->quoteIdentifier('TREE_INDEX')); + $sm->dropTable($platform->quoteIdentifier('INDEX')); + $sm->dropTable($platform->quoteIdentifier('LIKE')); + } + /** * @group DDC-832 */ From 4f71c3e6a39d564da8a5b125fbaf78aead47d93a Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Sun, 31 Oct 2010 11:07:26 +0100 Subject: [PATCH 059/119] [DDC-812] Fix uninitialized collections of managed entities not being initialized on subsequent fetch-join. --- .../ORM/Internal/Hydration/ObjectHydrator.php | 14 +++--- .../ORM/Functional/Ticket/DDC812Test.php | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC812Test.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index cb33a56a6..202fdc7ff 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -145,12 +145,12 @@ class ObjectHydrator extends AbstractHydrator { $oid = spl_object_hash($entity); $relation = $class->associationMappings[$fieldName]; - + $value = $class->reflFields[$fieldName]->getValue($entity); if ($value === null) { $value = new ArrayCollection; } - + if ( ! $value instanceof PersistentCollection) { $value = new PersistentCollection( $this->_em, @@ -161,17 +161,19 @@ class ObjectHydrator extends AbstractHydrator $class->reflFields[$fieldName]->setValue($entity, $value); $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); $this->_initializedCollections[$oid . $fieldName] = $value; - } else if (isset($this->_hints[Query::HINT_REFRESH])) { - // Is already PersistentCollection, but REFRESH + } else if (isset($this->_hints[Query::HINT_REFRESH]) || + isset($this->_hints['fetched'][$class->name][$fieldName]) && + ! $value->isInitialized()) { + // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED! $value->setDirty(false); $value->setInitialized(true); $value->unwrap()->clear(); $this->_initializedCollections[$oid . $fieldName] = $value; } else { - // Is already PersistentCollection, and DONT REFRESH + // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN! $this->_existingCollections[$oid . $fieldName] = $value; } - + return $value; } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC812Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC812Test.php new file mode 100644 index 000000000..3bb0a51fd --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC812Test.php @@ -0,0 +1,48 @@ +useModelSet('cms'); + parent::setUp(); + } + + /** + * @group DDC-812 + */ + public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManagedEntity() + { + //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $article = new CmsArticle; + $article->topic = "hello"; + $article->text = "talk talk talk"; + + $comment = new CmsComment; + $comment->topic = "good!"; + $comment->text = "stuff!"; + $comment->article = $article; + + $this->_em->persist($article); + $this->_em->persist($comment); + $this->_em->flush(); + $this->_em->clear(); + + $article2 = $this->_em->find(get_class($article), $article->id); + + $article2Again = $this->_em->createQuery( + "select a, c from Doctrine\Tests\Models\CMS\CmsArticle a join a.comments c where a.id = ?1") + ->setParameter(1, $article->id) + ->getSingleResult(); + + $this->assertTrue($article2Again === $article2); + $this->assertTrue($article2Again->comments->isInitialized()); + } +} From 634aa0b2912360eab060c7beffd1b9813d1b8f59 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 31 Oct 2010 11:11:15 +0100 Subject: [PATCH 060/119] DDC-832 - Small adjustments in the patch --- lib/Doctrine/ORM/UnitOfWork.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 1261a4cd5..5088bb273 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1439,12 +1439,11 @@ class UnitOfWork implements PropertyChangedListener } if ($assoc2['isCascadeMerge']) { $managedCol->initialize(); - $managedCol->takeSnapshot(); if (!$managedCol->isEmpty()) { $managedCol->unwrap()->clear(); $managedCol->setDirty(true); if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) { - $this->scheduleForDirtyCheck($managedCol); + $this->scheduleForDirtyCheck($managedCopy); } } } From 6a904a2d679100f91f276093fc077bdf0fb78d14 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 5 Nov 2010 22:13:19 +0100 Subject: [PATCH 061/119] DDC-860 - Allow access to EntityManager in loadClassMetadata event --- .../ORM/Event/LoadClassMetadataEventArgs.php | 35 ++++++++++++++++--- .../ORM/Mapping/ClassMetadataFactory.php | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php b/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php index 862152a99..15ea0799a 100644 --- a/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php +++ b/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php @@ -4,6 +4,9 @@ namespace Doctrine\ORM\Event; use Doctrine\Common\EventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManager; + /** * Class that holds event arguments for a loadMetadata event. * @@ -12,16 +15,40 @@ use Doctrine\Common\EventArgs; */ class LoadClassMetadataEventArgs extends EventArgs { - private $_classMetadata; + /** + * @var ClassMetadata + */ + private $classMetadata; - public function __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata) + /** + * @var EntityManager + */ + private $em; + + /** + * @param ClassMetadata $classMetadata + * @param EntityManager $em + */ + public function __construct(ClassMetadata $classMetadata, EntityManager $em) { - $this->_classMetadata = $classMetadata; + $this->classMetadata = $classMetadata; + $this->em = $em; } + /** + * @return ClassMetadata + */ public function getClassMetadata() { - return $this->_classMetadata; + return $this->classMetadata; + } + + /** + * @return EntityManager + */ + public function getEntityManager() + { + return $this->em; } } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 51994f334..5f1de3883 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -309,7 +309,7 @@ class ClassMetadataFactory $class->setParentClasses($visited); if ($this->evm->hasListeners(Events::loadClassMetadata)) { - $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class); + $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em); $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); } From 31efc9a1495d415b49ba4f812ee696c479e5fe48 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 5 Nov 2010 22:17:05 +0100 Subject: [PATCH 062/119] DDC-861 - Rework ProxyFactory to not use autoloader anymore --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 472e73013..8be75d996 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -77,9 +77,11 @@ class ProxyFactory $proxyClassName = str_replace('\\', '', $className) . 'Proxy'; $fqn = $this->_proxyNamespace . '\\' . $proxyClassName; - if ($this->_autoGenerate && ! class_exists($fqn, false)) { + if (! class_exists($fqn, false)) { $fileName = $this->_proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php'; - $this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate); + if ($this->_autoGenerate) { + $this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate); + } require $fileName; } From 53e8b8f32dddc854a75709ad128b2aebc398ca41 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 9 Nov 2010 22:13:35 +0100 Subject: [PATCH 063/119] DDC-868 - Fix bug where a ClassMetadata instance from a STI child level is processed before the parent. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index d201d1829..cee2fe294 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -92,6 +92,22 @@ class SchemaTool return $schema->toSql($this->_platform); } + /** + * Some instances of ClassMetadata don't need to be processed in the SchemaTool context. This method detects them. + * + * @param ClassMetadata $class + * @param array $processedClasses + * @return bool + */ + private function processingNotRequired($class, array $processedClasses) + { + return ( + isset($processedClasses[$class->name]) || + $class->isMappedSuperclass || + ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) + ); + } + /** * From a given set of metadata classes this method creates a Schema instance. * @@ -110,7 +126,7 @@ class SchemaTool $evm = $this->_em->getEventManager(); foreach ($classes as $class) { - if (isset($processedClasses[$class->name]) || $class->isMappedSuperclass) { + if ($this->processingNotRequired($class, $processedClasses)) { continue; } From ac85584e9bc6a8cb5e00aa71c67e876923056275 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 9 Nov 2010 23:15:14 +0100 Subject: [PATCH 064/119] DDC-870 - Fix several bugs with optimistic locking, conversion of types, multiple updating of values and inheritance related stuff. --- .../ORM/Persisters/BasicEntityPersister.php | 9 ++++++++- .../Persisters/JoinedSubclassPersister.php | 3 +++ .../ORM/Functional/Locking/OptimisticTest.php | 20 ++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index af45218f9..f249ac29a 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -251,6 +251,8 @@ class BasicEntityPersister $sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->getQuotedTableName($this->_platform) . " WHERE " . implode(' = ? AND ', $identifier) . " = ?"; $value = $this->_conn->fetchColumn($sql, array_values((array)$id)); + + $value = Type::getType($class->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform); $this->_class->setFieldValue($entity, $versionField, $value); } @@ -277,6 +279,11 @@ class BasicEntityPersister $entity, $this->_class->getQuotedTableName($this->_platform), $updateData[$tableName], $this->_class->isVersioned ); + + if ($this->_class->isVersioned) { + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->_assignDefaultVersionValue($this->_class, $entity, $id); + } } } @@ -313,7 +320,7 @@ class BasicEntityPersister if ($versioned) { $versionField = $this->_class->versionField; - $versionFieldType = $this->_class->getTypeOfField($versionField); + $versionFieldType = $this->_class->fieldMappings[$versionField]['type']; $versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform); if ($versionFieldType == Type::INTEGER) { $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 4972ee6b2..4cbe11ee9 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -205,6 +205,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // table were affected. if ($isVersioned && ! isset($updateData[$versionedTable])) { $this->_updateTable($entity, $versionedClass->getQuotedTableName($this->_platform), array(), true); + + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->_assignDefaultVersionValue($this->_class, $entity, $id); } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php b/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php index 73c5cdcf1..498a8b0b8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php @@ -100,6 +100,20 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase } } + public function testMultipleFlushesDoIncrementalUpdates() + { + $test = new OptimisticStandard(); + + for ($i = 0; $i < 5; $i++) { + $test->name = 'test' . $i; + $this->_em->persist($test); + $this->_em->flush(); + + $this->assertType('int', $test->getVersion()); + $this->assertEquals($i + 1, $test->getVersion()); + } + } + public function testStandardInsertSetsInitialVersionValue() { $test = new OptimisticStandard(); @@ -107,6 +121,7 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($test); $this->_em->flush(); + $this->assertType('int', $test->getVersion()); $this->assertEquals(1, $test->getVersion()); return $test; @@ -139,10 +154,13 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase { $test = new OptimisticTimestamp(); $test->name = 'Testing'; + + $this->assertNull($test->version, "Pre-Condition"); + $this->_em->persist($test); $this->_em->flush(); - $this->assertTrue(strtotime($test->version) > 0); + $this->assertType('DateTime', $test->version); return $test; } From e4280cf82e96b70068915ce2adf4beea50c67b79 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 11 Nov 2010 21:12:09 +0100 Subject: [PATCH 065/119] DDC-736 - Fix ordering of identification variables in DQL parser to be by specification. --- lib/Doctrine/ORM/Query/Parser.php | 35 +++++++++++++++- .../ORM/Functional/Ticket/DDC736Test.php | 42 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 3d3fb5f5e..c2ee2137e 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -125,6 +125,16 @@ class Parser */ private $_customOutputWalker; + /** + * @var array + */ + private $_identVariableOrder = array(); + + /** + * @var array + */ + private $_identVariableExpressions = array(); + /** * Creates a new query parser object. * @@ -297,6 +307,19 @@ class Parser } } + // fix order of identification variables + if ( count($this->_identVariableExpressions) > 1) { + $n = count($this->_identVariableOrder); + for ($i = 0; $i < $n; $i++) { + if (isset($this->_identVariableExpressions[$this->_identVariableOrder[$i]])) { + $expr = $this->_identVariableExpressions[$this->_identVariableOrder[$i]]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + unset($AST->selectClause->selectExpressions[$key]); + $AST->selectClause->selectExpressions[] = $expr; + } + } + } + if ($this->_customOutputWalker) { $outputWalker = new $this->_customOutputWalker( $this->_query, $this->_parserResult, $this->_queryComponents @@ -1419,6 +1442,7 @@ class Parser $token = $this->_lexer->lookahead; $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $this->_identVariableOrder[] = $aliasIdentificationVariable; $classMetadata = $this->_em->getClassMetadata($abstractSchemaName); // Building queryComponent @@ -1509,6 +1533,7 @@ class Parser $token = $this->_lexer->lookahead; $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $this->_identVariableOrder[] = $aliasIdentificationVariable; // Verify that the association exists. $parentClass = $this->_queryComponents[$joinPathExpression->identificationVariable]['metadata']; @@ -1628,6 +1653,7 @@ class Parser public function SelectExpression() { $expression = null; + $identVariable = null; $fieldAliasIdentificationVariable = null; $peek = $this->_lexer->glimpse(); @@ -1639,7 +1665,7 @@ class Parser $expression = $this->ScalarExpression(); } else { $supportsAlias = false; - $expression = $this->IdentificationVariable(); + $expression = $identVariable = $this->IdentificationVariable(); } } else if ($this->_lexer->lookahead['value'] == '(') { if ($peek['type'] == Lexer::T_SELECT) { @@ -1666,6 +1692,7 @@ class Parser } else if ($this->_lexer->lookahead['type'] == Lexer::T_PARTIAL) { $supportsAlias = false; $expression = $this->PartialObjectExpression(); + $identVariable = $expression->identificationVariable; } else if ($this->_lexer->lookahead['type'] == Lexer::T_INTEGER || $this->_lexer->lookahead['type'] == Lexer::T_FLOAT) { // Shortcut: ScalarExpression => SimpleArithmeticExpression @@ -1694,7 +1721,11 @@ class Parser } } - return new AST\SelectExpression($expression, $fieldAliasIdentificationVariable); + $expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable); + if (!$supportsAlias) { + $this->_identVariableExpressions[$identVariable] = $expr; + } + return $expr; } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php new file mode 100644 index 000000000..e6138f87d --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php @@ -0,0 +1,42 @@ +useModelSet('ecommerce'); + parent::setUp(); + } + + /** + * @group DDC-736 + */ + public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManagedEntity() + { + $cust = new ECommerceCustomer; + $cust->setName('roman'); + + $cart = new ECommerceCart; + $cart->setPayment('cash'); + $cart->setCustomer($cust); + + $this->_em->persist($cust); + $this->_em->persist($cart); + $this->_em->flush(); + $this->_em->clear(); + + $cart2 = $this->_em->createQuery("select c, ca from Doctrine\Tests\Models\ECommerce\ECommerceCart ca join ca.customer c") + ->getSingleResult(/*\Doctrine\ORM\Query::HYDRATE_ARRAY*/); + + $this->assertTrue($cart2 instanceof ECommerceCart); + $this->assertFalse($cart2->getCustomer() instanceof \Doctrine\ORM\Proxy\Proxy); + $this->assertTrue($cart2->getCustomer() instanceof ECommerceCustomer); + } +} From 4ea3277c28303ca25356f25c8b7456525ed446b7 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 11 Nov 2010 21:13:03 +0100 Subject: [PATCH 066/119] DDC-856 - Add default "string" type to discriminator column, throw exception on specification of a bunch of invalid types --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 7 +++++++ lib/Doctrine/ORM/Mapping/MappingException.php | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 077eef1bc..2abf5269c 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -1442,6 +1442,13 @@ class ClassMetadataInfo if ( ! isset($columnDef['fieldName'])) { $columnDef['fieldName'] = $columnDef['name']; } + if ( ! isset($columnDef['type'])) { + $columnDef['type'] = "string"; + } + if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) { + throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); + } + $this->discriminatorColumn = $columnDef; } } diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index 8ea5be213..faf0e830c 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -207,6 +207,11 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("Entity class '$className' is using inheritance but no discriminator column was defined."); } + public static function invalidDiscriminatorColumnType($className, $type) + { + return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!"); + } + /** * @param string $className * @param string $columnName @@ -216,4 +221,5 @@ class MappingException extends \Doctrine\ORM\ORMException { return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); } + } \ No newline at end of file From ae9080aa986e10da1b1e0ca997eb7e7d9ce34fb5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 11 Nov 2010 21:21:17 +0100 Subject: [PATCH 067/119] DDC-873 - throw exception if trying to add @version to @id --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 4 ++++ lib/Doctrine/ORM/Mapping/MappingException.php | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 2abf5269c..3b3d5eee4 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -670,6 +670,10 @@ class ClassMetadataInfo // Complete id mapping if (isset($mapping['id']) && $mapping['id'] === true) { + if ($this->versionField == $mapping['fieldName']) { + throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']); + } + if ( ! in_array($mapping['fieldName'], $this->identifier)) { $this->identifier[] = $mapping['fieldName']; } diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index faf0e830c..a7b1c75ef 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -212,6 +212,11 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!"); } + public static function cannotVersionIdField($className, $fieldName) + { + return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported."); + } + /** * @param string $className * @param string $columnName From d3d3032759748ffde9773ee2cf2133e685e6bf15 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 13 Nov 2010 09:52:35 +0100 Subject: [PATCH 068/119] DDC-736 - Simplified patch and extended test to verify scalar results are still in order. --- lib/Doctrine/ORM/Query/Parser.php | 19 +++++++------------ .../ORM/Functional/Ticket/DDC736Test.php | 12 ++++++++---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index c2ee2137e..e5b9f69ca 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -125,11 +125,6 @@ class Parser */ private $_customOutputWalker; - /** - * @var array - */ - private $_identVariableOrder = array(); - /** * @var array */ @@ -307,12 +302,14 @@ class Parser } } - // fix order of identification variables + // Fix order of identification variables. + // They have to appear in the select clause in the same order as the + // declarations (from ... x join ... y join ... z ...) appear in the query + // as the hydration process relies on that order for proper operation. if ( count($this->_identVariableExpressions) > 1) { - $n = count($this->_identVariableOrder); - for ($i = 0; $i < $n; $i++) { - if (isset($this->_identVariableExpressions[$this->_identVariableOrder[$i]])) { - $expr = $this->_identVariableExpressions[$this->_identVariableOrder[$i]]; + foreach ($this->_queryComponents as $dqlAlias => $qComp) { + if (isset($this->_identVariableExpressions[$dqlAlias])) { + $expr = $this->_identVariableExpressions[$dqlAlias]; $key = array_search($expr, $AST->selectClause->selectExpressions); unset($AST->selectClause->selectExpressions[$key]); $AST->selectClause->selectExpressions[] = $expr; @@ -1442,7 +1439,6 @@ class Parser $token = $this->_lexer->lookahead; $aliasIdentificationVariable = $this->AliasIdentificationVariable(); - $this->_identVariableOrder[] = $aliasIdentificationVariable; $classMetadata = $this->_em->getClassMetadata($abstractSchemaName); // Building queryComponent @@ -1533,7 +1529,6 @@ class Parser $token = $this->_lexer->lookahead; $aliasIdentificationVariable = $this->AliasIdentificationVariable(); - $this->_identVariableOrder[] = $aliasIdentificationVariable; // Verify that the association exists. $parentClass = $this->_queryComponents[$joinPathExpression->identificationVariable]['metadata']; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php index e6138f87d..0c8b764a2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php @@ -32,11 +32,15 @@ class DDC736Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); $this->_em->clear(); - $cart2 = $this->_em->createQuery("select c, ca from Doctrine\Tests\Models\ECommerce\ECommerceCart ca join ca.customer c") + $result = $this->_em->createQuery("select c, c.name, ca, ca.payment from Doctrine\Tests\Models\ECommerce\ECommerceCart ca join ca.customer c") ->getSingleResult(/*\Doctrine\ORM\Query::HYDRATE_ARRAY*/); + + $cart2 = $result[0]; + unset($result[0]); - $this->assertTrue($cart2 instanceof ECommerceCart); - $this->assertFalse($cart2->getCustomer() instanceof \Doctrine\ORM\Proxy\Proxy); - $this->assertTrue($cart2->getCustomer() instanceof ECommerceCustomer); + $this->assertInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceCart', $cart2); + $this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $cart2->getCustomer()); + $this->assertInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceCustomer', $cart2->getCustomer()); + $this->assertEquals(array('name' => 'roman', 'payment' => 'cash'), $result); } } From e62fb0b48efb4c40d2aa7a071b8f550e7d83c209 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 15 Nov 2010 19:03:09 +0100 Subject: [PATCH 069/119] DDC-849 - Test verifies all different behaviors, clear contains, remove contains and clear count to be correct. --- .../ORM/Functional/Ticket/DDC849Test.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC849Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC849Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC849Test.php new file mode 100644 index 000000000..45ff132cd --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC849Test.php @@ -0,0 +1,84 @@ +useModelSet('cms'); + parent::setUp(); + + $this->user = new CmsUser(); + $this->user->username = "beberlei"; + $this->user->name = "Benjamin"; + $this->user->status = "active"; + + $this->group1 = new CmsGroup(); + $this->group1->name = "Group 1"; + $this->group2 = new CmsGroup(); + $this->group2->name = "Group 2"; + + $this->user->addGroup($this->group1); + $this->user->addGroup($this->group2); + + $this->_em->persist($this->user); + $this->_em->persist($this->group1); + $this->_em->persist($this->group2); + + $this->_em->flush(); + $this->_em->clear(); + + $this->user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->user->getId()); + } + + public function testRemoveContains() + { + $group1 = $this->user->groups[0]; + $group2 = $this->user->groups[1]; + + $this->assertTrue($this->user->groups->contains($group1)); + $this->assertTrue($this->user->groups->contains($group2)); + + $this->user->groups->removeElement($group1); + $this->user->groups->remove(1); + + $this->assertFalse($this->user->groups->contains($group1)); + $this->assertFalse($this->user->groups->contains($group2)); + } + + public function testClearCount() + { + $this->user->addGroup(new CmsGroup); + $this->assertEquals(3, count($this->user->groups)); + + $this->user->groups->clear(); + + $this->assertEquals(0, $this->user->groups->count()); + $this->assertEquals(0, count($this->user->groups)); + } + + public function testClearContains() + { + $group1 = $this->user->groups[0]; + $group2 = $this->user->groups[1]; + + $this->assertTrue($this->user->groups->contains($group1)); + $this->assertTrue($this->user->groups->contains($group2)); + + $this->user->groups->clear(); + + $this->assertFalse($this->user->groups->contains($group1)); + $this->assertFalse($this->user->groups->contains($group2)); + } +} \ No newline at end of file From 85a579febcc5e278848e0004f9ca2cd8cf1369a0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 15 Nov 2010 21:32:38 +0100 Subject: [PATCH 070/119] DDC-867 - Deep clone of the QueryBuilder nested expression objects --- lib/Doctrine/ORM/QueryBuilder.php | 20 +++++++++++++++++++ tests/Doctrine/Tests/ORM/QueryBuilderTest.php | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php index 27f65e14c..436522b69 100644 --- a/lib/Doctrine/ORM/QueryBuilder.php +++ b/lib/Doctrine/ORM/QueryBuilder.php @@ -932,4 +932,24 @@ class QueryBuilder { return $this->getDQL(); } + + /** + * Deep clone of all expression objects in the DQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->_dqlParts AS $part => $elements) { + if (is_array($this->_dqlParts[$part])) { + foreach ($this->_dqlParts[$part] AS $idx => $element) { + if (is_object($element)) { + $this->_dqlParts[$part][$idx] = clone $element; + } + } + } else if (\is_object($elements)) { + $this->_dqlParts[$part] = clone $elements; + } + } + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index 1ad039bb9..f18516fc4 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -569,4 +569,24 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase $this->assertNull($qb->getDQLPart('where')); $this->assertEquals(0, count($qb->getDQLPart('orderBy'))); } + + /** + * @group DDC-867 + */ + public function testDeepClone() + { + $qb = $this->_em->createQueryBuilder() + ->select('u') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->andWhere('u.username = ?1') + ->andWhere('u.status = ?2'); + + $expr = $qb->getDQLPart('where'); + $this->assertEquals(2, $expr->count(), "Modifying the second query should affect the first one."); + + $qb2 = clone $qb; + $qb2->andWhere('u.name = ?3'); + + $this->assertEquals(2, $expr->count(), "Modifying the second query should affect the first one."); + } } \ No newline at end of file From ae76b2ab8dbeade33fdbbcf6824f3b70802d9447 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 16 Nov 2010 21:31:54 +0100 Subject: [PATCH 071/119] DDC-853, DDC-629 - Fix drop schema always dropping everything at the cost of potential failures when dropping due to foreign keys. Added a full-database drop mode that resembles the old behavior. --- .../Command/SchemaTool/DropCommand.php | 24 +++++- lib/Doctrine/ORM/Tools/SchemaTool.php | 76 +++++++++++-------- .../ORM/Functional/SchemaTool/DDC214Test.php | 21 ++--- .../ORM/Functional/Ticket/DDC735Test.php | 3 +- .../ORM/Functional/Ticket/DDC809Test.php | 18 ++--- .../ORM/Functional/Ticket/DDC832Test.php | 5 ++ 6 files changed, 95 insertions(+), 52 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index df6167d64..f72cca1fe 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -60,6 +60,10 @@ class DropCommand extends AbstractCommand 'force', null, InputOption::PARAMETER_NONE, "Don't ask for the deletion of the database, but force the operation to run." ), + new InputOption( + 'full-database', null, InputOption::PARAMETER_NONE, + 'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.' + ), )) ->setHelp(<<getOption('full-database')); + if ($input->getOption('dump-sql') === true) { - $sqls = $schemaTool->getDropSchemaSql($metadatas); + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); } else if ($input->getOption('force') === true) { $output->write('Dropping database schema...' . PHP_EOL); - $schemaTool->dropSchema($metadatas); + if ($isFullDatabaseDrop) { + $schemaTool->dropDatabase(); + } else { + $schemaTool->dropSchema($metadatas); + } $output->write('Database schema dropped successfully!' . PHP_EOL); } else { $output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL); - $sqls = $schemaTool->getDropSchemaSql($metadatas); + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } if (count($sqls)) { $output->write('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.' . PHP_EOL); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index cee2fe294..0da4be1f3 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -509,12 +509,26 @@ class SchemaTool } /** - * Gets the SQL needed to drop the database schema for the given classes. + * Drops all elements in the database of the current connection. + * + * @return void + */ + public function dropDatabase() + { + $dropSchemaSql = $this->getDropDatabaseSQL(); + $conn = $this->_em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the SQL needed to drop the database schema for the connections database. * - * @param array $classes * @return array */ - public function getDropSchemaSql(array $classes) + public function getDropDatabaseSQL() { $sm = $this->_em->getConnection()->getSchemaManager(); $schema = $sm->createSchema(); @@ -526,39 +540,31 @@ class SchemaTool } /** - * Drop all tables of the database connection. * + * @param array $classes * @return array */ - private function _getDropSchemaTablesDatabaseMode($classes) + public function getDropSchemaSQL(array $classes) { - $conn = $this->_em->getConnection(); + $sm = $this->_em->getConnection()->getSchemaManager(); + + $sql = array(); + $orderedTables = array(); - $sm = $conn->getSchemaManager(); - /* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */ - - $allTables = $sm->listTables(); - - $orderedTables = $this->_getDropSchemaTablesMetadataMode($classes); - foreach($allTables AS $tableName) { - if(!in_array($tableName, $orderedTables)) { - $orderedTables[] = $tableName; + foreach ($classes AS $class) { + if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName && $this->_platform->supportsSequences()) { + $sql[] = $this->_platform->getDropSequenceSQL($class->sequenceGeneratorDefinition['sequenceName']); } } - return $orderedTables; - } - - private function _getDropSchemaTablesMetadataMode(array $classes) - { - $orderedTables = array(); - $commitOrder = $this->_getCommitOrder($classes); $associationTables = $this->_getAssociationTables($commitOrder); // Drop association tables first foreach ($associationTables as $associationTable) { - $orderedTables[] = $associationTable; + if (!in_array($associationTable, $orderedTables)) { + $orderedTables[] = $associationTable; + } } // Drop tables in reverse commit order @@ -570,17 +576,27 @@ class SchemaTool continue; } - $orderedTables[] = $class->getTableName(); + if (!in_array($class->getTableName(), $orderedTables)) { + $orderedTables[] = $class->getTableName(); + } } - //TODO: Drop other schema elements, like sequences etc. + $dropTablesSql = array(); + foreach ($orderedTables AS $tableName) { + /* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */ + $foreignKeys = $sm->listTableForeignKeys($tableName); + foreach ($foreignKeys AS $foreignKey) { + $sql[] = $this->_platform->getDropForeignKeySQL($foreignKey, $tableName); + } + $dropTablesSql[] = $this->_platform->getDropTableSQL($tableName); + } - return $orderedTables; + return array_merge($sql, $dropTablesSql); } /** * Updates the database schema of the given classes by comparing the ClassMetadata - * instances to the current database schema that is inspected. + * ins$tableNametances to the current database schema that is inspected. * * @param array $classes * @return void @@ -628,7 +644,7 @@ class SchemaTool $calc->addClass($class); foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide) { + if ($assoc['isOwningSide']) { $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); if ( ! $calc->hasClass($targetClass->name)) { @@ -650,8 +666,8 @@ class SchemaTool foreach ($classes as $class) { foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { - $associationTables[] = $assoc->joinTable['name']; + if ($assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $associationTables[] = $assoc['joinTable']['name']; } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php index a6682b6a8..e4e5e69a0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php @@ -12,6 +12,9 @@ require_once __DIR__ . '/../../../TestInit.php'; */ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase { + private $classes = array(); + private $schemaTool = null; + public function setUp() { parent::setUp(); @@ -20,6 +23,7 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase if (strpos($conn->getDriver()->getName(), "sqlite") !== false) { $this->markTestSkipped('SQLite does not support ALTER TABLE statements.'); } + $this->schemaTool = new Tools\SchemaTool($this->_em); } /** @@ -27,7 +31,7 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testCmsAddressModel() { - $classes = array( + $this->classes = array( 'Doctrine\Tests\Models\CMS\CmsUser', 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'Doctrine\Tests\Models\CMS\CmsAddress', @@ -35,7 +39,7 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase 'Doctrine\Tests\Models\CMS\CmsArticle' ); - $this->assertCreatedSchemaNeedsNoUpdates($classes); + $this->assertCreatedSchemaNeedsNoUpdates($this->classes); } /** @@ -43,7 +47,7 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testCompanyModel() { - $classes = array( + $this->classes = array( 'Doctrine\Tests\Models\Company\CompanyPerson', 'Doctrine\Tests\Models\Company\CompanyEmployee', 'Doctrine\Tests\Models\Company\CompanyManager', @@ -54,7 +58,7 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase 'Doctrine\Tests\Models\Company\CompanyCar' ); - $this->assertCreatedSchemaNeedsNoUpdates($classes); + $this->assertCreatedSchemaNeedsNoUpdates($this->classes); } public function assertCreatedSchemaNeedsNoUpdates($classes) @@ -64,19 +68,18 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase $classMetadata[] = $this->_em->getClassMetadata($class); } - $schemaTool = new Tools\SchemaTool($this->_em); - $schemaTool->dropSchema($classMetadata); - $schemaTool->createSchema($classMetadata); + $this->schemaTool->dropDatabase(); + $this->schemaTool->createSchema($classMetadata); $sm = $this->_em->getConnection()->getSchemaManager(); $fromSchema = $sm->createSchema(); - $toSchema = $schemaTool->getSchemaFromMetadata($classMetadata); + $toSchema = $this->schemaTool->getSchemaFromMetadata($classMetadata); $comparator = new \Doctrine\DBAL\Schema\Comparator(); $schemaDiff = $comparator->compare($fromSchema, $toSchema); $sql = $schemaDiff->toSql($this->_em->getConnection()->getDatabasePlatform()); - $this->assertEquals(0, count($sql)); + $this->assertEquals(0, count($sql), "SQL: " . implode(PHP_EOL, $sql)); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php index 76d3720c9..77ef6e148 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php @@ -35,6 +35,7 @@ class DDC735Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(1, count($product->getReviews())); // Remove the review + $reviewId = $review->getId(); $product->removeReview($review); $this->_em->flush(); @@ -48,7 +49,7 @@ class DDC735Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(0, count($product->getReviews()), 'count($reviews) should still be 0 after the refresh'); // Review should also not be available anymore - $this->assertNull($this->_em->find(__NAMESPACE__.'\DDC735Review', $review->getId())); + $this->assertNull($this->_em->find(__NAMESPACE__.'\DDC735Review', $reviewId)); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php index c0dcba90e..8ab65d8c6 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php @@ -29,15 +29,15 @@ class DDC809Test extends \Doctrine\Tests\OrmFunctionalTestCase $conn->insert('variant_test', array('variant_id' => 545208)); $conn->insert('variant_test', array('variant_id' => 545209)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94606)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94607)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94609)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545208, 'specification_value_id' => 94711)); + $conn->insert('var_spec_value_test', array('variant_id' => 545208, 'specification_value_id' => 94606)); + $conn->insert('var_spec_value_test', array('variant_id' => 545208, 'specification_value_id' => 94607)); + $conn->insert('var_spec_value_test', array('variant_id' => 545208, 'specification_value_id' => 94609)); + $conn->insert('var_spec_value_test', array('variant_id' => 545208, 'specification_value_id' => 94711)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94589)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94593)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94606)); - $conn->insert('variant_specification_value_test', array('variant_id' => 545209, 'specification_value_id' => 94607)); + $conn->insert('var_spec_value_test', array('variant_id' => 545209, 'specification_value_id' => 94589)); + $conn->insert('var_spec_value_test', array('variant_id' => 545209, 'specification_value_id' => 94593)); + $conn->insert('var_spec_value_test', array('variant_id' => 545209, 'specification_value_id' => 94606)); + $conn->insert('var_spec_value_test', array('variant_id' => 545209, 'specification_value_id' => 94607)); } /** @@ -72,7 +72,7 @@ class DDC809Variant /** * @ManyToMany(targetEntity="DDC809SpecificationValue", inversedBy="Variants") - * @JoinTable(name="variant_specification_value_test", + * @JoinTable(name="var_spec_value_test", * joinColumns={ * @JoinColumn(name="variant_id", referencedColumnName="variant_id") * }, diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php index c598a8194..5e112ffb7 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -13,6 +13,11 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase public function setUp() { parent::setUp(); + $platform = $this->_em->getConnection()->getDatabasePlatform(); + if ($platform->getName() == "oracle") { + $this->markTestSkipped('Doesnt run on Oracle.'); + } + try { $this->_schemaTool->createSchema(array( $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832JoinedIndex'), From c1661dd53e2f8d18651f646011b3076e822298c7 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 16 Nov 2010 21:53:46 +0100 Subject: [PATCH 072/119] DDC-511 - MappedSuperclasses specifications of inheritance mapping details make no sense and are ignored --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 5f1de3883..806d41e78 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -262,15 +262,19 @@ class ClassMetadataFactory $class = $this->newClassMetadataInstance($className); if ($parent) { - $class->setInheritanceType($parent->inheritanceType); - $class->setDiscriminatorColumn($parent->discriminatorColumn); + if (!$parent->isMappedSuperclass) { + $class->setInheritanceType($parent->inheritanceType); + $class->setDiscriminatorColumn($parent->discriminatorColumn); + } $class->setIdGeneratorType($parent->generatorType); $this->addInheritedFields($class, $parent); $this->addInheritedRelations($class, $parent); $class->setIdentifier($parent->identifier); $class->setVersioned($parent->isVersioned); $class->setVersionField($parent->versionField); - $class->setDiscriminatorMap($parent->discriminatorMap); + if (!$parent->isMappedSuperclass) { + $class->setDiscriminatorMap($parent->discriminatorMap); + } $class->setLifecycleCallbacks($parent->lifecycleCallbacks); $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); } From 494bfc89661c578a987f99db5ca42fc23c102a3f Mon Sep 17 00:00:00 2001 From: Juozas Kaziukenas Date: Wed, 9 Jun 2010 15:40:54 +0100 Subject: [PATCH 073/119] Fixed length, lower, upper and mod AST functions to use platform for generating SQL --- .../ORM/Query/AST/Functions/LengthFunction.php | 5 +++-- lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php | 5 +++-- lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php | 10 ++++------ lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php | 5 +++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php index fc779e4f7..36787786d 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php @@ -41,8 +41,9 @@ class LengthFunction extends FunctionNode */ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { - //TODO: Use platform to get SQL - return 'LENGTH(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')'; + return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); } /** diff --git a/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php index b5d33face..775f51d9a 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php @@ -41,8 +41,9 @@ class LowerFunction extends FunctionNode */ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { - //TODO: Use platform to get SQL - return 'LOWER(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')'; + return $sqlWalker->getConnection()->getDatabasePlatform()->getLowerExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); } /** diff --git a/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php index 7c52d60b8..4d124fe85 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -42,12 +42,10 @@ class ModFunction extends FunctionNode */ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { - //TODO: Use platform to get SQL - return 'MOD(' - . $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression) - . ', ' - . $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression) - . ')'; + return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression) + ); } /** diff --git a/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php index ff2b1e30b..acc8dd8eb 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php @@ -41,8 +41,9 @@ class UpperFunction extends FunctionNode */ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { - //TODO: Use platform to get SQL - return 'UPPER(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')'; + return $sqlWalker->getConnection()->getDatabasePlatform()->getUpperExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); } /** From b6da2e0e425c4481aac3bc332a0e0eced1d2bacf Mon Sep 17 00:00:00 2001 From: Juozas Kaziukenas Date: Tue, 16 Nov 2010 16:12:08 -0800 Subject: [PATCH 074/119] Proper FORM clause generation to support locking --- lib/Doctrine/ORM/Persisters/BasicEntityPersister.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index f249ac29a..2903ac96f 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -844,8 +844,8 @@ class BasicEntityPersister } return 'SELECT ' . $this->_getSelectColumnListSQL() - . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' - . $this->_getSQLTableAlias($this->_class->name) + . $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' + . $this->_getSQLTableAlias($this->_class->name), $lockMode) . $joinSql . ($conditionSql ? ' WHERE ' . $conditionSql : '') . $orderBySql @@ -1082,7 +1082,7 @@ class BasicEntityPersister } $sql = 'SELECT 1 ' - . $this->getLockTablesSql() + . $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode) . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; $params = array_values($criteria); $this->_conn->executeQuery($sql, $params); From 831b40e0938f77602934ed869f98644ee47faef1 Mon Sep 17 00:00:00 2001 From: Juozas Kaziukenas Date: Wed, 17 Nov 2010 13:18:18 -0800 Subject: [PATCH 075/119] Fixes required for Microsoft SQL tests --- tests/Doctrine/Tests/Models/DirectoryTree/File.php | 1 + tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php | 2 -- tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | 2 +- tests/Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/File.php b/tests/Doctrine/Tests/Models/DirectoryTree/File.php index 353177c81..57f505c33 100644 --- a/tests/Doctrine/Tests/Models/DirectoryTree/File.php +++ b/tests/Doctrine/Tests/Models/DirectoryTree/File.php @@ -22,6 +22,7 @@ namespace Doctrine\Tests\Models\DirectoryTree; /** * @Entity + * @Table(name="File_model") */ class File extends AbstractContentItem { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php index 8ab65d8c6..3b0734998 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC809Test.php @@ -66,7 +66,6 @@ class DDC809Variant /** * @Column(name="variant_id", type="integer") * @Id - * @GeneratedValue(strategy="AUTO") */ protected $variantId; @@ -98,7 +97,6 @@ class DDC809SpecificationValue /** * @Column(name="specification_value_id", type="integer") * @Id - * @GeneratedValue(strategy="AUTO") */ protected $specificationValueId; diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 642637116..b7b33542b 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -840,7 +840,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT f FROM Doctrine\Tests\Models\DirectoryTree\File f JOIN f.parentDirectory d WHERE f.id = ?1', - 'SELECT f0_.id AS id0, f0_.extension AS extension1, f0_.name AS name2 FROM File f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' + 'SELECT f0_.id AS id0, f0_.extension AS extension1, f0_.name AS name2 FROM File_model f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' ); } } diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index a115abc46..53706ed47 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -168,7 +168,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM navigation_countries'); } if (isset($this->_usedModelSets['directorytree'])) { - $conn->executeUpdate('DELETE FROM File'); + $conn->executeUpdate('DELETE FROM File_model'); // MySQL doesnt know deferred deletions therefore only executing the second query gives errors. $conn->executeUpdate('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL'); $conn->executeUpdate('DELETE FROM Directory'); From ec50125568c838652ebff873b8b3a91db40cce6b Mon Sep 17 00:00:00 2001 From: Juozas Kaziukenas Date: Wed, 17 Nov 2010 14:07:05 -0800 Subject: [PATCH 076/119] Fix for foreign keys and autoincrement --- lib/Doctrine/ORM/Tools/SchemaTool.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 0da4be1f3..0838f55bf 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -185,6 +185,7 @@ class SchemaTool $idMapping = $class->fieldMappings[$class->identifier[0]]; $this->_gatherColumn($class, $idMapping, $table); $columnName = $class->getQuotedColumnName($class->identifier[0], $this->_platform); + $table->getColumn($class->identifier[0])->setAutoincrement(false); $pkColumns[] = $columnName; // TODO: REMOVE From a2cbb8f72f98580264ca9984a327c04c3b1910c1 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 18 Nov 2010 20:45:44 +0100 Subject: [PATCH 077/119] DDC-882 - Bugfix with typehint --- lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php b/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php index 15ea0799a..f00520a20 100644 --- a/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php +++ b/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php @@ -4,7 +4,7 @@ namespace Doctrine\ORM\Event; use Doctrine\Common\EventArgs; -use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\EntityManager; /** @@ -26,17 +26,17 @@ class LoadClassMetadataEventArgs extends EventArgs private $em; /** - * @param ClassMetadata $classMetadata + * @param ClassMetadataInfo $classMetadata * @param EntityManager $em */ - public function __construct(ClassMetadata $classMetadata, EntityManager $em) + public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em) { $this->classMetadata = $classMetadata; $this->em = $em; } /** - * @return ClassMetadata + * @return ClassMetadataInfo */ public function getClassMetadata() { From 154176516ef1d09df52222ad29ef9d915ae498cb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 18 Nov 2010 23:05:34 +0100 Subject: [PATCH 078/119] Upgrade Dependencies to Common RC2 and DBAL RC3 --- lib/vendor/doctrine-common | 2 +- lib/vendor/doctrine-dbal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vendor/doctrine-common b/lib/vendor/doctrine-common index 0bd0fa68b..a46c6180f 160000 --- a/lib/vendor/doctrine-common +++ b/lib/vendor/doctrine-common @@ -1 +1 @@ -Subproject commit 0bd0fa68bbdc4d81c7742f2c972b5ed0c02b0640 +Subproject commit a46c6180f96647fdd66e2c8f2771d61ecebe6a3f diff --git a/lib/vendor/doctrine-dbal b/lib/vendor/doctrine-dbal index c49def311..dd708ce98 160000 --- a/lib/vendor/doctrine-dbal +++ b/lib/vendor/doctrine-dbal @@ -1 +1 @@ -Subproject commit c49def3112ffae7781e228677ae069454395110c +Subproject commit dd708ce98aa35018ec45b078c6c31f289b3f61a0 From 97b80d69f14a2bef60c4d80834ac8b1710e52d5b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 18 Nov 2010 23:07:32 +0100 Subject: [PATCH 079/119] Fix several test-issues after upgrading dependencies and a Bug in a SchemaTool and DatabaseDriver --- .../ORM/Mapping/Driver/DatabaseDriver.php | 2 +- lib/Doctrine/ORM/Tools/SchemaTool.php | 4 ++-- .../Tests/Models/DirectoryTree/File.php | 1 + .../SchemaTool/MySqlSchemaToolTest.php | 4 ++-- .../SchemaTool/PostgreSqlSchemaToolTest.php | 19 +++++++++++-------- .../ORM/Functional/Ticket/DDC440Test.php | 1 + .../ORM/Functional/Ticket/DDC832Test.php | 1 + .../ORM/Query/SelectSqlGenerationTest.php | 2 +- .../Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- 9 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index dd53b0d3a..c6c4547b6 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -89,7 +89,7 @@ class DatabaseDriver implements Driver foreach ($foreignKeys AS $foreignKey) { $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); } - + $pkColumns = $table->getPrimaryKey()->getColumns(); sort($pkColumns); sort($allForeignKeyColumns); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 0da4be1f3..540745d8a 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -194,7 +194,7 @@ class SchemaTool // Add a FK constraint on the ID column $table->addUnnamedForeignKeyConstraint( - $this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform), + $this->_em->getClassMetadata($class->rootEntityName)->getTableName(), array($columnName), array($columnName), array('onDelete' => 'CASCADE') ); } @@ -485,7 +485,7 @@ class SchemaTool } $theJoinTable->addUnnamedForeignKeyConstraint( - $class->getQuotedTableName($this->_platform), $localColumns, $foreignColumns, $fkOptions + $class->getTableName(), $localColumns, $foreignColumns, $fkOptions ); } diff --git a/tests/Doctrine/Tests/Models/DirectoryTree/File.php b/tests/Doctrine/Tests/Models/DirectoryTree/File.php index 353177c81..c43f2c82f 100644 --- a/tests/Doctrine/Tests/Models/DirectoryTree/File.php +++ b/tests/Doctrine/Tests/Models/DirectoryTree/File.php @@ -22,6 +22,7 @@ namespace Doctrine\Tests\Models\DirectoryTree; /** * @Entity + * @Table(name="`file`") */ class File extends AbstractContentItem { diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php index e3117b48e..22f6eadf3 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php @@ -28,8 +28,8 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $sql = $tool->getCreateSchemaSql($classes); $this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX cms_addresses_user_id_uniq (user_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]); $this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX cms_users_username_uniq (username), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]); - $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]); - $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]); + $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX cms_users_groups_user_id_idx (user_id), INDEX cms_users_groups_group_id_idx (group_id), PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]); + $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX cms_phonenumbers_user_id_idx (user_id), PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]); $this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[4]); $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]); $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]); diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index 6a691a1d6..d0b688708 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -38,15 +38,18 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[2]); $this->assertEquals("CREATE UNIQUE INDEX cms_users_username_uniq ON cms_users (username)", $sql[3]); $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[4]); - $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[5]); - $this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[6]); - $this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[7]); - $this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]); - $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[9]); - $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[10]); - $this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]); + $this->assertEquals("CREATE INDEX cms_users_groups_user_id_idx ON cms_users_groups (user_id)", $sql[5]); + $this->assertEquals("CREATE INDEX cms_users_groups_group_id_idx ON cms_users_groups (group_id)", $sql[6]); + $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[7]); + $this->assertEquals("CREATE INDEX cms_phonenumbers_user_id_idx ON cms_phonenumbers (user_id)", $sql[8]); + $this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[9]); + $this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[10]); + $this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]); + $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]); + $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]); + $this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]); - $this->assertEquals(count($sql), 12); + $this->assertEquals(count($sql), 15); } public function testGetCreateSchemaSql2() diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC440Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC440Test.php index 35ff90421..a11c62407 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC440Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC440Test.php @@ -159,6 +159,7 @@ class DDC440Client protected $main_phone; /** * @OneToMany(targetEntity="DDC440Phone", mappedBy="client", cascade={"persist", "remove"}, fetch="EAGER") + * @orderBy({"number"="ASC"}) */ protected $phones; /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php index 5e112ffb7..1d9de5334 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -18,6 +18,7 @@ class DDC832Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->markTestSkipped('Doesnt run on Oracle.'); } + $this->_em->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger()); try { $this->_schemaTool->createSchema(array( $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC832JoinedIndex'), diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 642637116..55674a213 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -840,7 +840,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT f FROM Doctrine\Tests\Models\DirectoryTree\File f JOIN f.parentDirectory d WHERE f.id = ?1', - 'SELECT f0_.id AS id0, f0_.extension AS extension1, f0_.name AS name2 FROM File f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' + 'SELECT f0_.id AS id0, f0_.extension AS extension1, f0_.name AS name2 FROM "file" f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' ); } } diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index a115abc46..f7674ed4e 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -168,7 +168,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM navigation_countries'); } if (isset($this->_usedModelSets['directorytree'])) { - $conn->executeUpdate('DELETE FROM File'); + $conn->executeUpdate('DELETE FROM ' . $this->_em->getConnection()->getDatabasePlatform()->quoteIdentifier("file")); // MySQL doesnt know deferred deletions therefore only executing the second query gives errors. $conn->executeUpdate('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL'); $conn->executeUpdate('DELETE FROM Directory'); From e4f74d82908bdc4e8313f3725b2f0f66c5cea6ad Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Nov 2010 17:38:12 +0100 Subject: [PATCH 080/119] DDC-886 - Bugfix for composite identifier flag not being mapped to child classes in inheritence/mapped superclass scenarios. --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 1 + .../Tests/ORM/Mapping/ClassMetadataTest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 3b3d5eee4..2d6ec7c20 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -940,6 +940,7 @@ class ClassMetadataInfo public function setIdentifier(array $identifier) { $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 34829fcae..3264519e8 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -276,4 +276,17 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('cmsaddress_id', $cm->associationMappings['user']['joinTable']['joinColumns'][0]['name']); $this->assertEquals('cmsuser_id', $cm->associationMappings['user']['joinTable']['inverseJoinColumns'][0]['name']); } + + /** + * @group DDC-886 + */ + public function testSetMultipleIdentifierSetsComposite() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->mapField(array('fieldName' => 'name')); + $cm->mapField(array('fieldName' => 'username')); + + $cm->setIdentifier(array('name', 'username')); + $this->assertTrue($cm->isIdentifierComposite); + } } \ No newline at end of file From 51922a1ff03796e934cfe9a40b2282fae48172d4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Nov 2010 19:57:37 +0100 Subject: [PATCH 081/119] DDC-855 - Fix EntityGenerator annoyance when run successive times. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index f2fa583c8..b0c83203d 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -218,7 +218,7 @@ public function () $body = str_replace('', $this->_spaces, $body); $last = strrpos($currentCode, '}'); - return substr($currentCode, 0, $last) . $body . "\n}"; + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}"; } /** From 386b7e26d61dd23bb68aadc6d020a039f72395c9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Nov 2010 20:05:36 +0100 Subject: [PATCH 082/119] DDC-888 - Fix Warning in AnnotationsDriver when using @JoinTable without explicitly defining join- and inverse join-columns. --- lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index fb7871c74..435676566 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -114,8 +114,8 @@ final class Index extends Annotation { final class JoinTable extends Annotation { public $name; public $schema; - public $joinColumns; - public $inverseJoinColumns; + public $joinColumns = array(); + public $inverseJoinColumns = array(); } final class SequenceGenerator extends Annotation { public $sequenceName; From 7196999b69f10ec56804aa95abd9ed9dff0affb0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Nov 2010 20:32:14 +0100 Subject: [PATCH 083/119] DDC-895 - Fix Generated XML from XMLExporter and remove silly formatting xml code used on SimpleXML by just stuffing it into DOMDocument with formatOutput=true. --- .../ORM/Tools/Export/Driver/XmlExporter.php | 80 ++++++------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php index 75c0ca3e8..fc27ea911 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -46,11 +46,14 @@ class XmlExporter extends AbstractExporter */ public function exportClassMetadata(ClassMetadataInfo $metadata) { - $xml = new \SimpleXmlElement(""); + $xml = new \SimpleXmlElement(""); - $xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); + /*$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); $xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); - $xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd'); + $xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');*/ if ($metadata->isMappedSuperclass) { $root = $xml->addChild('mapped-superclass'); @@ -128,6 +131,21 @@ class XmlExporter extends AbstractExporter $id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; } + if ($id) { + foreach ($id as $field) { + $idXml = $root->addChild('id'); + $idXml->addAttribute('name', $field['fieldName']); + $idXml->addAttribute('type', $field['type']); + if (isset($field['columnName'])) { + $idXml->addAttribute('column', $field['columnName']); + } + if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $generatorXml = $idXml->addChild('generator'); + $generatorXml->addAttribute('strategy', $idGeneratorType); + } + } + } + if ($fields) { foreach ($fields as $field) { $fieldXml = $root->addChild('field'); @@ -163,21 +181,6 @@ class XmlExporter extends AbstractExporter } } - if ($id) { - foreach ($id as $field) { - $idXml = $root->addChild('id'); - $idXml->addAttribute('name', $field['fieldName']); - $idXml->addAttribute('type', $field['type']); - if (isset($field['columnName'])) { - $idXml->addAttribute('column', $field['columnName']); - } - if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { - $generatorXml = $idXml->addChild('generator'); - $generatorXml->addAttribute('strategy', $idGeneratorType); - } - } - } - foreach ($metadata->associationMappings as $name => $associationMapping) { if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { $associationMappingXml = $root->addChild('one-to-one'); @@ -305,47 +308,16 @@ class XmlExporter extends AbstractExporter } /** - * Code originally taken from - * http://recurser.com/articles/2007/04/05/format-xml-with-php/ - * - * @param string $simpleXml + * @param \SimpleXMLElement $simpleXml * @return string $xml */ private function _asXml($simpleXml) { - $xml = $simpleXml->asXml(); + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadXML($simpleXml->asXML()); + $dom->formatOutput = true; - // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries) - $xml = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $xml); - - // now indent the tags - $token = strtok($xml, "\n"); - $result = ''; // holds formatted version as it is built - $pad = 0; // initial indent - $matches = array(); // returns from preg_matches() - - // test for the various tag states - while ($token !== false) { - // 1. open and closing tags on same line - no change - if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) { - $indent = 0; - // 2. closing tag - outdent now - } else if (preg_match('/^<\/\w/', $token, $matches)) { - $pad = $pad - 4; - // 3. opening tag - don't pad this one, only subsequent tags - } elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches)) { - $indent = 4; - // 4. no indentation needed - } else { - $indent = 0; - } - - // pad the line with the required number of leading spaces - $line = str_pad($token, strlen($token)+$pad, ' ', STR_PAD_LEFT); - $result .= $line . "\n"; // add to the cumulative result, with linefeed - $token = strtok("\n"); // get the next token - $pad += $indent; // update the pad size for subsequent lines - } + $result = $dom->saveXML(); return $result; } } \ No newline at end of file From dff5dae416b09494094612782eab505e846e1037 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Nov 2010 20:53:26 +0100 Subject: [PATCH 084/119] DDC-897 - Make ClassMetadataFactory configurable. --- lib/Doctrine/ORM/Configuration.php | 21 +++++++++++++++++++ lib/Doctrine/ORM/EntityManager.php | 6 +++++- .../ORM/Mapping/ClassMetadataFactory.php | 6 ++---- .../Command/GenerateEntitiesCommand.php | 3 ++- .../ORM/Mapping/AnnotationDriverTest.php | 3 ++- .../Mapping/BasicInheritanceMappingTest.php | 3 ++- .../ORM/Mapping/ClassMetadataFactoryTest.php | 3 ++- .../ORM/Mapping/YamlMappingDriverTest.php | 3 ++- .../ORM/Tools/ConvertDoctrine1SchemaTest.php | 3 ++- .../AbstractClassMetadataExporterTest.php | 6 ++++-- 10 files changed, 44 insertions(+), 13 deletions(-) diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 5e5341d1d..0bc206d27 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -462,4 +462,25 @@ class Configuration extends \Doctrine\DBAL\Configuration { $this->_attributes['customHydrationModes'][$modeName] = $hydrator; } + + /** + * Set a class metadata factory. + * + * @param string $cmf + */ + public function setClassMetadataFactoryName($cmfName) + { + $this->_attributes['classMetadataFactoryName'] = $cmfName; + } + + /** + * @return string + */ + public function getClassMetadataFactoryName() + { + if (!isset($this->_attributes['classMetadataFactoryName'])) { + $this->_attributes['classMetadataFactoryName'] = 'Doctrine\ORM\Mapping\ClassMetadataFactory'; + } + return $this->_attributes['classMetadataFactoryName']; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index abd89b0d4..0ab1541ba 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -118,8 +118,12 @@ class EntityManager $this->conn = $conn; $this->config = $config; $this->eventManager = $eventManager; - $this->metadataFactory = new ClassMetadataFactory($this); + + $metadataFactoryClassName = $config->getClassMetadataFactoryName(); + $this->metadataFactory = new $metadataFactoryClassName; + $this->metadataFactory->setEntityManager($this); $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); + $this->unitOfWork = new UnitOfWork($this); $this->proxyFactory = new ProxyFactory($this, $config->getProxyDir(), diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 806d41e78..174e95eaf 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -74,11 +74,9 @@ class ClassMetadataFactory private $initialized = false; /** - * Creates a new factory instance that uses the given metadata driver implementation. - * - * @param $driver The metadata driver to use. + * @param EntityManager $$em */ - public function __construct(EntityManager $em) + public function setEntityManager(EntityManager $em) { $this->em = $em; } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php index 08654ba18..3014d57ae 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -96,7 +96,8 @@ EOT { $em = $this->getHelper('em')->getEntityManager(); - $cmf = new DisconnectedClassMetadataFactory($em); + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); $metadatas = $cmf->getAllMetadata(); $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php index b34d727a7..c2f3a13c4 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php @@ -122,7 +122,8 @@ class AnnotationDriverTest extends AbstractMappingDriverTest $em = $this->_getTestEntityManager(); $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory($em); + $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory(); + $factory->setEntityManager($em); $classPage = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\File'); $classPage = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\File'); diff --git a/tests/Doctrine/Tests/ORM/Mapping/BasicInheritanceMappingTest.php b/tests/Doctrine/Tests/ORM/Mapping/BasicInheritanceMappingTest.php index fdaf30f41..671028852 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/BasicInheritanceMappingTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/BasicInheritanceMappingTest.php @@ -11,7 +11,8 @@ class BasicInheritanceMappingTest extends \Doctrine\Tests\OrmTestCase private $_factory; protected function setUp() { - $this->_factory = new ClassMetadataFactory($this->_getTestEntityManager()); + $this->_factory = new ClassMetadataFactory(); + $this->_factory->setEntityManager($this->_getTestEntityManager()); } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index cbd9ce5e5..72a49a701 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -42,7 +42,8 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); // SUT - $cmf = new ClassMetadataFactoryTestSubject($entityManager); + $cmf = new ClassMetadataFactoryTestSubject(); + $cmf->setEntityManager($entityManager); $cmf->setMetadataForClass('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm1); // Prechecks diff --git a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php index 4e58485f1..34c3fa810 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php @@ -31,7 +31,8 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest $em = $this->_getTestEntityManager(); $em->getConfiguration()->setMetadataDriverImpl($yamlDriver); - $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory($em); + $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory(); + $factory->setEntityManager($em); $classPage = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\File'); $classPage = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\File'); diff --git a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php index 6f542dba9..b944c26d9 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php @@ -77,7 +77,8 @@ class ConvertDoctrine1SchemaTest extends \Doctrine\Tests\OrmTestCase $metadataDriver = new \Doctrine\ORM\Mapping\Driver\YamlDriver(__DIR__ . '/convert'); $em = $this->_createEntityManager($metadataDriver); - $cmf = new DisconnectedClassMetadataFactory($em); + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); $metadata = $cmf->getAllMetadata(); $profileClass = $metadata[0]; $userClass = $metadata[1]; diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php index 95759c539..4a85918d6 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php @@ -88,10 +88,12 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest protected function _createClassMetadataFactory($em, $type) { if ($type === 'annotation') { - return new ClassMetadataFactory($em); + $factory = new ClassMetadataFactory(); } else { - return new DisconnectedClassMetadataFactory($em); + $factory = new DisconnectedClassMetadataFactory(); } + $factory->setEntityManager($em); + return $factory; } public function testExportDirectoryAndFilesAreCreated() From 796b62cd2cee2bbee596db82ded711bc6c35dd6b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Nov 2010 22:17:55 +0100 Subject: [PATCH 085/119] DDC-897 - Fix DisconnecetdClassMetadataFactory with regards to namespace setting, now inferred from the FQCN. --- .../ORM/Tools/DisconnectedClassMetadataFactory.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php b/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php index 436c04b4f..55503d400 100644 --- a/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php @@ -1,7 +1,5 @@ * @author Guilherme Blanco * @author Jonathan Wage @@ -46,7 +43,13 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory */ protected function newClassMetadataInstance($className) { - return new ClassMetadataInfo($className); + $metadata = new ClassMetadataInfo($className); + if (strpos($className, "\\") !== false) { + $metadata->namespace = strrev(substr( strrev($className), strpos(strrev($className), "\\")+1 )); + } else { + $metadata->namespace = ""; + } + return $metadata; } /** From 892eec2f26a27161d940c479ec6fd13396f052f8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 28 Nov 2010 16:59:23 +0800 Subject: [PATCH 086/119] Update Symfony\Component\Console to latest version --- .../Command/ClearCache/ResultCommand.php | 8 +-- .../Command/ConvertDoctrine1SchemaCommand.php | 6 +-- .../Console/Command/ConvertMappingCommand.php | 6 +-- .../EnsureProductionSettingsCommand.php | 2 +- .../Command/GenerateEntitiesCommand.php | 14 ++--- .../Command/GenerateProxiesCommand.php | 2 +- .../Command/GenerateRepositoriesCommand.php | 2 +- .../Tools/Console/Command/RunDqlCommand.php | 8 +-- .../Command/SchemaTool/CreateCommand.php | 2 +- .../Command/SchemaTool/DropCommand.php | 6 +-- .../Command/SchemaTool/UpdateCommand.php | 6 +-- .../Symfony/Component/Console/Application.php | 46 ++++++++-------- .../Component/Console/Command/Command.php | 4 +- .../Component/Console/Command/HelpCommand.php | 4 +- .../Component/Console/Command/ListCommand.php | 2 +- .../Component/Console/Input/ArgvInput.php | 10 ++-- .../Component/Console/Input/ArrayInput.php | 4 +- .../Console/Input/InputDefinition.php | 12 ++--- .../Component/Console/Input/InputOption.php | 52 +++++++++---------- .../Symfony/Component/Console/Shell.php | 2 +- 20 files changed, 99 insertions(+), 99 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php index d994d708f..f1506809b 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -49,19 +49,19 @@ class ResultCommand extends Console\Command\Command ->setDescription('Clear result cache of the various cache drivers.') ->setDefinition(array( new InputOption( - 'id', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'id', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'ID(s) of the cache entry to delete (accepts * wildcards).', array() ), new InputOption( - 'regex', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'regex', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Delete cache entries that match the given regular expression(s).', array() ), new InputOption( - 'prefix', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'prefix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Delete cache entries that have the given prefix(es).', array() ), new InputOption( - 'suffix', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'suffix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Delete cache entries that have the given suffix(es).', array() ), )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php index a518b76e8..1a1328aac 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -112,16 +112,16 @@ class ConvertDoctrine1SchemaCommand extends Console\Command\Command 'The path to generate your Doctrine 2.X mapping information.' ), new InputOption( - 'from', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Optional paths of Doctrine 1.X schema information.', array() ), new InputOption( - 'extend', null, InputOption::PARAMETER_OPTIONAL, + 'extend', null, InputOption::VALUE_OPTIONAL, 'Defines a base class to be extended by generated entity classes.' ), new InputOption( - 'num-spaces', null, InputOption::PARAMETER_OPTIONAL, + 'num-spaces', null, InputOption::VALUE_OPTIONAL, 'Defines the number of indentation spaces', 4 ) )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index ad3134779..75fea1f89 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -53,7 +53,7 @@ class ConvertMappingCommand extends Console\Command\Command ->setDescription('Convert mapping information between supported formats.') ->setDefinition(array( new InputOption( - 'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.' ), new InputArgument( @@ -67,11 +67,11 @@ class ConvertMappingCommand extends Console\Command\Command 'from-database', null, null, 'Whether or not to convert mapping information from existing database.' ), new InputOption( - 'extend', null, InputOption::PARAMETER_OPTIONAL, + 'extend', null, InputOption::VALUE_OPTIONAL, 'Defines a base class to be extended by generated entity classes.' ), new InputOption( - 'num-spaces', null, InputOption::PARAMETER_OPTIONAL, + 'num-spaces', null, InputOption::VALUE_OPTIONAL, 'Defines the number of indentation spaces', 4 ) )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php index 4fdd8fad7..d29bfce0b 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -49,7 +49,7 @@ class EnsureProductionSettingsCommand extends Console\Command\Command ->setDescription('Verify that Doctrine is properly configured for a production environment.') ->setDefinition(array( new InputOption( - 'complete', null, InputOption::PARAMETER_NONE, + 'complete', null, InputOption::VALUE_NONE, 'Flag to also inspect database connection existance.' ) )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php index 3014d57ae..f69b5167f 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -52,34 +52,34 @@ class GenerateEntitiesCommand extends Console\Command\Command ->setDescription('Generate entity classes and method stubs from your mapping information.') ->setDefinition(array( new InputOption( - 'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.' ), new InputArgument( 'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.' ), new InputOption( - 'generate-annotations', null, InputOption::PARAMETER_OPTIONAL, + 'generate-annotations', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should generate annotation metadata on entities.', false ), new InputOption( - 'generate-methods', null, InputOption::PARAMETER_OPTIONAL, + 'generate-methods', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should generate stub methods on entities.', true ), new InputOption( - 'regenerate-entities', null, InputOption::PARAMETER_OPTIONAL, + 'regenerate-entities', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should regenerate entity if it exists.', false ), new InputOption( - 'update-entities', null, InputOption::PARAMETER_OPTIONAL, + 'update-entities', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should only update entity if it exists.', true ), new InputOption( - 'extend', null, InputOption::PARAMETER_OPTIONAL, + 'extend', null, InputOption::VALUE_OPTIONAL, 'Defines a base class to be extended by generated entity classes.' ), new InputOption( - 'num-spaces', null, InputOption::PARAMETER_OPTIONAL, + 'num-spaces', null, InputOption::VALUE_OPTIONAL, 'Defines the number of indentation spaces', 4 ) )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php index 09376d867..987628956 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -50,7 +50,7 @@ class GenerateProxiesCommand extends Console\Command\Command ->setDescription('Generates proxy classes for entity classes.') ->setDefinition(array( new InputOption( - 'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.' ), new InputArgument( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php index a70aa39d0..30d36eb44 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -51,7 +51,7 @@ class GenerateRepositoriesCommand extends Console\Command\Command ->setDescription('Generate repository classes from your mapping information.') ->setDefinition(array( new InputOption( - 'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY, + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.' ), new InputArgument( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php index 7619534f7..c794cb996 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -50,20 +50,20 @@ class RunDqlCommand extends Console\Command\Command ->setDefinition(array( new InputArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.'), new InputOption( - 'hydrate', null, InputOption::PARAMETER_REQUIRED, + 'hydrate', null, InputOption::VALUE_REQUIRED, 'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.', 'object' ), new InputOption( - 'first-result', null, InputOption::PARAMETER_REQUIRED, + 'first-result', null, InputOption::VALUE_REQUIRED, 'The first result in the result set.' ), new InputOption( - 'max-result', null, InputOption::PARAMETER_REQUIRED, + 'max-result', null, InputOption::VALUE_REQUIRED, 'The maximum number of results in the result set.' ), new InputOption( - 'depth', null, InputOption::PARAMETER_REQUIRED, + 'depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of Entity graph.', 7 ) )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php index a772381b3..e18a9c56a 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -53,7 +53,7 @@ class CreateCommand extends AbstractCommand ) ->setDefinition(array( new InputOption( - 'dump-sql', null, InputOption::PARAMETER_NONE, + 'dump-sql', null, InputOption::VALUE_NONE, 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' ) )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index f72cca1fe..82d91f4c6 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -53,15 +53,15 @@ class DropCommand extends AbstractCommand ) ->setDefinition(array( new InputOption( - 'dump-sql', null, InputOption::PARAMETER_NONE, + 'dump-sql', null, InputOption::VALUE_NONE, 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' ), new InputOption( - 'force', null, InputOption::PARAMETER_NONE, + 'force', null, InputOption::VALUE_NONE, "Don't ask for the deletion of the database, but force the operation to run." ), new InputOption( - 'full-database', null, InputOption::PARAMETER_NONE, + 'full-database', null, InputOption::VALUE_NONE, 'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.' ), )) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index f553a8c4b..f1a3a73cf 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -53,15 +53,15 @@ class UpdateCommand extends AbstractCommand ) ->setDefinition(array( new InputOption( - 'complete', null, InputOption::PARAMETER_NONE, + 'complete', null, InputOption::VALUE_NONE, 'If defined, all assets of the database which are not relevant to the current metadata will be dropped.' ), new InputOption( - 'dump-sql', null, InputOption::PARAMETER_NONE, + 'dump-sql', null, InputOption::VALUE_NONE, 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' ), new InputOption( - 'force', null, InputOption::PARAMETER_NONE, + 'force', null, InputOption::VALUE_NONE, "Don't ask for the incremental update of the database, but force the operation to run." ), )) diff --git a/lib/vendor/Symfony/Component/Console/Application.php b/lib/vendor/Symfony/Component/Console/Application.php index 850dbee20..7db8f1a86 100644 --- a/lib/vendor/Symfony/Component/Console/Application.php +++ b/lib/vendor/Symfony/Component/Console/Application.php @@ -37,7 +37,7 @@ use Symfony\Component\Console\Helper\DialogHelper; * Usage: * * $app = new Application('myapp', '1.0 (stable)'); - * $app->addCommand(new SimpleCommand()); + * $app->add(new SimpleCommand()); * $app->run(); * * @author Fabien Potencier @@ -74,18 +74,18 @@ class Application new DialogHelper(), )); - $this->addCommand(new HelpCommand()); - $this->addCommand(new ListCommand()); + $this->add(new HelpCommand()); + $this->add(new ListCommand()); $this->definition = new InputDefinition(array( new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), - new InputOption('--help', '-h', InputOption::PARAMETER_NONE, 'Display this help message.'), - new InputOption('--quiet', '-q', InputOption::PARAMETER_NONE, 'Do not output any message.'), - new InputOption('--verbose', '-v', InputOption::PARAMETER_NONE, 'Increase verbosity of messages.'), - new InputOption('--version', '-V', InputOption::PARAMETER_NONE, 'Display this program version.'), - new InputOption('--ansi', '-a', InputOption::PARAMETER_NONE, 'Force ANSI output.'), - new InputOption('--no-interaction', '-n', InputOption::PARAMETER_NONE, 'Do not ask any interactive question.'), + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'), + new InputOption('--verbose', '-v', InputOption::VALUE_NONE, 'Increase verbosity of messages.'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this program version.'), + new InputOption('--ansi', '-a', InputOption::VALUE_NONE, 'Force ANSI output.'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'), )); } @@ -181,7 +181,7 @@ class Application } // the command name MUST be the first element of the input - $command = $this->findCommand($name); + $command = $this->find($name); $this->runningCommand = $command; $statusCode = $command->run($input, $output); @@ -329,7 +329,7 @@ class Application */ public function register($name) { - return $this->addCommand(new Command($name)); + return $this->add(new Command($name)); } /** @@ -340,7 +340,7 @@ class Application public function addCommands(array $commands) { foreach ($commands as $command) { - $this->addCommand($command); + $this->add($command); } } @@ -353,7 +353,7 @@ class Application * * @return Command The registered command */ - public function addCommand(Command $command) + public function add(Command $command) { $command->setApplication($this); @@ -375,7 +375,7 @@ class Application * * @throws \InvalidArgumentException When command name given does not exist */ - public function getCommand($name) + public function get($name) { if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); @@ -386,7 +386,7 @@ class Application if ($this->wantHelps) { $this->wantHelps = false; - $helpCommand = $this->getCommand('help'); + $helpCommand = $this->get('help'); $helpCommand->setCommand($command); return $helpCommand; @@ -402,7 +402,7 @@ class Application * * @return Boolean true if the command exists, false otherwise */ - public function hasCommand($name) + public function has($name) { return isset($this->commands[$name]) || isset($this->aliases[$name]); } @@ -451,7 +451,7 @@ class Application /** * Finds a command by name or alias. * - * Contrary to getCommand, this command tries to find the best + * Contrary to get, this command tries to find the best * match if you give it an abbreviation of a name or alias. * * @param string $name A command name or a command alias @@ -460,7 +460,7 @@ class Application * * @throws \InvalidArgumentException When command name is incorrect or ambiguous */ - public function findCommand($name) + public function find($name) { // namespace $namespace = ''; @@ -481,7 +481,7 @@ class Application $abbrevs = static::getAbbreviations($commands); if (isset($abbrevs[$name]) && 1 == count($abbrevs[$name])) { - return $this->getCommand($namespace ? $namespace.':'.$abbrevs[$name][0] : $abbrevs[$name][0]); + return $this->get($namespace ? $namespace.':'.$abbrevs[$name][0] : $abbrevs[$name][0]); } if (isset($abbrevs[$name]) && count($abbrevs[$name]) > 1) { @@ -500,7 +500,7 @@ class Application throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $fullName, $this->getAbbreviationSuggestions($abbrevs[$fullName]))); } - return $this->getCommand($abbrevs[$fullName][0]); + return $this->get($abbrevs[$fullName][0]); } /** @@ -512,7 +512,7 @@ class Application * * @return array An array of Command instances */ - public function getCommands($namespace = null) + public function all($namespace = null) { if (null === $namespace) { return $this->commands; @@ -566,7 +566,7 @@ class Application */ public function asText($namespace = null) { - $commands = $namespace ? $this->getCommands($this->findNamespace($namespace)) : $this->commands; + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; $messages = array($this->getHelp(), ''); if ($namespace) { @@ -607,7 +607,7 @@ class Application */ public function asXml($namespace = null, $asDom = false) { - $commands = $namespace ? $this->getCommands($this->findNamespace($namespace)) : $this->commands; + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; diff --git a/lib/vendor/Symfony/Component/Console/Command/Command.php b/lib/vendor/Symfony/Component/Console/Command/Command.php index 5f04baa54..3777d9617 100644 --- a/lib/vendor/Symfony/Component/Console/Command/Command.php +++ b/lib/vendor/Symfony/Component/Console/Command/Command.php @@ -236,9 +236,9 @@ class Command * * @param string $name The option name * @param string $shortcut The shortcut (can be null) - * @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL + * @param integer $mode The option mode: One of the InputOption::VALUE_* constants * @param string $description A description text - * @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE) + * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or self::VALUE_NONE) * * @return Command The current instance */ diff --git a/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php b/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php index 5e0c30739..5504832d8 100644 --- a/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php +++ b/lib/vendor/Symfony/Component/Console/Command/HelpCommand.php @@ -37,7 +37,7 @@ class HelpCommand extends Command $this ->setDefinition(array( new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), - new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), )) ->setName('help') ->setAliases(array('?')) @@ -65,7 +65,7 @@ EOF protected function execute(InputInterface $input, OutputInterface $output) { if (null === $this->command) { - $this->command = $this->application->getCommand($input->getArgument('command_name')); + $this->command = $this->application->get($input->getArgument('command_name')); } if ($input->getOption('xml')) { diff --git a/lib/vendor/Symfony/Component/Console/Command/ListCommand.php b/lib/vendor/Symfony/Component/Console/Command/ListCommand.php index f180e4124..a1f77e97c 100644 --- a/lib/vendor/Symfony/Component/Console/Command/ListCommand.php +++ b/lib/vendor/Symfony/Component/Console/Command/ListCommand.php @@ -33,7 +33,7 @@ class ListCommand extends Command $this ->setDefinition(array( new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), - new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), )) ->setName('list') ->setDescription('Lists commands') diff --git a/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php b/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php index beb6fc436..a1ca7a26a 100644 --- a/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php +++ b/lib/vendor/Symfony/Component/Console/Input/ArgvInput.php @@ -88,7 +88,7 @@ class ArgvInput extends Input $name = substr($token, 1); if (strlen($name) > 1) { - if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptParameter()) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { // an option with a value (with no space) $this->addShortOption($name[0], substr($name, 1)); } else { @@ -115,7 +115,7 @@ class ArgvInput extends Input } $option = $this->definition->getOptionForShortcut($name[$i]); - if ($option->acceptParameter()) { + if ($option->acceptValue()) { $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); break; @@ -190,7 +190,7 @@ class ArgvInput extends Input $option = $this->definition->getOption($name); - if (null === $value && $option->acceptParameter()) { + if (null === $value && $option->acceptValue()) { // if option accepts an optional or mandatory argument // let's see if there is one provided $next = array_shift($this->parsed); @@ -202,11 +202,11 @@ class ArgvInput extends Input } if (null === $value) { - if ($option->isParameterRequired()) { + if ($option->isValueRequired()) { throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); } - $value = $option->isParameterOptional() ? $option->getDefault() : true; + $value = $option->isValueOptional() ? $option->getDefault() : true; } $this->options[$name] = $value; diff --git a/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php b/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php index 786d3ad71..865e48205 100644 --- a/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php +++ b/lib/vendor/Symfony/Component/Console/Input/ArrayInput.php @@ -133,11 +133,11 @@ class ArrayInput extends Input $option = $this->definition->getOption($name); if (null === $value) { - if ($option->isParameterRequired()) { + if ($option->isValueRequired()) { throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name)); } - $value = $option->isParameterOptional() ? $option->getDefault() : true; + $value = $option->isValueOptional() ? $option->getDefault() : true; } $this->options[$name] = $value; diff --git a/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php b/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php index cc737440d..bc2e8bcc7 100644 --- a/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php +++ b/lib/vendor/Symfony/Component/Console/Input/InputDefinition.php @@ -18,7 +18,7 @@ namespace Symfony\Component\Console\Input; * * $definition = new InputDefinition(array( * new InputArgument('name', InputArgument::REQUIRED), - * new InputOption('foo', 'f', InputOption::PARAMETER_REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), * )); * * @author Fabien Potencier @@ -347,7 +347,7 @@ class InputDefinition $elements = array(); foreach ($this->getOptions() as $option) { $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; - $elements[] = sprintf('['.($option->isParameterRequired() ? '%s--%s="..."' : ($option->isParameterOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); + $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); } foreach ($this->getArguments() as $argument) { @@ -399,7 +399,7 @@ class InputDefinition $text[] = 'Options:'; foreach ($this->getOptions() as $option) { - if ($option->acceptParameter() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { $default = sprintf(' (default: %s)', is_array($option->getDefault()) ? str_replace("\n", '', print_r($option->getDefault(), true)): $option->getDefault()); } else { $default = ''; @@ -450,13 +450,13 @@ class InputDefinition $optionsXML->appendChild($optionXML = $dom->createElement('option')); $optionXML->setAttribute('name', '--'.$option->getName()); $optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); - $optionXML->setAttribute('accept_parameter', $option->acceptParameter() ? 1 : 0); - $optionXML->setAttribute('is_parameter_required', $option->isParameterRequired() ? 1 : 0); + $optionXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $optionXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); $optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); $optionXML->appendChild($descriptionXML = $dom->createElement('description')); $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); - if ($option->acceptParameter()) { + if ($option->acceptValue()) { $optionXML->appendChild($defaultsXML = $dom->createElement('defaults')); $defaults = is_array($option->getDefault()) ? $option->getDefault() : ($option->getDefault() ? array($option->getDefault()) : array()); foreach ($defaults as $default) { diff --git a/lib/vendor/Symfony/Component/Console/Input/InputOption.php b/lib/vendor/Symfony/Component/Console/Input/InputOption.php index ca76c826f..3b22bbe1e 100644 --- a/lib/vendor/Symfony/Component/Console/Input/InputOption.php +++ b/lib/vendor/Symfony/Component/Console/Input/InputOption.php @@ -18,10 +18,10 @@ namespace Symfony\Component\Console\Input; */ class InputOption { - const PARAMETER_NONE = 1; - const PARAMETER_REQUIRED = 2; - const PARAMETER_OPTIONAL = 4; - const PARAMETER_IS_ARRAY = 8; + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; protected $name; protected $shortcut; @@ -34,9 +34,9 @@ class InputOption * * @param string $name The option name * @param string $shortcut The shortcut (can be null) - * @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL + * @param integer $mode The option mode: One of the VALUE_* constants * @param string $description A description text - * @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE) + * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE) * * @throws \InvalidArgumentException If option mode is invalid or incompatible */ @@ -57,7 +57,7 @@ class InputOption } if (null === $mode) { - $mode = self::PARAMETER_NONE; + $mode = self::VALUE_NONE; } else if (!is_int($mode) || $mode > 15) { throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } @@ -67,8 +67,8 @@ class InputOption $this->mode = $mode; $this->description = $description; - if ($this->isArray() && !$this->acceptParameter()) { - throw new \InvalidArgumentException('Impossible to have an option mode PARAMETER_IS_ARRAY if the option does not accept a parameter.'); + if ($this->isArray() && !$this->acceptValue()) { + throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); } $this->setDefault($default); @@ -95,43 +95,43 @@ class InputOption } /** - * Returns true if the option accept a parameter. + * Returns true if the option accepts a value. * - * @return Boolean true if parameter mode is not self::PARAMETER_NONE, false otherwise + * @return Boolean true if value mode is not self::VALUE_NONE, false otherwise */ - public function acceptParameter() + public function acceptValue() { - return $this->isParameterRequired() || $this->isParameterOptional(); + return $this->isValueRequired() || $this->isValueOptional(); } /** - * Returns true if the option requires a parameter. + * Returns true if the option requires a value. * - * @return Boolean true if parameter mode is self::PARAMETER_REQUIRED, false otherwise + * @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise */ - public function isParameterRequired() + public function isValueRequired() { - return self::PARAMETER_REQUIRED === (self::PARAMETER_REQUIRED & $this->mode); + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); } /** - * Returns true if the option takes an optional parameter. + * Returns true if the option takes an optional value. * - * @return Boolean true if parameter mode is self::PARAMETER_OPTIONAL, false otherwise + * @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise */ - public function isParameterOptional() + public function isValueOptional() { - return self::PARAMETER_OPTIONAL === (self::PARAMETER_OPTIONAL & $this->mode); + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); } /** * Returns true if the option can take multiple values. * - * @return Boolean true if mode is self::PARAMETER_IS_ARRAY, false otherwise + * @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise */ public function isArray() { - return self::PARAMETER_IS_ARRAY === (self::PARAMETER_IS_ARRAY & $this->mode); + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); } /** @@ -141,8 +141,8 @@ class InputOption */ public function setDefault($default = null) { - if (self::PARAMETER_NONE === (self::PARAMETER_NONE & $this->mode) && null !== $default) { - throw new \LogicException('Cannot set a default value when using Option::PARAMETER_NONE mode.'); + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new \LogicException('Cannot set a default value when using Option::VALUE_NONE mode.'); } if ($this->isArray()) { @@ -153,7 +153,7 @@ class InputOption } } - $this->default = $this->acceptParameter() ? $default : false; + $this->default = $this->acceptValue() ? $default : false; } /** diff --git a/lib/vendor/Symfony/Component/Console/Shell.php b/lib/vendor/Symfony/Component/Console/Shell.php index 278572795..38b2fbbfe 100644 --- a/lib/vendor/Symfony/Component/Console/Shell.php +++ b/lib/vendor/Symfony/Component/Console/Shell.php @@ -97,7 +97,7 @@ class Shell // task name? if (false === strpos($text, ' ') || !$text) { - return array_keys($this->application->getCommands()); + return array_keys($this->application->all()); } // options and arguments? From 0ba9321f067f832c95582b93c105d3fbd80791d5 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Sat, 6 Nov 2010 05:25:15 +0800 Subject: [PATCH 087/119] [ORM] Fixed typo --- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index e43965ee7..31f75f0e1 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -689,7 +689,7 @@ class SqlWalker implements TreeWalker $sql = $this->_scalarResultAliasMap[$columnName]; } - return $sql . ' ' . strtoupper($orderByItem->type);; + return $sql . ' ' . strtoupper($orderByItem->type); } /** From 8e4197adc5f09545df3a8a1a8e05e3d7a5ca0ea5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 3 Dec 2010 17:34:56 +0100 Subject: [PATCH 088/119] DDC-909 - Fix Result Cache with entities as parameters. --- lib/Doctrine/ORM/AbstractQuery.php | 15 ++++- .../Tests/ORM/Functional/ResultCacheTest.php | 60 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index e578a7e70..562a5d6fb 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -562,9 +562,22 @@ abstract class AbstractQuery if ($this->_resultCacheId) { return $this->_resultCacheId; } else { + $params = $this->_params; + foreach ($params AS $key => $value) { + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { + if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) { + $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $class = $this->_em->getClassMetadata(get_class($value)); + $idValues = $class->getIdentifierValues($value); + } + $params[$key] = $idValues; + } + } + $sql = $this->getSql(); ksort($this->_hints); - return md5(implode(";", (array)$sql) . var_export($this->_params, true) . + return md5(implode(";", (array)$sql) . var_export($params, true) . var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/ResultCacheTest.php index 3c5f00982..68616f99b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ResultCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ResultCacheTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Common\Cache\ArrayCache; require_once __DIR__ . '/../../TestInit.php'; @@ -146,4 +147,63 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($cacheCount + 1, count($cache->getIds())); } + + /** + * @group DDC-909 + */ + public function testResultCacheWithObjectParameter() + { + $user1 = new CmsUser; + $user1->name = 'Roman'; + $user1->username = 'romanb'; + $user1->status = 'dev'; + + $user2 = new CmsUser; + $user2->name = 'Benjamin'; + $user2->username = 'beberlei'; + $user2->status = 'dev'; + + $article = new CmsArticle(); + $article->text = "foo"; + $article->topic = "baz"; + $article->user = $user1; + + $this->_em->persist($article); + $this->_em->persist($user1); + $this->_em->persist($user2); + $this->_em->flush(); + + $query = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.user = ?1'); + $query->setParameter(1, $user1); + + $cache = new ArrayCache(); + + $query->setResultCacheDriver($cache)->useResultCache(true); + + $articles = $query->getResult(); + + $this->assertEquals(1, count($articles)); + $this->assertEquals('baz', $articles[0]->topic); + + $this->_em->clear(); + + $query2 = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.user = ?1'); + $query2->setParameter(1, $user1); + + $query2->setResultCacheDriver($cache)->useResultCache(true); + + $articles = $query2->getResult(); + + $this->assertEquals(1, count($articles)); + $this->assertEquals('baz', $articles[0]->topic); + + $query3 = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.user = ?1'); + $query3->setParameter(1, $user2); + + $query3->setResultCacheDriver($cache)->useResultCache(true); + + $articles = $query3->getResult(); + + $this->assertEquals(0, count($articles)); + } } \ No newline at end of file From c6a6aaf493671820856341d76d9c475a45ff2127 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 3 Dec 2010 17:44:24 +0100 Subject: [PATCH 089/119] DDC-899 - Add method to check if EntityManager is still open. --- lib/Doctrine/ORM/EntityManager.php | 10 ++++++++++ tests/Doctrine/Tests/ORM/EntityManagerTest.php | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 0ab1541ba..3af9cf3c3 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -619,6 +619,16 @@ class EntityManager } } + /** + * Check if the Entity manager is open or closed. + * + * @return bool + */ + public function isOpen() + { + return (!$this->closed); + } + /** * Gets the UnitOfWork used by the EntityManager to coordinate operations. * diff --git a/tests/Doctrine/Tests/ORM/EntityManagerTest.php b/tests/Doctrine/Tests/ORM/EntityManagerTest.php index ad5f41de7..a2fe796d6 100644 --- a/tests/Doctrine/Tests/ORM/EntityManagerTest.php +++ b/tests/Doctrine/Tests/ORM/EntityManagerTest.php @@ -14,6 +14,16 @@ class EntityManagerTest extends \Doctrine\Tests\OrmTestCase $this->_em = $this->_getTestEntityManager(); } + /** + * @group DDC-899 + */ + public function testIsOpen() + { + $this->assertTrue($this->_em->isOpen()); + $this->_em->close(); + $this->assertFalse($this->_em->isOpen()); + } + public function testGetConnection() { $this->assertType('\Doctrine\DBAL\Connection', $this->_em->getConnection()); From 054f26c0a7b30801bf7afc65bfcec62f3149ef65 Mon Sep 17 00:00:00 2001 From: "Jonathan H. Wage" Date: Fri, 3 Dec 2010 11:44:22 -0600 Subject: [PATCH 090/119] Fixing issue with change to ClassMetadataFactory constructor and ConvertMappingCommand. --- .../ORM/Tools/Console/Command/ConvertMappingCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index 75fea1f89..797bc29a8 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -96,7 +96,8 @@ EOT ); } - $cmf = new DisconnectedClassMetadataFactory($em); + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); $metadata = $cmf->getAllMetadata(); $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); From ca682b384050e23f4e8175c2ff91ed0bd52b2104 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Dec 2010 10:59:14 +0100 Subject: [PATCH 091/119] Update build.xml with deployment tasks. --- build.properties.dev | 3 ++- build.xml | 27 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/build.properties.dev b/build.properties.dev index d0844332f..7e72d6f1e 100644 --- a/build.properties.dev +++ b/build.properties.dev @@ -6,7 +6,8 @@ build.dir=build dist.dir=dist report.dir=reports log.archive.dir=logs -svn.path=/usr/bin/svn +project.pirum_dir= +project.download_dir= test.phpunit_configuration_file= test.phpunit_generate_coverage=0 test.pmd_reports=0 diff --git a/build.xml b/build.xml index 1bf835843..83157508a 100644 --- a/build.xml +++ b/build.xml @@ -141,7 +141,7 @@ - + DoctrineORM Doctrine Object Relational Mapper @@ -180,4 +180,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2ba9d5a5977a9f9d3823dff61c163a1b8fe98fc6 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Dec 2010 11:04:20 +0100 Subject: [PATCH 092/119] Update Dependency of DBAL from RC3 to RC4 --- lib/vendor/doctrine-dbal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vendor/doctrine-dbal b/lib/vendor/doctrine-dbal index dd708ce98..6d36298de 160000 --- a/lib/vendor/doctrine-dbal +++ b/lib/vendor/doctrine-dbal @@ -1 +1 @@ -Subproject commit dd708ce98aa35018ec45b078c6c31f289b3f61a0 +Subproject commit 6d36298de493fc00f90c33d38b4ea1dd36600bd0 From 8654d060c60d68cd5eddddbc9013c625f2fb7503 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Dec 2010 05:24:00 -0500 Subject: [PATCH 093/119] Bump Dev Version to 2.0.0-DEV --- lib/Doctrine/ORM/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 89f885a38..01a32d83b 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.0.0RC1-DEV'; + const VERSION = '2.0.0-DEV'; /** * Compares a Doctrine version with the current one. From 72ba369dbb5dfc8efcda56f5d751e5341736f3fb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Dec 2010 05:28:26 -0500 Subject: [PATCH 094/119] Revert Version to 2.0.0RC1-DEV --- lib/Doctrine/ORM/Version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 01a32d83b..f51b170be 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.0.0-DEV'; + const VERSION = '2.0.0RC1-DEV'; /** * Compares a Doctrine version with the current one. @@ -52,4 +52,4 @@ class Version return version_compare($version, $currentVersion); } -} \ No newline at end of file +} From 9a68015ccf5b3b25aaf3c05879e71e4975d43607 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Dec 2010 05:29:18 -0500 Subject: [PATCH 095/119] Bump Dev Version to 2.0.0-DEV --- lib/Doctrine/ORM/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index f51b170be..203cdfa2a 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.0.0RC1-DEV'; + const VERSION = '2.0.0-DEV'; /** * Compares a Doctrine version with the current one. From ad5032774423025889ee3d01ecd27b2b054f88e3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 Dec 2010 11:31:45 +0100 Subject: [PATCH 096/119] Fixed some build.xml problems. --- build.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build.xml b/build.xml index 83157508a..4736e777d 100644 --- a/build.xml +++ b/build.xml @@ -5,8 +5,6 @@ --> - - @@ -24,6 +22,7 @@ + - + @@ -187,7 +186,7 @@ - + From ef50d940de8eba75e72471b33ab86285a480b33b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Dec 2010 21:21:00 +0100 Subject: [PATCH 097/119] CleanUp in SchemaTool. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 76685c2b7..82b3a1dbf 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -132,13 +132,6 @@ class SchemaTool $table = $schema->createTable($class->getQuotedTableName($this->_platform)); - // TODO: Remove - /**if ($class->isIdGeneratorIdentity()) { - $table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_IDENTITY); - } else if ($class->isIdGeneratorSequence()) { - $table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_SEQUENCE); - }*/ - $columns = array(); // table columns if ($class->isInheritanceTypeSingleTable()) { @@ -189,10 +182,6 @@ class SchemaTool $table->getColumn($class->identifier[0])->setAutoincrement(false); $pkColumns[] = $columnName; - // TODO: REMOVE - /*if ($table->isIdGeneratorIdentity()) { - $table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_NONE); - }*/ // Add a FK constraint on the ID column $table->addUnnamedForeignKeyConstraint( @@ -347,6 +336,9 @@ class SchemaTool if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) { $options['autoincrement'] = true; } + if ($class->isInheritanceTypeJoined() && $class->name != $class->rootEntityName) { + $options['autoincrement'] = false; + } if ($table->hasColumn($columnName)) { // required in some inheritance scenarios @@ -502,7 +494,7 @@ class SchemaTool */ public function dropSchema(array $classes) { - $dropSchemaSql = $this->getDropSchemaSql($classes); + $dropSchemaSql = $this->getDropSchemaSQL($classes); $conn = $this->_em->getConnection(); foreach ($dropSchemaSql as $sql) { From aa2501eb965eec7949af1cfd125389dc14693dbe Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Dec 2010 23:29:21 +0100 Subject: [PATCH 098/119] DDC-917 - Bugfix with DriverChain::getAllClassNames() - It was not semantically correct and returning too many metadata. --- lib/Doctrine/ORM/Mapping/Driver/DriverChain.php | 11 ++++++++--- tests/Doctrine/Tests/ORM/Mapping/DriverChainTest.php | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php b/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php index d97a61e5b..77c258a18 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php @@ -88,10 +88,15 @@ class DriverChain implements Driver public function getAllClassNames() { $classNames = array(); - foreach ($this->_drivers AS $driver) { - $classNames = array_merge($classNames, $driver->getAllClassNames()); + foreach ($this->_drivers AS $namespace => $driver) { + $driverClasses = $driver->getAllClassNames(); + foreach ($driverClasses AS $className) { + if (strpos($className, $namespace) === 0) { + $classNames[] = $className; + } + } } - return $classNames; + return array_unique($classNames); } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/DriverChainTest.php b/tests/Doctrine/Tests/ORM/Mapping/DriverChainTest.php index 333b8638b..d218aaf8f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/DriverChainTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/DriverChainTest.php @@ -60,17 +60,21 @@ class DriverChainTest extends \Doctrine\Tests\OrmTestCase $driver1 = $this->getMock('Doctrine\ORM\Mapping\Driver\Driver'); $driver1->expects($this->once()) ->method('getAllClassNames') - ->will($this->returnValue(array('Foo'))); + ->will($this->returnValue(array('Doctrine\Tests\Models\Company\Foo'))); $driver2 = $this->getMock('Doctrine\ORM\Mapping\Driver\Driver'); $driver2->expects($this->once()) ->method('getAllClassNames') - ->will($this->returnValue(array('Bar', 'Baz'))); + ->will($this->returnValue(array('Doctrine\Tests\ORM\Mapping\Bar', 'Doctrine\Tests\ORM\Mapping\Baz', 'FooBarBaz'))); $chain->addDriver($driver1, 'Doctrine\Tests\Models\Company'); $chain->addDriver($driver2, 'Doctrine\Tests\ORM\Mapping'); - $this->assertEquals(array('Foo', 'Bar', 'Baz'), $chain->getAllClassNames()); + $this->assertEquals(array( + 'Doctrine\Tests\Models\Company\Foo', + 'Doctrine\Tests\ORM\Mapping\Bar', + 'Doctrine\Tests\ORM\Mapping\Baz' + ), $chain->getAllClassNames()); } /** From 1daf658ec68ce6762c9bcf0b8a0af59111f3230a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Dec 2010 23:36:15 +0100 Subject: [PATCH 099/119] DDC-917 - Skip Mapped Superclasses in the Drop Sequence Loop in SchemaTool. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 82b3a1dbf..33572ba9b 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -546,7 +546,7 @@ class SchemaTool $orderedTables = array(); foreach ($classes AS $class) { - if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName && $this->_platform->supportsSequences()) { + if ($class->isIdGeneratorSequence() && !$class->isMappedSuperclass && $class->name == $class->rootEntityName && $this->_platform->supportsSequences()) { $sql[] = $this->_platform->getDropSequenceSQL($class->sequenceGeneratorDefinition['sequenceName']); } } From 5e788a0b8467cfccca0dc622396516ddce3df10e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Dec 2010 23:42:02 +0100 Subject: [PATCH 100/119] DDC-915 - Bugfix in Identification Variable reordering in combination with SQL Walkers. --- lib/Doctrine/ORM/Query/Parser.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index e5b9f69ca..26dfb0535 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -277,6 +277,21 @@ class Parser { $AST = $this->getAST(); + // Fix order of identification variables. + // They have to appear in the select clause in the same order as the + // declarations (from ... x join ... y join ... z ...) appear in the query + // as the hydration process relies on that order for proper operation. + if ( count($this->_identVariableExpressions) > 1) { + foreach ($this->_queryComponents as $dqlAlias => $qComp) { + if (isset($this->_identVariableExpressions[$dqlAlias])) { + $expr = $this->_identVariableExpressions[$dqlAlias]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + unset($AST->selectClause->selectExpressions[$key]); + $AST->selectClause->selectExpressions[] = $expr; + } + } + } + if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { $this->_customTreeWalkers = $customWalkers; } @@ -302,21 +317,6 @@ class Parser } } - // Fix order of identification variables. - // They have to appear in the select clause in the same order as the - // declarations (from ... x join ... y join ... z ...) appear in the query - // as the hydration process relies on that order for proper operation. - if ( count($this->_identVariableExpressions) > 1) { - foreach ($this->_queryComponents as $dqlAlias => $qComp) { - if (isset($this->_identVariableExpressions[$dqlAlias])) { - $expr = $this->_identVariableExpressions[$dqlAlias]; - $key = array_search($expr, $AST->selectClause->selectExpressions); - unset($AST->selectClause->selectExpressions[$key]); - $AST->selectClause->selectExpressions[] = $expr; - } - } - } - if ($this->_customOutputWalker) { $outputWalker = new $this->_customOutputWalker( $this->_query, $this->_parserResult, $this->_queryComponents From 06326918a5f2dd85744a54f98dacee41d072fe72 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 10 Dec 2010 21:22:48 +0100 Subject: [PATCH 101/119] DDC-915, DDC-925 - Fix Identification Ordering in combination with Tree Walkers. --- .../ORM/Functional/Ticket/DDC736Test.php | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php index 0c8b764a2..66c73bb78 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php @@ -4,6 +4,8 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Tests\Models\ECommerce\ECommerceCart; use Doctrine\Tests\Models\ECommerce\ECommerceCustomer; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\AST; require_once __DIR__ . '/../../../TestInit.php'; @@ -18,7 +20,7 @@ class DDC736Test extends \Doctrine\Tests\OrmFunctionalTestCase /** * @group DDC-736 */ - public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManagedEntity() + public function testReorderEntityFetchJoinForHydration() { $cust = new ECommerceCustomer; $cust->setName('roman'); @@ -43,4 +45,56 @@ class DDC736Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceCustomer', $cart2->getCustomer()); $this->assertEquals(array('name' => 'roman', 'payment' => 'cash'), $result); } + + /** + * @group DDC-736 + * @group DDC-925 + * @group DDC-915 + */ + public function testDqlTreeWalkerReordering() + { + $cust = new ECommerceCustomer; + $cust->setName('roman'); + + $cart = new ECommerceCart; + $cart->setPayment('cash'); + $cart->setCustomer($cust); + + $this->_em->persist($cust); + $this->_em->persist($cart); + $this->_em->flush(); + $this->_em->clear(); + + $dql = "select c, c.name, ca, ca.payment from Doctrine\Tests\Models\ECommerce\ECommerceCart ca join ca.customer c"; + $result = $this->_em->createQuery($dql) + ->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\Ticket\DisableFetchJoinTreeWalker')) + ->getResult(); + + /* @var $cart2 Doctrine\Tests\Models\ECommerce\ECommerceCart */ + $cart2 = $result[0][0]; + $this->assertType('Doctrine\ORM\Proxy\Proxy', $cart2->getCustomer()); + } } + +class DisableFetchJoinTreeWalker extends \Doctrine\ORM\Query\TreeWalkerAdapter +{ + public function walkSelectStatement(AST\SelectStatement $AST) + { + $this->walkSelectClause($AST->selectClause); + } + + /** + * @param \Doctrine\ORM\Query\AST\SelectClause $selectClause + */ + public function walkSelectClause($selectClause) + { + foreach ($selectClause->selectExpressions AS $key => $selectExpr) { + /* @var $selectExpr \Doctrine\ORM\Query\AST\SelectExpression */ + if ($selectExpr->expression == "c") { + unset($selectClause->selectExpressions[$key]); + break; + } + } + } +} + From 4f154b6aa171d44840219390a82afcd15c074d06 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 10 Dec 2010 21:55:48 +0100 Subject: [PATCH 102/119] DDC-920 - Fix bug when detaching a managed entity that is not yet in the identity map (no id). --- lib/Doctrine/ORM/UnitOfWork.php | 4 +++- .../ORM/Functional/DetachedEntityTest.php | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 5088bb273..76ce5219a 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1509,7 +1509,9 @@ class UnitOfWork implements PropertyChangedListener switch ($this->getEntityState($entity, self::STATE_DETACHED)) { case self::STATE_MANAGED: - $this->removeFromIdentityMap($entity); + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } unset($this->entityInsertions[$oid], $this->entityUpdates[$oid], $this->entityDeletions[$oid], $this->entityIdentifiers[$oid], $this->entityStates[$oid], $this->originalEntityData[$oid]); diff --git a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php index a993606cf..30069a37b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php @@ -173,5 +173,23 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $newUser); $this->assertEquals('gblanco', $newUser->username); } + + /** + * @group DDC-920 + */ + public function testDetachManagedUnpersistedEntity() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $this->_em->persist($user); + $this->_em->detach($user); + + $this->_em->flush(); + + $this->assertNull($user->id); + } } From 3c0f92f4c7daeee029d10302fc9da285f29fc0f2 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 11 Dec 2010 00:54:54 +0100 Subject: [PATCH 103/119] Remove call to EntityManager#flush() if the unitofwork contains pending insertions. Flush should always be triggered explicitly. --- lib/Doctrine/ORM/AbstractQuery.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 562a5d6fb..dd9d1c81a 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -482,15 +482,6 @@ abstract class AbstractQuery */ public function execute($params = array(), $hydrationMode = null) { - // If there are still pending insertions in the UnitOfWork we need to flush - // in order to guarantee a correct result. - //TODO: Think this over. Its tricky. Not doing this can lead to strange results - // potentially, but doing it could result in endless loops when querying during - // a flush, i.e. inside an event listener. - if ($this->_em->getUnitOfWork()->hasPendingInsertions()) { - $this->_em->flush(); - } - if ($hydrationMode !== null) { $this->setHydrationMode($hydrationMode); } From 6c26af069cbc6be4a1676265d80ac434cb9bf623 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 12 Dec 2010 15:43:12 +0100 Subject: [PATCH 104/119] DDC-920 - Fix bug in DetachedEntityTest that occours with pre-persist generators (Postgresql, Oracle). Didnt came up when testing against Sqlite. --- tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php index 30069a37b..db2678e62 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php @@ -189,7 +189,8 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); - $this->assertNull($user->id); + $this->assertFalse($this->_em->contains($user)); + $this->assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user)); } } From 6c9eeb6127dae74d431044ed1ba41d6721198104 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 12 Dec 2010 16:02:46 +0100 Subject: [PATCH 105/119] Update Dependency to DBAL RC5 --- lib/vendor/doctrine-dbal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vendor/doctrine-dbal b/lib/vendor/doctrine-dbal index 6d36298de..17c1476e8 160000 --- a/lib/vendor/doctrine-dbal +++ b/lib/vendor/doctrine-dbal @@ -1 +1 @@ -Subproject commit 6d36298de493fc00f90c33d38b4ea1dd36600bd0 +Subproject commit 17c1476e8681ad05154631bcc2b9ec17419461b2 From 2648c1a6ca56ab93ef7fb41d32bc614606cb686e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 14 Dec 2010 23:10:45 +0100 Subject: [PATCH 106/119] Fix build.xml to generate proper package.xml for Windows PEAR Package. --- build.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.xml b/build.xml index 4736e777d..fcd2dae5c 100644 --- a/build.xml +++ b/build.xml @@ -168,6 +168,8 @@ + + From d87391e40c3a2e56bdfc75f108edcdea1bd84f3d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 14 Dec 2010 23:26:40 +0100 Subject: [PATCH 107/119] DDC-933 - Fix bug in lock sql generation of CTI classes. --- .../Persisters/JoinedSubclassPersister.php | 1 + .../ORM/Functional/Ticket/DDC933Test.php | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 4cbe11ee9..e9750f8c3 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -365,6 +365,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister */ public function getLockTablesSql() { + $idColumns = $this->_class->getIdentifierColumnNames(); $baseTableAlias = $this->_getSQLTableAlias($this->_class->name); // INNER JOIN parent tables diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php new file mode 100644 index 000000000..c5d95e490 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php @@ -0,0 +1,37 @@ +useModelSet('company'); + parent::setUp(); + } + + /** + * @group DDC-933 + */ + public function testLockCTIClass() + { + $manager = new \Doctrine\Tests\Models\Company\CompanyManager(); + $manager->setName('beberlei'); + $manager->setSalary(1234); + $manager->setTitle('Vice Precident of This Test'); + $manager->setDepartment("Foo"); + + $this->_em->persist($manager); + $this->_em->flush(); + + $this->_em->beginTransaction(); + $this->_em->lock($manager, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ); + $this->_em->rollback(); + } +} \ No newline at end of file From c03fc00f45bf8d05aa445a3b7f9002c00e086af4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 15 Dec 2010 22:01:16 +0100 Subject: [PATCH 108/119] DDC-936 - Fix target-entity and repository-class to be string rather than xs:NMTOKEN because of the class backslash. --- doctrine-mapping.xsd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index badd28f4f..accab4f03 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -73,7 +73,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -264,7 +264,7 @@ - + @@ -279,7 +279,7 @@ - + @@ -295,7 +295,7 @@ - + From 43c63765db8f9d3d96939e5356ca8488cf14ef7c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 20 Dec 2010 23:34:28 +0100 Subject: [PATCH 109/119] Extend phing build.xml for upcoming release. --- build.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.xml b/build.xml index fcd2dae5c..e4b7805e1 100644 --- a/build.xml +++ b/build.xml @@ -83,6 +83,7 @@ Builds ORM package, preparing it for distribution. --> + @@ -183,6 +184,7 @@ + @@ -197,6 +199,7 @@ + From 988d229c07763568c969e68a69daa2b00150aa12 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Dec 2010 22:04:13 +0100 Subject: [PATCH 110/119] Fix XSD Schema --- doctrine-mapping.xsd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index accab4f03..2dde4d9b8 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -73,7 +73,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -264,7 +264,7 @@ - + @@ -279,7 +279,7 @@ - + @@ -295,11 +295,11 @@ - + - \ No newline at end of file + From 22ffbe7488d04118542220c344cdb5fceba8e140 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Dec 2010 22:33:23 +0100 Subject: [PATCH 111/119] Fix tests so that PostgreSQL does not fail anymore on certain test. --- .../Tests/ORM/Functional/Ticket/DDC168Test.php | 17 ++++++++++++++--- .../Tests/ORM/Functional/Ticket/DDC933Test.php | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php index ac06d5b38..41c41df45 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php @@ -8,9 +8,23 @@ require_once __DIR__ . '/../../../TestInit.php'; class DDC168Test extends \Doctrine\Tests\OrmFunctionalTestCase { + protected $oldMetadata; + protected function setUp() { $this->useModelSet('company'); parent::setUp(); + + $this->oldMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee'); + + $metadata = clone $this->oldMetadata; + ksort($metadata->reflFields); + $this->_em->getMetadataFactory()->setMetadataFor('Doctrine\Tests\Models\Company\CompanyEmployee', $metadata); + } + + public function tearDown() + { + $this->_em->getMetadataFactory()->setMetadataFor('Doctrine\Tests\Models\Company\CompanyEmployee', $this->oldMetadata); + parent::tearDown(); } /** @@ -19,9 +33,6 @@ class DDC168Test extends \Doctrine\Tests\OrmFunctionalTestCase public function testJoinedSubclassPersisterRequiresSpecificOrderOfMetadataReflFieldsArray() { //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); - - $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee'); - ksort($metadata->reflFields); $spouse = new CompanyEmployee; $spouse->setName("Blub"); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php index c5d95e490..cd2b584ea 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC933Test.php @@ -21,6 +21,8 @@ class DDC933Test extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testLockCTIClass() { + //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger()); + $manager = new \Doctrine\Tests\Models\Company\CompanyManager(); $manager->setName('beberlei'); $manager->setSalary(1234); From 7def30f2831b2f06b1ea0fed951ed4ff0ec33cc2 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Dec 2010 22:39:26 +0100 Subject: [PATCH 112/119] Update dependencies to Common 2.0 stable and DBAL 2.0 stable. --- lib/vendor/doctrine-common | 2 +- lib/vendor/doctrine-dbal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vendor/doctrine-common b/lib/vendor/doctrine-common index a46c6180f..9eb66b7cf 160000 --- a/lib/vendor/doctrine-common +++ b/lib/vendor/doctrine-common @@ -1 +1 @@ -Subproject commit a46c6180f96647fdd66e2c8f2771d61ecebe6a3f +Subproject commit 9eb66b7cf90919ff64281f7e476530fd879cefe4 diff --git a/lib/vendor/doctrine-dbal b/lib/vendor/doctrine-dbal index 17c1476e8..e97fbbf75 160000 --- a/lib/vendor/doctrine-dbal +++ b/lib/vendor/doctrine-dbal @@ -1 +1 @@ -Subproject commit 17c1476e8681ad05154631bcc2b9ec17419461b2 +Subproject commit e97fbbf755ea7bc5d575d71778501a73f72a4ecb From 6988b55f50d7a5e033248c7c30699eff317ae93a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 21 Dec 2010 16:45:50 -0500 Subject: [PATCH 113/119] Bump Dev Version to 2.1.0-DEV --- lib/Doctrine/ORM/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 203cdfa2a..e459dceb6 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.0.0-DEV'; + const VERSION = '2.1.0-DEV'; /** * Compares a Doctrine version with the current one. From e46c65db09826c41a1ec4892247a1d743f770396 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 22 Dec 2010 00:17:50 +0100 Subject: [PATCH 114/119] Fix for DDC-944 --- lib/Doctrine/ORM/Mapping/MappingException.php | 4 ++-- .../Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index a7b1c75ef..15cfc60ff 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -68,9 +68,9 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("No mapping file found named '$fileName' for class '$entityName'."); } - public static function mappingNotFound($fieldName) + public static function mappingNotFound($className, $fieldName) { - return new self("No mapping found for field '$fieldName'."); + return new self("No mapping found for field '$fieldName' on class '$className'."); } public static function oneToManyRequiresMappedBy($fieldName) diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 3264519e8..f8425b42f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -289,4 +289,15 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $cm->setIdentifier(array('name', 'username')); $this->assertTrue($cm->isIdentifierComposite); } + + /** + * @group DDC-944 + */ + public function testMappingNotFound() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + + $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', "No mapping found for field 'foo' on class 'Doctrine\Tests\Models\CMS\CmsUser'."); + $cm->getFieldMapping('foo'); + } } \ No newline at end of file From a4f88407c2f518b327e4b0e9a0d0b76b0e771cdc Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 22 Dec 2010 22:04:11 +0100 Subject: [PATCH 115/119] DDC-931 - SchemaTool#dropSchema() should not stop on failure of a single query (as stated in docblocks). --- lib/Doctrine/ORM/Tools/SchemaTool.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 33572ba9b..e87a99a98 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -498,7 +498,11 @@ class SchemaTool $conn = $this->_em->getConnection(); foreach ($dropSchemaSql as $sql) { - $conn->executeQuery($sql); + try { + $conn->executeQuery($sql); + } catch(\Exception $e) { + + } } } From fe672d2f612e4ba3b329271725545b574cc4df42 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 28 Dec 2010 10:17:33 +0100 Subject: [PATCH 116/119] DDC-617 - Throw error if selecting identification variables without picking at least one root entity alias. --- lib/Doctrine/ORM/Query/Parser.php | 56 ++++-- .../ORM/Query/LanguageRecognitionTest.php | 186 +++++++++--------- .../ORM/Query/SelectSqlGenerationTest.php | 2 +- 3 files changed, 140 insertions(+), 104 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 26dfb0535..0c22c9fbf 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -277,20 +277,8 @@ class Parser { $AST = $this->getAST(); - // Fix order of identification variables. - // They have to appear in the select clause in the same order as the - // declarations (from ... x join ... y join ... z ...) appear in the query - // as the hydration process relies on that order for proper operation. - if ( count($this->_identVariableExpressions) > 1) { - foreach ($this->_queryComponents as $dqlAlias => $qComp) { - if (isset($this->_identVariableExpressions[$dqlAlias])) { - $expr = $this->_identVariableExpressions[$dqlAlias]; - $key = array_search($expr, $AST->selectClause->selectExpressions); - unset($AST->selectClause->selectExpressions[$key]); - $AST->selectClause->selectExpressions[] = $expr; - } - } - } + $this->fixIdentificationVariableOrder($AST); + $this->assertSelectEntityRootAliasRequirement(); if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { $this->_customTreeWalkers = $customWalkers; @@ -332,6 +320,46 @@ class Parser return $this->_parserResult; } + + private function assertSelectEntityRootAliasRequirement() + { + if ( count($this->_identVariableExpressions) > 0) { + $foundRootEntity = false; + foreach ($this->_identVariableExpressions AS $dqlAlias => $expr) { + if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) { + $foundRootEntity = true; + } + } + + if (!$foundRootEntity) { + $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.'); + } + } + } + + /** + * Fix order of identification variables. + * + * They have to appear in the select clause in the same order as the + * declarations (from ... x join ... y join ... z ...) appear in the query + * as the hydration process relies on that order for proper operation. + * + * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST + * @return void + */ + private function fixIdentificationVariableOrder($AST) + { + if ( count($this->_identVariableExpressions) > 1) { + foreach ($this->_queryComponents as $dqlAlias => $qComp) { + if (isset($this->_identVariableExpressions[$dqlAlias])) { + $expr = $this->_identVariableExpressions[$dqlAlias]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + unset($AST->selectClause->selectExpressions[$key]); + $AST->selectClause->selectExpressions[] = $expr; + } + } + } + } /** * Generates a new syntax error. diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index aab94c9dc..0ebe1aafe 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -15,7 +15,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase $this->_em = $this->_getTestEntityManager(); } - public function assertValidDql($dql, $debug = false) + public function assertValidDQL($dql, $debug = false) { try { $parserResult = $this->parseDql($dql); @@ -28,7 +28,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase } } - public function assertInvalidDql($dql, $debug = false) + public function assertInvalidDQL($dql, $debug = false) { try { $parserResult = $this->parseDql($dql); @@ -62,311 +62,311 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase public function testEmptyQueryString() { - $this->assertInvalidDql(''); + $this->assertInvalidDQL(''); } public function testPlainFromClauseWithAlias() { - $this->assertValidDql('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testSelectSingleComponentWithAsterisk() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testSelectSingleComponentWithMultipleColumns() { - $this->assertValidDql('SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testSelectMultipleComponentsUsingMultipleFrom() { - $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE u = p.user'); + $this->assertValidDQL('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE u = p.user'); } public function testSelectMultipleComponentsWithAsterisk() { - $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p'); } public function testSelectDistinctIsSupported() { - $this->assertValidDql('SELECT DISTINCT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT DISTINCT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testAggregateFunctionInSelect() { - $this->assertValidDql('SELECT COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testDuplicatedAliasInAggregateFunction() { - $this->assertInvalidDql('SELECT COUNT(u.id) AS num, SUM(u.id) AS num FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertInvalidDQL('SELECT COUNT(u.id) AS num, SUM(u.id) AS num FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testAggregateFunctionWithDistinctInSelect() { - $this->assertValidDql('SELECT COUNT(DISTINCT u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT COUNT(DISTINCT u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testFunctionalExpressionsSupportedInWherePart() { - $this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'"); + $this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'"); } public function testArithmeticExpressionsSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000'); } public function testInExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1, 2)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1, 2)'); } public function testInExpressionWithoutSpacesSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1,2,3)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1,2,3)'); } public function testNotInExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (1)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (1)'); } public function testInExpressionWithSingleValuedAssociationPathExpression() { - $this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar IN (?1, ?2)"); + $this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar IN (?1, ?2)"); } public function testInvalidInExpressionWithCollectionValuedAssociationPathExpression() { - $this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IN (?1, ?2)"); + $this->assertInvalidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IN (?1, ?2)"); } public function testInstanceOfExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee'); } public function testInstanceOfExpressionWithInputParamSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1'); } public function testNotInstanceOfExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1'); } public function testExistsExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)'); } public function testNotExistsExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)'); } public function testAggregateFunctionInHavingClause() { - $this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING COUNT(p.phonenumber) > 2'); - $this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING MAX(u.name) = 'romanb'"); + $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING COUNT(p.phonenumber) > 2'); + $this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING MAX(u.name) = 'romanb'"); } public function testLeftJoin() { - $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p'); } public function testJoin() { - $this->assertValidDql('SELECT u,p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u,p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p'); } public function testInnerJoin() { - $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers p'); } public function testMultipleLeftJoin() { - $this->assertValidDql('SELECT u, a, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a LEFT JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u, a, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a LEFT JOIN u.phonenumbers p'); } public function testMultipleInnerJoin() { - $this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INNER JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INNER JOIN u.phonenumbers p'); } public function testMixingOfJoins() { - $this->assertValidDql('SELECT u.name, a.topic, p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a LEFT JOIN u.phonenumbers p'); + $this->assertValidDQL('SELECT u.name, a.topic, p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a LEFT JOIN u.phonenumbers p'); } public function testOrderBySingleColumn() { - $this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name'); + $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name'); } public function testOrderBySingleColumnAscending() { - $this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name ASC'); + $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name ASC'); } public function testOrderBySingleColumnDescending() { - $this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name DESC'); + $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name DESC'); } public function testOrderByMultipleColumns() { - $this->assertValidDql('SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username DESC, u.name DESC'); + $this->assertValidDQL('SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username DESC, u.name DESC'); } public function testSubselectInInExpression() { - $this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = 'zYne')"); + $this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = 'zYne')"); } public function testSubselectInSelectPart() { - $this->assertValidDql("SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); + $this->assertValidDQL("SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); } public function testArithmeticExpressionInSelectPart() { - $this->assertValidDql("SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u"); + $this->assertValidDQL("SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u"); } public function testArithmeticExpressionInSubselectPart() { - $this->assertValidDql("SELECT (SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); + $this->assertValidDQL("SELECT (SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); } public function testArithmeticExpressionWithParenthesisInSubselectPart() { - $this->assertValidDql("SELECT (SELECT (SUM(u.id) / COUNT(u.id)) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); + $this->assertValidDQL("SELECT (SELECT (SUM(u.id) / COUNT(u.id)) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); } public function testDuplicateAliasInSubselectPart() { - $this->assertInvalidDql("SELECT (SELECT SUM(u.id) / COUNT(u.id) AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u2) foo FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); + $this->assertInvalidDQL("SELECT (SELECT SUM(u.id) / COUNT(u.id) AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u2) foo FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); } public function testPositionalInputParameter() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1'); } public function testNamedInputParameter() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id'); } public function testJoinConditionOverrideNotSupported() { - $this->assertInvalidDql("SELECT u.name, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p ON p.phonenumber = '123 123'"); + $this->assertInvalidDQL("SELECT u.name, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p ON p.phonenumber = '123 123'"); } public function testIndexByClauseWithOneComponent() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.id'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.id'); } public function testIndexBySupportsJoins() { - $this->assertValidDql('SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a INDEX BY a.id'); // INDEX BY is now referring to articles + $this->assertValidDQL('SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a INDEX BY a.id'); // INDEX BY is now referring to articles } public function testIndexBySupportsJoins2() { - $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.id LEFT JOIN u.phonenumbers p INDEX BY p.phonenumber'); + $this->assertValidDQL('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.id LEFT JOIN u.phonenumbers p INDEX BY p.phonenumber'); } public function testBetweenExpressionSupported() { - $this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name BETWEEN 'jepso' AND 'zYne'"); + $this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name BETWEEN 'jepso' AND 'zYne'"); } public function testNotBetweenExpressionSupported() { - $this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT BETWEEN 'jepso' AND 'zYne'"); + $this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT BETWEEN 'jepso' AND 'zYne'"); } public function testLikeExpression() { - $this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z%'"); + $this->assertValidDQL("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z%'"); } public function testNotLikeExpression() { - $this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT LIKE 'z%'"); + $this->assertValidDQL("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT LIKE 'z%'"); } public function testLikeExpressionWithCustomEscapeCharacter() { - $this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'"); + $this->assertValidDQL("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'"); } public function testFieldComparisonWithoutAlias() { - $this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE id = 1"); + $this->assertInvalidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE id = 1"); } public function testDuplicatedAliasDeclaration() { - $this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles u WHERE u.id = 1"); + $this->assertInvalidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles u WHERE u.id = 1"); } public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression() { // This should be allowed because avatar is a single-value association. // SQL: SELECT ... FROM forum_user fu INNER JOIN forum_avatar fa ON fu.avatar_id = fa.id WHERE fa.id = ? - $this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u JOIN u.avatar a WHERE a.id = ?1"); + $this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u JOIN u.avatar a WHERE a.id = ?1"); } public function testImplicitJoinInWhereOnCollectionValuedPathExpression() { // This should be forbidden, because articles is a collection - $this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a WHERE a.title = ?"); + $this->assertInvalidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a WHERE a.title = ?"); } public function testInvalidSyntaxIsRejected() { - $this->assertInvalidDql("FOOBAR CmsUser"); - $this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser.articles"); - $this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles.comments"); + $this->assertInvalidDQL("FOOBAR CmsUser"); + $this->assertInvalidDQL("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser.articles"); + $this->assertInvalidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles.comments"); // Currently UNDEFINED OFFSET error - $this->assertInvalidDql("SELECT c FROM CmsUser.articles.comments c"); + $this->assertInvalidDQL("SELECT c FROM CmsUser.articles.comments c"); } public function testUpdateWorksWithOneField() { - $this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone'"); + $this->assertValidDQL("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone'"); } public function testUpdateWorksWithMultipleFields() { - $this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone', u.username = 'some'"); + $this->assertValidDQL("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone', u.username = 'some'"); } public function testUpdateSupportsConditions() { - $this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone' WHERE u.id = 5"); + $this->assertValidDQL("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone' WHERE u.id = 5"); } public function testDeleteAll() { - $this->assertValidDql('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testDeleteWithCondition() { - $this->assertValidDql('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = 3'); + $this->assertValidDQL('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = 3'); } /** @@ -375,94 +375,94 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase */ public function testImplicitJoinWithCartesianProductAndConditionInWhere() { - $this->assertValidDql("SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.name = a.topic"); + $this->assertValidDQL("SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.name = a.topic"); } public function testAllExpressionWithCorrelatedSubquery() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ALL (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ALL (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); } public function testCustomJoinsAndWithKeywordSupported() { - $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers p WITH p.phonenumber = 123 WHERE u.id = 1'); + $this->assertValidDQL('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers p WITH p.phonenumber = 123 WHERE u.id = 1'); } public function testAnyExpressionWithCorrelatedSubquery() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ANY (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ANY (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); } public function testSomeExpressionWithCorrelatedSubquery() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > SOME (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > SOME (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); } public function testArithmeticExpressionWithoutParenthesisInWhereClause() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) + 1 > 10'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) + 1 > 10'); } public function testMemberOfExpression() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); - //$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE 'Joe' MEMBER OF u.nicknames"); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); + //$this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE 'Joe' MEMBER OF u.nicknames"); } public function testSizeFunction() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) > 1'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) > 1'); } public function testEmptyCollectionComparisonExpression() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY'); } public function testSingleValuedAssociationFieldInWhere() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1'); - $this->assertValidDql('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1'); + $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1'); + $this->assertValidDQL('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1'); } public function testBooleanLiteralInWhere() { - $this->assertValidDql('SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true'); + $this->assertValidDQL('SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true'); } public function testSubqueryInSelectExpression() { - $this->assertValidDql('select u, (select max(p.phonenumber) from Doctrine\Tests\Models\CMS\CmsPhonenumber p) maxId from Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('select u, (select max(p.phonenumber) from Doctrine\Tests\Models\CMS\CmsPhonenumber p) maxId from Doctrine\Tests\Models\CMS\CmsUser u'); } public function testUsageOfQComponentOutsideSubquery() { - $this->assertInvalidDql('select u, (select max(p.phonenumber) from Doctrine\Tests\Models\CMS\CmsPhonenumber p) maxId from Doctrine\Tests\Models\CMS\CmsUser u WHERE p.user = ?1'); + $this->assertInvalidDQL('select u, (select max(p.phonenumber) from Doctrine\Tests\Models\CMS\CmsPhonenumber p) maxId from Doctrine\Tests\Models\CMS\CmsUser u WHERE p.user = ?1'); } public function testUnknownAbstractSchemaName() { - $this->assertInvalidDql('SELECT u FROM UnknownClassName u'); + $this->assertInvalidDQL('SELECT u FROM UnknownClassName u'); } public function testCorrectPartialObjectLoad() { - $this->assertValidDql('SELECT PARTIAL u.{id,name} FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT PARTIAL u.{id,name} FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testIncorrectPartialObjectLoadBecauseOfMissingIdentifier() { - $this->assertInvalidDql('SELECT PARTIAL u.{name} FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertInvalidDQL('SELECT PARTIAL u.{name} FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testScalarExpressionInSelect() { - $this->assertValidDql('SELECT u, 42 + u.id AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT u, 42 + u.id AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testInputParameterInSelect() { - $this->assertValidDql('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); + $this->assertValidDQL('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); } /** @@ -470,7 +470,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase */ public function testDQLKeywordInJoinIsAllowed() { - $this->assertValidDql('SELECT u FROM ' . __NAMESPACE__ . '\DQLKeywordsModelUser u JOIN u.group g'); + $this->assertValidDQL('SELECT u FROM ' . __NAMESPACE__ . '\DQLKeywordsModelUser u JOIN u.group g'); } /** @@ -478,15 +478,23 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase */ public function testDQLKeywordInConditionIsAllowed() { - $this->assertValidDql('SELECT g FROM ' . __NAMESPACE__ . '\DQLKeywordsModelGroup g WHERE g.from=0'); + $this->assertValidDQL('SELECT g FROM ' . __NAMESPACE__ . '\DQLKeywordsModelGroup g WHERE g.from=0'); } /* The exception is currently thrown in the SQLWalker, not earlier. public function testInverseSideSingleValuedAssociationPathNotAllowed() { - $this->assertInvalidDql('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1'); + $this->assertInvalidDQL('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1'); } */ + + /** + * @group DDC-617 + */ + public function testSelectOnlyNonRootEntityAlias() + { + $this->assertInvalidDQL('SELECT g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g'); + } } /** @Entity */ diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 94c41c50d..ecc32fe56 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -325,7 +325,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase public function testSupportsMultipleJoins() { $this->assertSqlGeneration( - 'SELECT u.id, a.id, p, c.id from Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c', + 'SELECT u.id, a.id, p.phonenumber, c.id from Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c', 'SELECT c0_.id AS id0, c1_.id AS id1, c2_.phonenumber AS phonenumber2, c3_.id AS id3 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id INNER JOIN cms_phonenumbers c2_ ON c0_.id = c2_.user_id INNER JOIN cms_comments c3_ ON c1_.id = c3_.article_id' ); } From aa6ac3d6b00edfea0e2cf4cecffad221f7f9d221 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 28 Dec 2010 11:59:51 +0100 Subject: [PATCH 117/119] DDC-945 - Throw exception in ClassMetadataFactory when mapped superclass has to many associations. --- .../ORM/Mapping/ClassMetadataFactory.php | 3 ++ lib/Doctrine/ORM/Mapping/MappingException.php | 4 ++ .../ORM/Mapping/AnnotationDriverTest.php | 42 ++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 174e95eaf..0f0fb17aa 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -382,6 +382,9 @@ class ClassMetadataFactory { foreach ($parentClass->associationMappings as $field => $mapping) { if ($parentClass->isMappedSuperclass) { + if ($mapping['type'] & ClassMetadata::TO_MANY) { + throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field); + } $mapping['sourceEntity'] = $subClass->name; } diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index 15cfc60ff..14bd9ca0b 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -227,4 +227,8 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); } + public static function illegalToManyAssocationOnMappedSuperclass($className, $field) + { + return new self("It is illegal to put a one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'."); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php index c2f3a13c4..5730fcfe4 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php @@ -125,14 +125,30 @@ class AnnotationDriverTest extends AbstractMappingDriverTest $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory(); $factory->setEntityManager($em); - $classPage = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\File'); $classPage = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\File'); $this->assertEquals('Doctrine\Tests\Models\DirectoryTree\File', $classPage->associationMappings['parentDirectory']['sourceEntity']); - $classDirectory = new ClassMetadata('Doctrine\Tests\Models\DirectoryTree\Directory'); $classDirectory = $factory->getMetadataFor('Doctrine\Tests\Models\DirectoryTree\Directory'); $this->assertEquals('Doctrine\Tests\Models\DirectoryTree\Directory', $classDirectory->associationMappings['parentDirectory']['sourceEntity']); } + + /** + * @group DDC-945 + */ + public function testInvalidMappedSuperClassWithManyToManyAssociation() + { + $annotationDriver = $this->_loadDriver(); + + $em = $this->_getTestEntityManager(); + $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); + $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory(); + $factory->setEntityManager($em); + + $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', + "It is illegal to put a one-to-many or many-to-many association on ". + "mapped superclass 'Doctrine\Tests\ORM\Mapping\InvalidMappedSuperClass#users'"); + $usingInvalidMsc = $factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\UsingInvalidMappedSuperClass'); + } } /** @@ -143,3 +159,25 @@ class ColumnWithoutType /** @Id @Column */ public $id; } + +/** + * @MappedSuperclass + */ +class InvalidMappedSuperClass +{ + /** + * @ManyToMany(targetEntity="Doctrine\Tests\Models\CMS\CmsUser") + */ + private $users; +} + +/** + * @Entity + */ +class UsingInvalidMappedSuperClass extends InvalidMappedSuperClass +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + private $id; +} \ No newline at end of file From 1d2b2b2c8b5bee8f0eea6517e06530aeeaaabf2e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 28 Dec 2010 12:18:42 +0100 Subject: [PATCH 118/119] DDC-928 - Fix undefined variable notice. --- lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php b/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php index 14c9ada9a..ecd04ce6d 100644 --- a/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php +++ b/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -188,7 +188,6 @@ class ConvertDoctrine1Schema $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); } else if (isset($column['sequence'])) { $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE); - $metadata->setSequenceGeneratorDefinition($definition); $definition = array( 'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence'] ); @@ -198,6 +197,7 @@ class ConvertDoctrine1Schema if (isset($column['sequence']['value'])) { $definition['initialValue'] = $column['sequence']['value']; } + $metadata->setSequenceGeneratorDefinition($definition); } return $fieldMapping; } From 2d89ddfb1f7d6c3b369995a2c58d5d1b880217ed Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 28 Dec 2010 14:56:13 +0100 Subject: [PATCH 119/119] DDC-837 - Fix bug with associations of the same name not being possible in inheritance hierachies. --- .../AbstractEntityInheritancePersister.php | 33 ++- .../Persisters/JoinedSubclassPersister.php | 20 +- .../ORM/Persisters/SingleTablePersister.php | 10 +- .../ORM/Functional/Ticket/DDC837Test.php | 198 ++++++++++++++++++ 4 files changed, 238 insertions(+), 23 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC837Test.php diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php index 9be6de464..bfe1e60d9 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php @@ -33,11 +33,18 @@ use Doctrine\ORM\Mapping\ClassMetadata, abstract class AbstractEntityInheritancePersister extends BasicEntityPersister { /** - * Map from column names to class names that declare the field the column is mapped to. + * Map from column names to class metadata instances that declare the field the column is mapped to. * * @var array */ - private $_declaringClassMap = array(); + private $declaringClassMap = array(); + + /** + * Map from column names to class names that declare the field the association with join column is mapped to. + * + * @var array + */ + private $declaringJoinColumnMap = array(); /** * {@inheritdoc} @@ -70,8 +77,8 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister unset($sqlResult[$discrColumnName]); foreach ($sqlResult as $column => $value) { $realColumnName = $this->_resultColumnNames[$column]; - if (isset($this->_declaringClassMap[$column])) { - $class = $this->_declaringClassMap[$column]; + if (isset($this->declaringClassMap[$column])) { + $class = $this->declaringClassMap[$column]; if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) { $field = $class->fieldNames[$realColumnName]; if (isset($data[$field])) { @@ -81,6 +88,10 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister ->convertToPHPValue($value, $this->_platform); } } + } else if (isset($this->declaringJoinColumnMap[$column])) { + if ($this->declaringJoinColumnMap[$column] == $entityName || is_subclass_of($entityName, $this->declaringJoinColumnMap[$column])) { + $data[$realColumnName] = $value; + } } else { $data[$realColumnName] = $value; } @@ -99,9 +110,21 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister $columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); if ( ! isset($this->_resultColumnNames[$columnAlias])) { $this->_resultColumnNames[$columnAlias] = $columnName; - $this->_declaringClassMap[$columnAlias] = $class; + $this->declaringClassMap[$columnAlias] = $class; } return "$sql AS $columnAlias"; } + + protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className) + { + $columnAlias = $joinColumnName . $this->_sqlAliasCounter++; + $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); + if ( ! isset($this->_resultColumnNames[$resultColumnName])) { + $this->_resultColumnNames[$resultColumnName] = $joinColumnName; + $this->declaringJoinColumnMap[$resultColumnName] = $className; + } + + return $tableAlias . ".$joinColumnName AS $columnAlias"; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index e9750f8c3..d75656b06 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -263,12 +263,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias; foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { - $columnAlias = $srcColumn . $this->_sqlAliasCounter++; - $columnList .= ", $tableAlias.$srcColumn AS $columnAlias"; - $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); - if ( ! isset($this->_resultColumnNames[$resultColumnName])) { - $this->_resultColumnNames[$resultColumnName] = $srcColumn; - } + if ($columnList != '') $columnList .= ', '; + $columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, + isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name + ); } } } @@ -318,12 +316,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) { foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { - $columnAlias = $srcColumn . $this->_sqlAliasCounter++; - $columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias"; - $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); - if ( ! isset($this->_resultColumnNames[$resultColumnName])) { - $this->_resultColumnNames[$resultColumnName] = $srcColumn; - } + if ($columnList != '') $columnList .= ', '; + $columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, + isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name + ); } } } diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index 856ff3485..0ffa93826 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -63,12 +63,10 @@ class SingleTablePersister extends AbstractEntityInheritancePersister foreach ($subClass->associationMappings as $assoc) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { - $columnAlias = $srcColumn . $this->_sqlAliasCounter++; - $columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias"; - $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); - if ( ! isset($this->_resultColumnNames[$resultColumnName])) { - $this->_resultColumnNames[$resultColumnName] = $srcColumn; - } + if ($columnList != '') $columnList .= ', '; + $columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, + isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name + ); } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC837Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC837Test.php new file mode 100644 index 000000000..6afea88cd --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC837Test.php @@ -0,0 +1,198 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC837Super'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC837Class1'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC837Class2'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC837Class3'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC837Aggregate'), + )); + } + + /** + * @group DDC-837 + */ + public function testIssue() + { + //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + + $c1 = new DDC837Class1(); + $c1->title = "Foo"; + $c1->description = "Foo"; + $aggregate1 = new DDC837Aggregate('test1'); + $c1->aggregate = $aggregate1; + + $c2 = new DDC837Class2(); + $c2->title = "Bar"; + $c2->description = "Bar"; + $c2->text = "Bar"; + $aggregate2 = new DDC837Aggregate('test2'); + $c2->aggregate = $aggregate2; + + $c3 = new DDC837Class3(); + $c3->apples = "Baz"; + $c3->bananas = "Baz"; + + $this->_em->persist($c1); + $this->_em->persist($aggregate1); + $this->_em->persist($c2); + $this->_em->persist($aggregate2); + $this->_em->persist($c3); + $this->_em->flush(); + $this->_em->clear(); + + // Test Class1 + $e1 = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC837Super', $c1->id); + + $this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC837Class1', $e1); + $this->assertEquals('Foo', $e1->title); + $this->assertEquals('Foo', $e1->description); + $this->assertType(__NAMESPACE__ . '\DDC837Aggregate', $e1->aggregate); + $this->assertEquals('test1', $e1->aggregate->getSysname()); + + // Test Class 2 + $e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC837Super', $c2->id); + + $this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC837Class2', $e2); + $this->assertEquals('Bar', $e2->title); + $this->assertEquals('Bar', $e2->description); + $this->assertEquals('Bar', $e2->text); + $this->assertType(__NAMESPACE__ . '\DDC837Aggregate', $e2->aggregate); + $this->assertEquals('test2', $e2->aggregate->getSysname()); + + $all = $this->_em->getRepository(__NAMESPACE__.'\DDC837Super')->findAll(); + + foreach ($all as $obj) { + if ($obj instanceof DDC837Class1) { + $this->assertEquals('Foo', $obj->title); + $this->assertEquals('Foo', $obj->description); + } else if ($obj instanceof DDC837Class2) { + $this->assertTrue($e2 === $obj); + $this->assertEquals('Bar', $obj->title); + $this->assertEquals('Bar', $obj->description); + $this->assertEquals('Bar', $obj->text); + } else if ($obj instanceof DDC837Class3) { + $this->assertEquals('Baz', $obj->apples); + $this->assertEquals('Baz', $obj->bananas); + } else { + $this->fail('Instance of DDC837Class1, DDC837Class2 or DDC837Class3 expected.'); + } + } + } +} + +/** + * @Entity + * @Table(name="DDC837Super") + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="type", type="string") + * @DiscriminatorMap({"class1" = "DDC837Class1", "class2" = "DDC837Class2", "class3"="DDC837Class3"}) + */ +abstract class DDC837Super +{ + /** + * @Id @Column(name="id", type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $id; +} + +/** + * @Entity + */ +class DDC837Class1 extends DDC837Super +{ + /** + * @Column(name="title", type="string", length="150") + */ + public $title; + + /** + * @Column(name="content", type="string", length="500") + */ + public $description; + + /** + * @OneToOne(targetEntity="DDC837Aggregate") + */ + public $aggregate; +} + +/** + * @Entity + */ +class DDC837Class2 extends DDC837Super +{ + /** + * @Column(name="title", type="string", length="150") + */ + public $title; + + /** + * @Column(name="content", type="string", length="500") + */ + public $description; + + /** + * @Column(name="text", type="text") + */ + public $text; + + /** + * @OneToOne(targetEntity="DDC837Aggregate") + */ + public $aggregate; +} + +/** + * An extra class to demonstrate why title and description aren't in Super + * + * @Entity + */ +class DDC837Class3 extends DDC837Super +{ + /** + * @Column(name="title", type="string", length="150") + */ + public $apples; + + /** + * @Column(name="content", type="string", length="500") + */ + public $bananas; +} + +/** + * @Entity + */ +class DDC837Aggregate +{ + /** + * @Id @Column(name="id", type="integer") + * @GeneratedValue + */ + public $id; + + /** + * @Column(name="sysname", type="string") + */ + protected $sysname; + + public function __construct($sysname) + { + $this->sysname = $sysname; + } + + public function getSysname() + { + return $this->sysname; + } +} \ No newline at end of file