Skip to content

Commit 3b6c091

Browse files
committed
[Validator] Constraints as php 8 Attributes.
1 parent 9e4f511 commit 3b6c091

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+611
-149
lines changed

.github/patch-types.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
case false !== strpos($file, '/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures'):
3737
case false !== strpos($file, '/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectOuter.php'):
3838
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/LotsOfAttributes.php'):
39+
case false !== strpos($file, '/src/Symfony/Component/Validator/Tests/Fixtures/Attribute/'):
3940
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/MyAttribute.php'):
4041
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php'):
4142
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/Php74.php') && \PHP_VERSION_ID < 70400:

src/Symfony/Component/Validator/Constraint.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ public static function getErrorName($errorCode)
9191
* getRequiredOptions() to return the names of these options. If any
9292
* option is not set here, an exception is thrown.
9393
*
94-
* @param mixed $options The options (as associative array)
95-
* or the value for the default
96-
* option (any other type)
94+
* @param mixed $options The options (as associative array)
95+
* or the value for the default
96+
* option (any other type)
97+
* @param string[] $groups An array of validation groups
98+
* @param mixed $payload Domain-specific data attached to a constraint
9799
*
98100
* @throws InvalidOptionsException When you pass the names of non-existing
99101
* options
@@ -103,9 +105,15 @@ public static function getErrorName($errorCode)
103105
* array, but getDefaultOption() returns
104106
* null
105107
*/
106-
public function __construct($options = null)
108+
public function __construct($options = null, array $groups = null, $payload = null)
107109
{
108-
foreach ($this->normalizeOptions($options) as $name => $value) {
110+
$options = $this->normalizeOptions($options);
111+
if (null !== $groups) {
112+
$options['groups'] = $groups;
113+
}
114+
$options['payload'] = $payload ?? $options['payload'] ?? null;
115+
116+
foreach ($options as $name => $value) {
109117
$this->$name = $value;
110118
}
111119
}

src/Symfony/Component/Validator/Constraints/Callback.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
1415
use Symfony\Component\Validator\Constraint;
1516

1617
/**
@@ -19,6 +20,7 @@
1920
*
2021
* @author Bernhard Schussek <bschussek@gmail.com>
2122
*/
23+
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
2224
class Callback extends Constraint
2325
{
2426
/**
@@ -28,19 +30,25 @@ class Callback extends Constraint
2830

2931
/**
3032
* {@inheritdoc}
33+
*
34+
* @param array|string|callable $callback The callback or a set of options
3135
*/
32-
public function __construct($options = null)
36+
public function __construct($callback = null, array $groups = null, $payload = null, array $options = [])
3337
{
3438
// Invocation through annotations with an array parameter only
35-
if (\is_array($options) && 1 === \count($options) && isset($options['value'])) {
36-
$options = $options['value'];
39+
if (\is_array($callback) && 1 === \count($callback) && isset($callback['value'])) {
40+
$callback = $callback['value'];
3741
}
3842

39-
if (\is_array($options) && !isset($options['callback']) && !isset($options['groups']) && !isset($options['payload'])) {
40-
$options = ['callback' => $options];
43+
if (!\is_array($callback)
44+
|| (!isset($callback['callback']) && !isset($callback['groups']) && !isset($callback['payload']))
45+
) {
46+
$options['callback'] = $callback;
47+
} else {
48+
$options = array_merge($callback, $options);
4149
}
4250

43-
parent::__construct($options);
51+
parent::__construct($options, $groups, $payload);
4452
}
4553

4654
/**

src/Symfony/Component/Validator/Constraints/Choice.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
1415
use Symfony\Component\Validator\Constraint;
1516

1617
/**
@@ -19,6 +20,7 @@
1920
*
2021
* @author Bernhard Schussek <bschussek@gmail.com>
2122
*/
23+
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
2224
class Choice extends Constraint
2325
{
2426
const NO_SUCH_CHOICE_ERROR = '8e179f1b-97aa-4560-a02f-2a8b42e49df7';
@@ -49,4 +51,38 @@ public function getDefaultOption()
4951
{
5052
return 'choices';
5153
}
54+
55+
public function __construct(
56+
$choices = null,
57+
$callback = null,
58+
bool $multiple = null,
59+
bool $strict = null,
60+
int $min = null,
61+
int $max = null,
62+
string $message = null,
63+
string $multipleMessage = null,
64+
string $minMessage = null,
65+
string $maxMessage = null,
66+
$groups = null,
67+
$payload = null,
68+
array $options = []
69+
) {
70+
if (\is_array($choices) && \is_string(key($choices))) {
71+
$options = array_merge($choices, $options);
72+
} elseif (null !== $choices) {
73+
$options['choices'] = $choices;
74+
}
75+
76+
parent::__construct($options, $groups, $payload);
77+
78+
$this->callback = $callback ?? $this->callback;
79+
$this->multiple = $multiple ?? $this->multiple;
80+
$this->strict = $strict ?? $this->strict;
81+
$this->min = $min ?? $this->min;
82+
$this->max = $max ?? $this->max;
83+
$this->message = $message ?? $this->message;
84+
$this->multipleMessage = $multipleMessage ?? $this->multipleMessage;
85+
$this->minMessage = $minMessage ?? $this->minMessage;
86+
$this->maxMessage = $maxMessage ?? $this->maxMessage;
87+
}
5288
}

src/Symfony/Component/Validator/Constraints/GroupSequence.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
15+
1416
/**
1517
* A sequence of validation groups.
1618
*
@@ -51,6 +53,7 @@
5153
*
5254
* @author Bernhard Schussek <bschussek@gmail.com>
5355
*/
56+
#[Attribute(Attribute::TARGET_CLASS)]
5457
class GroupSequence
5558
{
5659
/**

src/Symfony/Component/Validator/Constraints/GroupSequenceProvider.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
15+
1416
/**
1517
* Annotation to define a group sequence provider.
1618
*
@@ -19,6 +21,7 @@
1921
*
2022
* @author Bernhard Schussek <bschussek@gmail.com>
2123
*/
24+
#[Attribute(Attribute::TARGET_CLASS)]
2225
class GroupSequenceProvider
2326
{
2427
}

src/Symfony/Component/Validator/Constraints/IsTrue.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
1415
use Symfony\Component\Validator\Constraint;
1516

1617
/**
@@ -19,6 +20,7 @@
1920
*
2021
* @author Bernhard Schussek <bschussek@gmail.com>
2122
*/
23+
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
2224
class IsTrue extends Constraint
2325
{
2426
const NOT_TRUE_ERROR = '2beabf1c-54c0-4882-a928-05249b26e23b';
@@ -28,4 +30,12 @@ class IsTrue extends Constraint
2830
];
2931

3032
public $message = 'This value should be true.';
33+
34+
public function __construct(array $options = null, string $message = null, array $groups = null, $payload = null)
35+
{
36+
$options = $options ?? [];
37+
$options['message'] = $message ?? $options['message'] ?? $this->message;
38+
39+
parent::__construct($options, $groups, $payload);
40+
}
3141
}

src/Symfony/Component/Validator/Constraints/NotNull.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
1415
use Symfony\Component\Validator\Constraint;
1516

1617
/**
@@ -19,6 +20,7 @@
1920
*
2021
* @author Bernhard Schussek <bschussek@gmail.com>
2122
*/
23+
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
2224
class NotNull extends Constraint
2325
{
2426
const IS_NULL_ERROR = 'ad32d13f-c3d4-423b-909a-857b961eb720';
@@ -28,4 +30,12 @@ class NotNull extends Constraint
2830
];
2931

3032
public $message = 'This value should not be null.';
33+
34+
public function __construct(array $options = null, string $message = null, array $groups = null, $payload = null)
35+
{
36+
$options = $options ?? [];
37+
$options['message'] = $message ?? $options['message'] ?? $this->message;
38+
39+
parent::__construct($options, $groups, $payload);
40+
}
3141
}

src/Symfony/Component/Validator/Constraints/Valid.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Attribute;
1415
use Symfony\Component\Validator\Constraint;
1516

1617
/**
@@ -19,6 +20,7 @@
1920
*
2021
* @author Bernhard Schussek <bschussek@gmail.com>
2122
*/
23+
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
2224
class Valid extends Constraint
2325
{
2426
public $traverse = true;

src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class AnnotationLoader implements LoaderInterface
2828
{
2929
protected $reader;
3030

31-
public function __construct(Reader $reader)
31+
public function __construct(Reader $reader = null)
3232
{
3333
$this->reader = $reader;
3434
}
@@ -42,7 +42,7 @@ public function loadClassMetadata(ClassMetadata $metadata)
4242
$className = $reflClass->name;
4343
$success = false;
4444

45-
foreach ($this->reader->getClassAnnotations($reflClass) as $constraint) {
45+
foreach ($this->getAnnotations($reflClass) as $constraint) {
4646
if ($constraint instanceof GroupSequence) {
4747
$metadata->setGroupSequence($constraint->groups);
4848
} elseif ($constraint instanceof GroupSequenceProvider) {
@@ -56,7 +56,7 @@ public function loadClassMetadata(ClassMetadata $metadata)
5656

5757
foreach ($reflClass->getProperties() as $property) {
5858
if ($property->getDeclaringClass()->name === $className) {
59-
foreach ($this->reader->getPropertyAnnotations($property) as $constraint) {
59+
foreach ($this->getAnnotations($property) as $constraint) {
6060
if ($constraint instanceof Constraint) {
6161
$metadata->addPropertyConstraint($property->name, $constraint);
6262
}
@@ -68,7 +68,7 @@ public function loadClassMetadata(ClassMetadata $metadata)
6868

6969
foreach ($reflClass->getMethods() as $method) {
7070
if ($method->getDeclaringClass()->name === $className) {
71-
foreach ($this->reader->getMethodAnnotations($method) as $constraint) {
71+
foreach ($this->getAnnotations($method) as $constraint) {
7272
if ($constraint instanceof Callback) {
7373
$constraint->callback = $method->getName();
7474

@@ -88,4 +88,35 @@ public function loadClassMetadata(ClassMetadata $metadata)
8888

8989
return $success;
9090
}
91+
92+
/**
93+
* @param \ReflectionClass|\ReflectionMethod|\ReflectionProperty $reflection
94+
*/
95+
private function getAnnotations(object $reflection): iterable
96+
{
97+
if (\PHP_VERSION_ID >= 80000) {
98+
foreach ($reflection->getAttributes(GroupSequence::class) as $attribute) {
99+
yield $attribute->newInstance();
100+
}
101+
foreach ($reflection->getAttributes(GroupSequenceProvider::class) as $attribute) {
102+
yield $attribute->newInstance();
103+
}
104+
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
105+
yield $attribute->newInstance();
106+
}
107+
}
108+
if (!$this->reader) {
109+
return;
110+
}
111+
112+
if ($reflection instanceof \ReflectionClass) {
113+
yield from $this->reader->getClassAnnotations($reflection);
114+
}
115+
if ($reflection instanceof \ReflectionMethod) {
116+
yield from $this->reader->getMethodAnnotations($reflection);
117+
}
118+
if ($reflection instanceof \ReflectionProperty) {
119+
yield from $this->reader->getPropertyAnnotations($reflection);
120+
}
121+
}
91122
}

0 commit comments

Comments
 (0)