diff --git a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php index b5b202de6d..9987b1ca1e 100644 --- a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -116,9 +116,27 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface $metadata->mergeConstraints($this->getMetadataFor($parent->name)); } - // Include constraints from all implemented interfaces that have not been processed via parent class yet - foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) { - if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->name || ($parent && $parent->implementsInterface($interface->name))) { + $interfaces = $metadata->getReflectionClass()->getInterfaces(); + + $interfaces = array_filter($interfaces, function ($interface) use ($parent, $interfaces) { + $interfaceName = $interface->getName(); + + if ($parent && $parent->implementsInterface($interfaceName)) { + return false; + } + + foreach ($interfaces as $i) { + if ($i !== $interface && $i->implementsInterface($interfaceName)) { + return false; + } + } + + return true; + }); + + // Include constraints from all directly implemented interfaces + foreach ($interfaces as $interface) { + if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->name) { continue; } $metadata->mergeConstraints($this->getMetadataFor($interface->name)); diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php index e4eec6be32..77a86108a1 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php @@ -19,7 +19,7 @@ use Symfony\Component\Validator\ExecutionContextInterface; * @Assert\GroupSequence({"Foo", "Entity"}) * @Assert\Callback({"Symfony\Component\Validator\Tests\Fixtures\CallbackClass", "callback"}) */ -class Entity extends EntityParent +class Entity extends EntityParent implements EntityInterfaceB { /** * @Assert\NotNull diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterface.php b/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterfaceA.php similarity index 91% rename from src/Symfony/Component/Validator/Tests/Fixtures/EntityInterface.php rename to src/Symfony/Component/Validator/Tests/Fixtures/EntityInterfaceA.php index 2901f26386..a0afcf8163 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterface.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterfaceA.php @@ -11,6 +11,6 @@ namespace Symfony\Component\Validator\Tests\Fixtures; -interface EntityInterface +interface EntityInterfaceA { } diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterfaceB.php b/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterfaceB.php new file mode 100644 index 0000000000..93b389414f --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/EntityInterfaceB.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +interface EntityInterfaceB extends EntityParentInterface +{ +} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/EntityParent.php b/src/Symfony/Component/Validator/Tests/Fixtures/EntityParent.php index acbec3d32e..4674f8b35a 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/EntityParent.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/EntityParent.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Validator\Tests\Fixtures; use Symfony\Component\Validator\Constraints\NotNull; -class EntityParent implements EntityInterface +class EntityParent implements EntityInterfaceA { protected $firstName; private $internal; diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/EntityParentInterface.php b/src/Symfony/Component/Validator/Tests/Fixtures/EntityParentInterface.php new file mode 100644 index 0000000000..3aad6fec5f --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/EntityParentInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +interface EntityParentInterface +{ +} diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index c78d2a72c8..c1aaa9f8c7 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -18,17 +18,19 @@ use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase { - const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; - const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; - const INTERFACECLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterface'; + const CLASS_NAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; + const PARENT_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; + const INTERFACE_A_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceA'; + const INTERFACE_B_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceB'; + const PARENT_INTERFACE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParentInterface'; public function testLoadClassMetadataWithInterface() { $factory = new LazyLoadingMetadataFactory(new TestLoader()); - $metadata = $factory->getMetadataFor(self::PARENTCLASS); + $metadata = $factory->getMetadataFor(self::PARENT_CLASS); $constraints = array( - new ConstraintA(array('groups' => array('Default', 'EntityInterface', 'EntityParent'))), + new ConstraintA(array('groups' => array('Default', 'EntityInterfaceA', 'EntityParent'))), new ConstraintA(array('groups' => array('Default', 'EntityParent'))), ); @@ -38,12 +40,12 @@ class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase public function testMergeParentConstraints() { $factory = new LazyLoadingMetadataFactory(new TestLoader()); - $metadata = $factory->getMetadataFor(self::CLASSNAME); + $metadata = $factory->getMetadataFor(self::CLASS_NAME); $constraints = array( new ConstraintA(array('groups' => array( 'Default', - 'EntityInterface', + 'EntityInterfaceA', 'EntityParent', 'Entity', ))), @@ -52,6 +54,17 @@ class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase 'EntityParent', 'Entity', ))), + new ConstraintA(array('groups' => array( + 'Default', + 'EntityParentInterface', + 'EntityInterfaceB', + 'Entity', + ))), + new ConstraintA(array('groups' => array( + 'Default', + 'EntityInterfaceB', + 'Entity', + ))), new ConstraintA(array('groups' => array( 'Default', 'Entity', @@ -67,34 +80,36 @@ class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase $factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache); $parentClassConstraints = array( - new ConstraintA(array('groups' => array('Default', 'EntityInterface', 'EntityParent'))), + new ConstraintA(array('groups' => array('Default', 'EntityInterfaceA', 'EntityParent'))), new ConstraintA(array('groups' => array('Default', 'EntityParent'))), ); - $interfaceConstraints = array(new ConstraintA(array('groups' => array('Default', 'EntityInterface')))); + $interfaceAConstraints = array( + new ConstraintA(array('groups' => array('Default', 'EntityInterfaceA'))), + ); $cache->expects($this->never()) ->method('has'); $cache->expects($this->exactly(2)) ->method('read') ->withConsecutive( - array($this->equalTo(self::PARENTCLASS)), - array($this->equalTo(self::INTERFACECLASS)) + array($this->equalTo(self::PARENT_CLASS)), + array($this->equalTo(self::INTERFACE_A_CLASS)) ) ->will($this->returnValue(false)); $cache->expects($this->exactly(2)) ->method('write') ->withConsecutive( - $this->callback(function ($metadata) use ($interfaceConstraints) { - return $interfaceConstraints == $metadata->getConstraints(); + $this->callback(function ($metadata) use ($interfaceAConstraints) { + return $interfaceAConstraints == $metadata->getConstraints(); }), $this->callback(function ($metadata) use ($parentClassConstraints) { return $parentClassConstraints == $metadata->getConstraints(); }) ); - $metadata = $factory->getMetadataFor(self::PARENTCLASS); + $metadata = $factory->getMetadataFor(self::PARENT_CLASS); - $this->assertEquals(self::PARENTCLASS, $metadata->getClassName()); + $this->assertEquals(self::PARENT_CLASS, $metadata->getClassName()); $this->assertEquals($parentClassConstraints, $metadata->getConstraints()); } @@ -104,7 +119,7 @@ class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase $cache = $this->getMock('Symfony\Component\Validator\Mapping\Cache\CacheInterface'); $factory = new LazyLoadingMetadataFactory($loader, $cache); - $metadata = new ClassMetadata(self::PARENTCLASS); + $metadata = new ClassMetadata(self::PARENT_CLASS); $metadata->addConstraint(new ConstraintA()); $loader->expects($this->never()) @@ -116,7 +131,7 @@ class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase ->method('read') ->will($this->returnValue($metadata)); - $this->assertEquals($metadata, $factory->getMetadataFor(self::PARENTCLASS)); + $this->assertEquals($metadata, $factory->getMetadataFor(self::PARENT_CLASS)); } }