Skip to content

Commit 254a442

Browse files
bug symfony#54760 [Validator] handle union and intersection types for cascaded validations (xabbuh)
This PR was merged into the 5.4 branch. Discussion ---------- [Validator] handle union and intersection types for cascaded validations | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix symfony#49629, Fix symfony#53735 | License | MIT Commits ------- 18e06a8 handle union and intersection types for cascaded validations
2 parents 2eaf70f + 18e06a8 commit 254a442

File tree

4 files changed

+108
-1
lines changed

4 files changed

+108
-1
lines changed

src/Symfony/Component/Validator/Mapping/ClassMetadata.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public function addConstraint(Constraint $constraint)
210210
$this->cascadingStrategy = CascadingStrategy::CASCADE;
211211

212212
foreach ($this->getReflectionClass()->getProperties() as $property) {
213-
if ($property->hasType() && (('array' === $type = $property->getType()->getName()) || class_exists($type))) {
213+
if ($this->canCascade($property->getType())) {
214214
$this->addPropertyConstraint($property->getName(), new Valid());
215215
}
216216
}
@@ -511,4 +511,33 @@ private function checkConstraint(Constraint $constraint)
511511
}
512512
}
513513
}
514+
515+
private function canCascade(?\ReflectionType $type = null): bool
516+
{
517+
if (null === $type) {
518+
return false;
519+
}
520+
521+
if ($type instanceof \ReflectionIntersectionType) {
522+
foreach ($type->getTypes() as $nestedType) {
523+
if ($this->canCascade($nestedType)) {
524+
return true;
525+
}
526+
}
527+
528+
return false;
529+
}
530+
531+
if ($type instanceof \ReflectionUnionType) {
532+
foreach ($type->getTypes() as $nestedType) {
533+
if (!$this->canCascade($nestedType)) {
534+
return false;
535+
}
536+
}
537+
538+
return true;
539+
}
540+
541+
return $type instanceof \ReflectionNamedType && (\in_array($type->getName(), ['array', 'null'], true) || class_exists($type->getName()));
542+
}
514543
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests\Fixtures;
13+
14+
class CascadingEntityIntersection
15+
{
16+
public CascadedChild&\stdClass $classes;
17+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests\Fixtures;
13+
14+
class CascadingEntityUnion
15+
{
16+
public CascadedChild|\stdClass $classes;
17+
public CascadedChild|array $classAndArray;
18+
public CascadedChild|null $classAndNull;
19+
public array|null $arrayAndNull;
20+
public CascadedChild|array|null $classAndArrayAndNull;
21+
public int|string $scalars;
22+
public int|null $scalarAndNull;
23+
public CascadedChild|int $classAndScalar;
24+
public array|int $arrayAndScalar;
25+
}

src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use Symfony\Component\Validator\Tests\Fixtures\Annotation\EntityParent;
2626
use Symfony\Component\Validator\Tests\Fixtures\Annotation\GroupSequenceProviderEntity;
2727
use Symfony\Component\Validator\Tests\Fixtures\CascadingEntity;
28+
use Symfony\Component\Validator\Tests\Fixtures\CascadingEntityIntersection;
29+
use Symfony\Component\Validator\Tests\Fixtures\CascadingEntityUnion;
2830
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
2931
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
3032
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
@@ -361,6 +363,40 @@ public function testCascadeConstraint()
361363
'children',
362364
], $metadata->getConstrainedProperties());
363365
}
366+
367+
/**
368+
* @requires PHP 8.0
369+
*/
370+
public function testCascadeConstraintWithUnionTypeProperties()
371+
{
372+
$metadata = new ClassMetadata(CascadingEntityUnion::class);
373+
$metadata->addConstraint(new Cascade());
374+
375+
$this->assertSame(CascadingStrategy::CASCADE, $metadata->getCascadingStrategy());
376+
$this->assertCount(5, $metadata->properties);
377+
$this->assertSame([
378+
'classes',
379+
'classAndArray',
380+
'classAndNull',
381+
'arrayAndNull',
382+
'classAndArrayAndNull',
383+
], $metadata->getConstrainedProperties());
384+
}
385+
386+
/**
387+
* @requires PHP 8.1
388+
*/
389+
public function testCascadeConstraintWithIntersectionTypeProperties()
390+
{
391+
$metadata = new ClassMetadata(CascadingEntityIntersection::class);
392+
$metadata->addConstraint(new Cascade());
393+
394+
$this->assertSame(CascadingStrategy::CASCADE, $metadata->getCascadingStrategy());
395+
$this->assertCount(1, $metadata->properties);
396+
$this->assertSame([
397+
'classes',
398+
], $metadata->getConstrainedProperties());
399+
}
364400
}
365401

366402
class ClassCompositeConstraint extends Composite

0 commit comments

Comments
 (0)