Skip to content

More intuitive matching of bundle directory lookups for Doctrine extensions #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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(),
)));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\Vendor\AnnotationsBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AnnotationsBundle extends Bundle
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\Vendor\AnnotationsBundle\Entity;

class Test
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,20 +188,19 @@ protected function loadDocumentManagerBundlesMappingInformation(array $documentM
// configure metadata driver for each bundle based on the type of mapping files found
$mappingDriverDef = new Definition('%doctrine.odm.mongodb.metadata.driver_chain_class%');
$bundleDocumentMappings = 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.'/Document')) {
if (is_dir($dir = $bundleDir.'/'.$class.'/Document')) {
if ($type === null) {
$type = 'annotation';
}
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
<div class="count"><?php echo $data->getQueryCount() ?></div>
<img style="margin: 0 5px 0 0; vertical-align: middle; width: 32px" width="32" height="32" alt="Mongo" src="<?php echo $view->get('assets')->getUrl('bundles/webprofiler/images/db.png') ?>" />
Doctrine MongoDB
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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(),
)));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace DoctrineMongoDBBundle\Tests\DependencyInjection\Fixtures\Bundles\Vendor\AnnotationsBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AnnotationsBundle extends Bundle
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace DoctrineMongoDBBundle\Tests\DependencyInjection\Fixtures\Bundles\Vendor\AnnotationsBundle\Document;

class Test
{
}
4 changes: 4 additions & 0 deletions src/Symfony/Component/HttpFoundation/UniversalClassLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ public function register()
*/
public function loadClass($class)
{
if ('\\' === $class[0]) {
$class = substr($class, 1);
}

if (false !== ($pos = strripos($class, '\\'))) {
// namespaced class name
$namespace = substr($class, 0, $pos);
Expand Down
14 changes: 8 additions & 6 deletions src/Symfony/Component/Validator/Constraints/ValidValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,23 @@ public function isValid($value, Constraint $constraint)
$propertyPath = $this->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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Namespaced;

class Bar {
public static $loaded = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Namespaced;

class Foo {
public static $loaded = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

class Pearlike_Bar {
public static $loaded = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

class Pearlike_Foo {
public static $loaded = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* 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'),
);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down