Skip to content

Commit cd4a4bd

Browse files
committed
bug #35591 [Validator] do not merge constraints within interfaces (greedyivan)
This PR was merged into the 3.4 branch. Discussion ---------- [Validator] do not merge constraints within interfaces | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #22538 | License | MIT | Doc PR | This fix disables merge constraints within interfaces. There is no reason to merge constraints from one interface to another because each class merges the constraints of all its interfaces. Only one check is needed is to eliminate all interfaces that comes from parent class to avoid duplication. Commits ------- 67f336b do not merge constraints within interfaces
2 parents db733da + 67f336b commit cd4a4bd

File tree

6 files changed

+74
-20
lines changed

6 files changed

+74
-20
lines changed

src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php

+10-19
Original file line numberDiff line numberDiff line change
@@ -117,34 +117,25 @@ public function getMetadataFor($value)
117117

118118
private function mergeConstraints(ClassMetadata $metadata)
119119
{
120+
if ($metadata->getReflectionClass()->isInterface()) {
121+
return;
122+
}
123+
120124
// Include constraints from the parent class
121125
if ($parent = $metadata->getReflectionClass()->getParentClass()) {
122126
$metadata->mergeConstraints($this->getMetadataFor($parent->name));
123127
}
124128

125-
$interfaces = $metadata->getReflectionClass()->getInterfaces();
126-
127-
$interfaces = array_filter($interfaces, function ($interface) use ($parent, $interfaces) {
128-
$interfaceName = $interface->getName();
129-
130-
if ($parent && $parent->implementsInterface($interfaceName)) {
131-
return false;
132-
}
133-
134-
foreach ($interfaces as $i) {
135-
if ($i !== $interface && $i->implementsInterface($interfaceName)) {
136-
return false;
137-
}
138-
}
139-
140-
return true;
141-
});
142-
143129
// Include constraints from all directly implemented interfaces
144-
foreach ($interfaces as $interface) {
130+
foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
145131
if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->name) {
146132
continue;
147133
}
134+
135+
if ($parent && \in_array($interface->getName(), $parent->getInterfaceNames(), true)) {
136+
continue;
137+
}
138+
148139
$metadata->mergeConstraints($this->getMetadataFor($interface->name));
149140
}
150141
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symfony\Component\Validator\Tests\Fixtures;
4+
5+
abstract class AbstractPropertyGetter implements PropertyGetterInterface
6+
{
7+
private $property;
8+
9+
public function getProperty()
10+
{
11+
return $this->property;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\Validator\Tests\Fixtures;
4+
5+
interface ChildGetterInterface extends PropertyGetterInterface
6+
{
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Symfony\Component\Validator\Tests\Fixtures;
4+
5+
/**
6+
* This class has two paths to PropertyGetterInterface:
7+
* PropertyGetterInterface <- AbstractPropertyGetter <- PropertyGetter
8+
* PropertyGetterInterface <- ChildGetterInterface <- PropertyGetter
9+
*/
10+
class PropertyGetter extends AbstractPropertyGetter implements ChildGetterInterface
11+
{
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Component\Validator\Tests\Fixtures;
4+
5+
interface PropertyGetterInterface
6+
{
7+
public function getProperty();
8+
}

src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php

+24-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1616
use Symfony\Component\Validator\Constraints\Callback;
17+
use Symfony\Component\Validator\Constraints\NotBlank;
1718
use Symfony\Component\Validator\Mapping\Cache\Psr6Cache;
1819
use Symfony\Component\Validator\Mapping\ClassMetadata;
1920
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
2021
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
2122
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
23+
use Symfony\Component\Validator\Tests\Fixtures\PropertyGetter;
24+
use Symfony\Component\Validator\Tests\Fixtures\PropertyGetterInterface;
2225

2326
class LazyLoadingMetadataFactoryTest extends TestCase
2427
{
@@ -70,7 +73,6 @@ public function testMergeParentConstraints()
7073
new ConstraintA(['groups' => [
7174
'Default',
7275
'EntityParentInterface',
73-
'EntityInterfaceB',
7476
'Entity',
7577
]]),
7678
];
@@ -186,6 +188,15 @@ public function testGroupsFromParent()
186188
$this->assertContains('EntityStaticCar', $groups);
187189
$this->assertContains('EntityStaticVehicle', $groups);
188190
}
191+
192+
public function testMultipathInterfaceConstraint()
193+
{
194+
$factory = new LazyLoadingMetadataFactory(new PropertyGetterInterfaceConstraintLoader());
195+
$metadata = $factory->getMetadataFor(PropertyGetter::class);
196+
$constraints = $metadata->getPropertyMetadata('property');
197+
198+
$this->assertCount(1, $constraints);
199+
}
189200
}
190201

191202
class TestLoader implements LoaderInterface
@@ -195,3 +206,15 @@ public function loadClassMetadata(ClassMetadata $metadata)
195206
$metadata->addConstraint(new ConstraintA());
196207
}
197208
}
209+
210+
class PropertyGetterInterfaceConstraintLoader implements LoaderInterface
211+
{
212+
public function loadClassMetadata(ClassMetadata $metadata)
213+
{
214+
if (PropertyGetterInterface::class === $metadata->getClassName()) {
215+
$metadata->addGetterConstraint('property', new NotBlank());
216+
}
217+
218+
return true;
219+
}
220+
}

0 commit comments

Comments
 (0)