diff --git a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php index 3cf4d8ecb57ab..d9d697b04e8f1 100644 --- a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php +++ b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php @@ -328,20 +328,19 @@ protected function loadOrmEntityManagerBundlesMappingInformation(array $entityMa // configure metadata driver for each bundle based on the type of mapping files found $mappingDriverDef = new Definition('%doctrine.orm.metadata.driver_chain_class%'); $bundleEntityMappings = array(); - $bundleDirs = $container->getParameter('kernel.bundle_dirs'); $aliasMap = array(); foreach ($container->getParameter('kernel.bundles') as $className) { $tmp = dirname(str_replace('\\', '/', $className)); $namespace = str_replace('/', '\\', dirname($tmp)); $class = basename($tmp); - if (!isset($bundleDirs[$namespace])) { + if (! $bundleDir = $this->findBundleDirForNamespace($namespace, $container)) { continue; } - $type = $this->detectMetadataDriver($bundleDirs[$namespace].'/'.$class, $container); + $type = $this->detectMetadataDriver($bundleDir.'/'.$class, $container); - if (is_dir($dir = $bundleDirs[$namespace].'/'.$class.'/Entity')) { + if (is_dir($dir = $bundleDir.'/'.$class.'/Entity')) { if ($type === null) { $type = 'annotation'; } @@ -443,6 +442,31 @@ protected function getEntityManagerCacheDefinition(array $entityManager, $cacheD return $cacheDef; } + /** + * Finds the bundle directory for a namespace. + * + * If the namespace does not yield a direct match, this method will attempt + * to match parent namespaces exhaustively. + * + * @param string $namespace A bundle namespace omitting the bundle name part + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @return string|false The bundle directory if found, false otherwise + */ + protected function findBundleDirForNamespace($namespace, ContainerBuilder $container) + { + $bundleDirs = $container->getParameter('kernel.bundle_dirs'); + $segment = $namespace; + + do { + if (isset($bundleDirs[$segment])) { + return $bundleDirs[$segment] . str_replace('\\', '/', substr($namespace, strlen($segment))); + } + } while ($segment = substr($segment, 0, ($pos = strrpos($segment, '\\')))); + + return false; + } + /** * Finds existing bundle subpaths. * diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php index 99894957c55ce..bc995866b3fbc 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php @@ -385,6 +385,24 @@ public function testAnnotationsBundleMappingDetection() $this->assertEquals('DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\AnnotationsBundle\Entity', $calls[0][1][1]); } + public function testAnnotationsBundleMappingDetectionWithVendorNamespace() + { + $container = $this->getContainer('AnnotationsBundle', 'Vendor'); + $loader = new DoctrineExtension(); + + $loader->dbalLoad(array(), $container); + $loader->ormLoad(array(), $container); + + $this->assertEquals(array(), $container->getParameter('doctrine.orm.metadata_driver.mapping_dirs')); + $this->assertEquals('%doctrine.orm.metadata_driver.mapping_dirs%', $container->getParameter('doctrine.orm.xml_mapping_dirs')); + $this->assertEquals('%doctrine.orm.metadata_driver.mapping_dirs%', $container->getParameter('doctrine.orm.yml_mapping_dirs')); + $this->assertEquals(array(__DIR__.'/Fixtures/Bundles/Vendor/AnnotationsBundle/Entity'), $container->getParameter('doctrine.orm.metadata_driver.entity_dirs')); + + $calls = $container->getDefinition('doctrine.orm.metadata_driver')->getMethodCalls(); + $this->assertEquals('doctrine.orm.metadata_driver.annotation', (string) $calls[0][1][0]); + $this->assertEquals('DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\Vendor\AnnotationsBundle\Entity', $calls[0][1][1]); + } + public function testEntityManagerMetadataCacheDriverConfiguration() { $container = $this->getContainer(); @@ -442,13 +460,13 @@ public function testDependencyInjectionImportsOverrideDefaults() $this->assertTrue($container->getParameter('doctrine.orm.auto_generate_proxy_classes')); } - protected function getContainer($bundle = 'YamlBundle') + protected function getContainer($bundle = 'YamlBundle', $vendor = null) { - require_once __DIR__.'/Fixtures/Bundles/'.$bundle.'/'.$bundle.'.php'; + require_once __DIR__.'/Fixtures/Bundles/'.($vendor ? $vendor.'/' : '').$bundle.'/'.$bundle.'.php'; return new ContainerBuilder(new ParameterBag(array( 'kernel.bundle_dirs' => array('DoctrineBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles' => __DIR__.'/Fixtures/Bundles'), - 'kernel.bundles' => array('DoctrineBundle\\Tests\DependencyInjection\\Fixtures\\Bundles\\'.$bundle.'\\'.$bundle), + 'kernel.bundles' => array('DoctrineBundle\\Tests\DependencyInjection\\Fixtures\\Bundles\\'.($vendor ? $vendor.'\\' : '').$bundle.'\\'.$bundle), 'kernel.cache_dir' => sys_get_temp_dir(), ))); } diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php new file mode 100644 index 0000000000000..53c3293c58c3d --- /dev/null +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php @@ -0,0 +1,9 @@ +getParameter('kernel.bundle_dirs'); $aliasMap = array(); foreach ($container->getParameter('kernel.bundles') as $className) { $tmp = dirname(str_replace('\\', '/', $className)); $namespace = str_replace('/', '\\', dirname($tmp)); $class = basename($tmp); - if (!isset($bundleDirs[$namespace])) { + if (! $bundleDir = $this->findBundleDirForNamespace($namespace, $container)) { continue; } - $type = $this->detectMetadataDriver($bundleDirs[$namespace].'/'.$class, $container); + $type = $this->detectMetadataDriver($bundleDir.'/'.$class, $container); - if (is_dir($dir = $bundleDirs[$namespace].'/'.$class.'/Document')) { + if (is_dir($dir = $bundleDir.'/'.$class.'/Document')) { if ($type === null) { $type = 'annotation'; } @@ -294,6 +293,31 @@ protected function getConnections(array $config, ContainerBuilder $container) return $connections; } + /** + * Finds the bundle directory for a namespace. + * + * If the namespace does not yield a direct match, this method will attempt + * to match parent namespaces exhaustively. + * + * @param string $namespace A bundle namespace omitting the bundle name part + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @return string|false The bundle directory if found, false otherwise + */ + protected function findBundleDirForNamespace($namespace, ContainerBuilder $container) + { + $bundleDirs = $container->getParameter('kernel.bundle_dirs'); + $segment = $namespace; + + do { + if (isset($bundleDirs[$segment])) { + return $bundleDirs[$segment] . str_replace('\\', '/', substr($namespace, strlen($segment))); + } + } while ($segment = substr($segment, 0, ($pos = strrpos($segment, '\\')))); + + return false; + } + /** * Finds existing bundle subpaths. * diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Resources/views/Profiler/mongodb_menu.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Resources/views/Profiler/mongodb_menu.php index 346740c9802ef..1e6e3653ee553 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Resources/views/Profiler/mongodb_menu.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Resources/views/Profiler/mongodb_menu.php @@ -1,2 +1,3 @@
getQueryCount() ?>
Mongo +Doctrine MongoDB diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php index ab6189a7ae472..9860733347672 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php @@ -259,6 +259,23 @@ public function testAnnotationsBundleMappingDetection() $this->assertEquals('DoctrineMongoDBBundle\Tests\DependencyInjection\Fixtures\Bundles\AnnotationsBundle\Document', $calls[0][1][1]); } + public function testAnnotationsBundleMappingDetectionWithVendorNamespace() + { + $container = $this->getContainer('AnnotationsBundle', 'Vendor'); + $loader = new DoctrineMongoDBExtension(); + + $loader->mongodbLoad(array(), $container); + + $this->assertEquals(array(), $container->getParameter('doctrine.odm.mongodb.mapping_dirs')); + $this->assertEquals('%doctrine.odm.mongodb.mapping_dirs%', $container->getParameter('doctrine.odm.mongodb.xml_mapping_dirs')); + $this->assertEquals('%doctrine.odm.mongodb.mapping_dirs%', $container->getParameter('doctrine.odm.mongodb.yml_mapping_dirs')); + $this->assertEquals(array(__DIR__.'/Fixtures/Bundles/Vendor/AnnotationsBundle/Document'), $container->getParameter('doctrine.odm.mongodb.document_dirs')); + + $calls = $container->getDefinition('doctrine.odm.mongodb.metadata')->getMethodCalls(); + $this->assertEquals('doctrine.odm.mongodb.metadata.annotation', (string) $calls[0][1][0]); + $this->assertEquals('DoctrineMongoDBBundle\Tests\DependencyInjection\Fixtures\Bundles\Vendor\AnnotationsBundle\Document', $calls[0][1][1]); + } + public function testDocumentManagerMetadataCacheDriverConfiguration() { $container = $this->getContainer(); @@ -316,13 +333,13 @@ public function testDependencyInjectionImportsOverrideDefaults() $this->assertTrue($container->getParameter('doctrine.odm.mongodb.auto_generate_proxy_classes')); } - protected function getContainer($bundle = 'YamlBundle') + protected function getContainer($bundle = 'YamlBundle', $vendor = null) { - require_once __DIR__.'/Fixtures/Bundles/'.$bundle.'/'.$bundle.'.php'; + require_once __DIR__.'/Fixtures/Bundles/'.($vendor ? $vendor.'/' : '').$bundle.'/'.$bundle.'.php'; return new ContainerBuilder(new ParameterBag(array( 'kernel.bundle_dirs' => array('DoctrineMongoDBBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles' => __DIR__.'/Fixtures/Bundles'), - 'kernel.bundles' => array('DoctrineMongoDBBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles\\'.$bundle.'\\'.$bundle), + 'kernel.bundles' => array('DoctrineMongoDBBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles\\'.($vendor ? $vendor.'\\' : '').$bundle.'\\'.$bundle), 'kernel.cache_dir' => sys_get_temp_dir(), ))); } diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php new file mode 100644 index 0000000000000..c64c8afdc2a69 --- /dev/null +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php @@ -0,0 +1,9 @@ +context->getPropertyPath(); $factory = $this->context->getClassMetadataFactory(); - if (is_array($value)) { - foreach ($value as $key => $element) { - $walker->walkConstraint($constraint, $element, $group, $propertyPath.'['.$key.']'); - } - } else if (!is_object($value)) { + if (!is_array($value) && !is_object($value)) { throw new UnexpectedTypeException($value, 'object or array'); } else if ($constraint->class && !$value instanceof $constraint->class) { $this->setMessage($constraint->message, array('class' => $constraint->class)); return false; - } else { + } else if (!is_array($value)) { $metadata = $factory->getClassMetadata(get_class($value)); $walker->walkClass($metadata, $value, $group, $propertyPath); } + if (is_array($value) || $value instanceof \Traversable) { + foreach ($value as $key => $element) { + $walker->walkConstraint($constraint, $element, $group, $propertyPath.'['.$key.']'); + } + } + return true; } } \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Fixtures/Namespaced/Bar.php b/tests/Symfony/Tests/Component/HttpFoundation/Fixtures/Namespaced/Bar.php new file mode 100644 index 0000000000000..c16eef7b080cc --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/Fixtures/Namespaced/Bar.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\UniversalClassLoader; + +class UniversalClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\HttpFoundation\UniversalClassLoader::loadClass + * @dataProvider testClassProvider + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerNamespace('Namespaced', __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures'); + $loader->registerPrefix('Pearlike_', __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures'); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public static function testClassProvider() + { + return array( + array('\\Namespaced\\Foo', 'Namespaced\\Foo', '->loadClass() loads Namespaced\Foo class'), + array('\\Pearlike_Foo', 'Pearlike_Foo', '->loadClass() loads Pearlike_Foo class'), + array('\\Namespaced\\Bar', '\\Namespaced\\Bar', '->loadClass() loads Namespaced\Bar class with a leading slash'), + array('\\Pearlike_Bar', '\\Pearlike_Bar', '->loadClass() loads Pearlike_Bar class with a leading slash'), + ); + } +} + diff --git a/tests/Symfony/Tests/Component/Validator/Constraints/ValidValidatorTest.php b/tests/Symfony/Tests/Component/Validator/Constraints/ValidValidatorTest.php index 788247d6e924d..17e971a6d334b 100644 --- a/tests/Symfony/Tests/Component/Validator/Constraints/ValidValidatorTest.php +++ b/tests/Symfony/Tests/Component/Validator/Constraints/ValidValidatorTest.php @@ -79,6 +79,35 @@ public function testWalkArray() $this->assertTrue($this->validator->isValid($array, $constraint)); } + public function testWalkTraversable() + { + $this->context->setGroup('MyGroup'); + $this->context->setPropertyPath('foo'); + + $constraint = new Valid(); + $metadata = $this->createClassMetadata(); + $entity = new Entity(); + // can only test for one object due to PHPUnit's mocking limitations + $traversable = new \ArrayObject( array('key' => $entity)); + + $this->walker->expects($this->once()) + ->method('walkConstraint') + ->with($this->equalTo($constraint), $this->equalTo($entity), 'MyGroup', 'foo[key]'); + + $this->factory->expects($this->once()) + ->method('getClassMetadata') + ->with($this->equalTo(get_class($traversable))) + ->will($this->returnValue($metadata)); + + + $this->walker->expects($this->once()) + ->method('walkClass') + ->with($this->equalTo($metadata), $this->equalTo($traversable), 'MyGroup', 'foo'); + + + $this->assertTrue($this->validator->isValid($traversable, $constraint)); + } + public function testValidateClass_Succeeds() { $metadata = $this->createClassMetadata();