8000 [Validator] Upgraded constraints to enable named arguments and attrib… · symfony/symfony@fb99eb2 · GitHub
[go: up one dir, main page]

Skip to content

Commit fb99eb2

Browse files
derrabusfabpot
authored andcommitted
[Validator] Upgraded constraints to enable named arguments and attributes
1 parent 0000dfe commit fb99eb2

File tree

94 files changed

+2849
-137
lines changed

Some content is hidden

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

94 files changed

+2849
-137
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Security\Core\Tests\Validator\Constraints;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
16+
use Symfony\Component\Validator\Mapping\ClassMetadata;
17+
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
18+
19+
class UserPasswordTest extends TestCase
20+
{
21+
public function testValidatedByStandardValidator()
22+
{
23+
$constraint = new UserPassword();
24+
25+
self::assertSame('security.validator.user_password', $constraint->validatedBy());
26+
}
27+
28+
/**
29+
* @dataProvider provideServiceValidatedConstraints
30+
*/
31+
public function testValidatedByService(UserPassword $constraint)
32+
{
33+
self::assertSame('my_service', $constraint->validatedBy());
34+
}
35+
36+
public function provideServiceValidatedConstraints(): iterable
37+
{
38+
yield 'Doctrine style' => [new UserPassword(['service' => 'my_service'])];
39+
40+
if (\PHP_VERSION_ID < 80000) {
41+
return;
42+
}
43+
44+
yield 'named arguments' => [eval('return new \Symfony\Component\Security\Core\Validator\Constraints\UserPassword(service: "my_service");')];
45+
46+
$metadata = new ClassMetadata(UserPasswordDummy::class);
47+
self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata));
48+
49+
yield 'attribute' => [$metadata->properties['b']->constraints[0]];
50+
}
51+
52+
/**
53+
* @requires PHP 8
54+
*/
55+
public function testAttributes()
56+
{
57+
$metadata = new ClassMetadata(UserPasswordDummy::class);
58+
self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata));
59+
60+
list($bConstraint) = $metadata->properties['b']->getConstraints();
61+
self::assertSame('myMessage', $bConstraint->message);
62+
self::assertSame(['Default', 'UserPasswordDummy'], $bConstraint->groups);
63+
self::assertNull($bConstraint->payload);
64+
65+
list($cConstraint) = $metadata->properties['c']->getConstraints();
66+
self::assertSame(['my_group'], $cConstraint->groups);
67+
self::assertSame('some attached data', $cConstraint->payload);
68+
}
69+
}
70+
71+
class UserPasswordDummy
72+
{
73+
#[UserPassword]
74+
private $a;
75+
76+
#[UserPassword(service: 'my_service', message: 'myMessage')]
77+
private $b;
78+
79+
#[UserPassword(groups: ['my_group'], payload: 'some attached data')]
80+
private $c;
81+
}

src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,11 @@ protected function setUp(): void
5656
parent::setUp();
5757
}
5858
59-
public function testPasswordIsValid()
59+
/**
60+
* @dataProvider provideConstraints
61+
*/
62+
public function testPasswordIsValid(UserPassword $constraint)
6063
{
61-
$constraint = new UserPassword([
62-
'message' => 'myMessage',
63-
]);
64-
6564
$this->encoder->expects($this->once())
6665
->method('isPasswordValid')
6766
->with(static::PASSWORD, 'secret', static::SALT)
@@ -72,12 +71,11 @@ public function testPasswordIsValid()
7271
$this->assertNoViolation();
7372
}
7473

75-
public function testPasswordIsNotValid()
74+
/**
75+
* @dataProvider provideConstraints
76+
*/
77+
public function testPasswordIsNotValid(UserPassword $constraint)
7678
{
77-
$constraint = new UserPassword([
78-
'message' => 'myMessage',
79-
]);
80-
8179
$this->encoder->expects($this->once())
8280
->method('isPasswordValid')
8381
->with(static::PASSWORD, 'secret', static::SALT)
@@ -89,6 +87,15 @@ public function testPasswordIsNotValid()
8987
->assertRaised();
9088
}
9189

90+
public function provideConstraints(): iterable
91+
{
92+
yield 'Doctrine style' => [new UserPassword(['message' => 'myMessage'])];
93+
94+
if (\PHP_VERSION_ID >= 80000) {
95+
yield 'named arguments' => [eval('return new \Symfony\Component\Security\Core\Validator\Constraints\UserPassword(message: "myMessage");')];
96+
}
97+
}
98+
9299
/**
93100
* @dataProvider emptyPasswordData
94101
*/

src/Symfony/Component/Security/Core/Validator/Constraints/UserPassword.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@
1717
* @Annotation
1818
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
1919
*/
20+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2021
class UserPassword extends Constraint
2122
{
2223
public $message = 'This value should be the user\'s current password.';
2324
public $service = 'security.validator.user_password';
2425

26+
public function __construct(array $options = null, string $message = null, string $service = null, array $groups = null, $payload = null)
27+
{
28+
parent::__construct($options, $groups, $payload);
29+
30+
$this->message = $message ?? $this->message;
31+
$this->service = $service ?? $this->service;
32+
}
33+
2534
/**
2635
* {@inheritdoc}
2736
*/

src/Symfony/Component/Security/Core/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@
2828
"symfony/expression-language": "^4.4|^5.0",
2929
"symfony/http-foundation": "^4.4|^5.0",
3030
"symfony/ldap": "^4.4|^5.0",
31-
"symfony/validator": "^4.4|^5.0",
31+
"symfony/validator": "^5.2",
3232
"psr/log": "~1.0"
3333
},
3434
"conflict": {
3535
"symfony/event-dispatcher": "<4.4",
3636
"symfony/security-guard": "<4.4",
37-
"symfony/ldap": "<4.4"
37+
"symfony/ldap": "<4.4",
38+
"symfony/validator": "<5.2"
3839
},
3940
"suggest": {
4041
"psr/container-implementation": "To instantiate the Security class",

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Intl\Countries;
1515
use Symfony\Component\PropertyAccess\PropertyAccess;
16+
use Symfony\Component\PropertyAccess\PropertyPathInterface;
1617
use Symfony\Component\Validator\Constraint;
1718
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1819
use Symfony\Component\Validator\Exception\LogicException;
@@ -23,6 +24,7 @@
2324
*
2425
* @author Michael Hirschler <michael.vhirsch@gmail.com>
2526
*/
27+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2628
class Bic extends Constraint
2729
{
2830
const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c';
@@ -45,20 +47,33 @@ class Bic extends Constraint
4547
public $iban;
4648
public $ibanPropertyPath;
4749

48-
public function __construct($options = null)
50+
/**
51+
* {@inheritdoc}
52+
*
53+
* @param string|PropertyPathInterface|null $ibanPropertyPath
54+
*/
55+
public function __construct(array $options = null, string $message = null, string $iban = null, $ibanPropertyPath = null, string $ibanMessage = null, array $groups = null, $payload = null)
4956
{
5057
if (!class_exists(Countries::class)) {
5158
throw new LogicException('The Intl component is required to use the Bic constraint. Try running "composer require symfony/intl".');
5259
}
60+
if (null !== $ibanPropertyPath && !\is_string($ibanPropertyPath) && !$ibanPropertyPath instanceof PropertyPathInterface) {
61+
throw new \TypeError(sprintf('"%s": Expected argument $ibanPropertyPath to be either null, a string or an instance of "%s", got "%s".', __METHOD__, PropertyPathInterface::class, get_debug_type($ibanPropertyPath)));
62+
}
63+
64+
parent::__construct($options, $groups, $payload);
5365

54-
if (isset($options['iban']) && isset($options['ibanPropertyPath'])) {
66+
$this->message = $message ?? $this->message;
67+
$this->ibanMessage = $ibanMessage ?? $this->ibanMessage;
68+
$this->iban = $iban ?? $this->iban;
69+
$this->ibanPropertyPath = $ibanPropertyPath ?? $this->ibanPropertyPath;
70+
71+
if (null !== $this->iban && null !== $this->ibanPropertyPath) {
5572
throw new ConstraintDefinitionException('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time.');
5673
}
5774

58-
if (isset($options['ibanPropertyPath']) && !class_exists(PropertyAccess::class)) {
75+
if (null !== $this->ibanPropertyPath && !class_exists(PropertyAccess::class)) {
5976
throw new LogicException(sprintf('The "symfony/property-access" component is required to use the "%s" constraint with the "ibanPropertyPath" option.', self::class));
6077
}
61-
62-
parent::__construct($options);
6378
}
6479
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,22 @@
2222
* @author Tim Nagel <t.nagel@infinite.net.au>
2323
* @author Bernhard Schussek <bschussek@gmail.com>
2424
*/
25+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2526
class CardScheme extends Constraint
2627
{
28+
const AMEX = 'AMEX';
29+
const CHINA_UNIONPAY = 'CHINA_UNIONPAY';
30+
const DINERS = 'DINERS';
31+
const DISCOVER = 'DISCOVER';
32+
const INSTAPAYMENT = 'INSTAPAYMENT';
33+
const JCB = 'JCB';
34+
const LASER = 'LASER';
35+
const MAESTRO = 'MAESTRO';
36+
const MASTERCARD = 'MASTERCARD';
37+
const MIR = 'MIR';
38+
const UATP = 'UATP';
39+
const VISA = 'VISA';
40+
2741
const NOT_NUMERIC_ERROR = 'a2ad9231-e827-485f-8a1e-ef4d9a6d5c2e';
2842
const INVALID_FORMAT_ERROR = 'a8faedbf-1c2f-4695-8d22-55783be8efed';
2943

@@ -35,6 +49,24 @@ class CardScheme extends Constraint
3549
public $message = 'Unsupported card type or invalid card number.';
3650
public $schemes;
3751

52+
/**
53+
* {@inheritdoc}
54+
*
55+
* @param array|string $schemes The schemes to validate against or a set of options
56+
*/
57+
public function __construct($schemes, string $message = null, array $groups = null, $payload = null, array $options = [])
58+
{
59+
if (\is_array($schemes) && \is_string(key($schemes))) {
60+
$options = array_merge($schemes, $options);
61+
} else {
62+
$options['value'] = $schemes;
63+
}
64+
65+
parent::__construct($options, $groups, $payload);
66+
67+
$this->message = $message ?? $this->message;
68+
}
69+
3870
public function getDefaultOption()
3971
{
4072
return 'schemes';

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,65 +28,65 @@ class CardSchemeValidator extends ConstraintValidator
2828
{
2929
protected $schemes = [
3030
// American Express card numbers start with 34 or 37 and have 15 digits.
31-
'AMEX' => [
31+
CardScheme::AMEX => [
3232
'/^3[47][0-9]{13}$/',
3333
],
3434
// China UnionPay cards start with 62 and have between 16 and 19 digits.
3535
// Please note that these cards do not follow Luhn Algorithm as a checksum.
36-
'CHINA_UNIONPAY' => [
36+
CardScheme::CHINA_UNIONPAY => [
3737
'/^62[0-9]{14,17}$/',
3838
],
3939
// Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits.
4040
// There are Diners Club cards that begin with 5 and have 16 digits.
4141
// These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard.
42-
'DINERS' => [
42+
CardScheme::DINERS => [
4343
'/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',
4444
],
4545
// Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65.
4646
// All have 16 digits.
47-
'DISCOVER' => [
47+
CardScheme::DISCOVER => [
4848
'/^6011[0-9]{12}$/',
4949
'/^64[4-9][0-9]{13}$/',
5050
'/^65[0-9]{14}$/',
5151
'/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/',
5252
],
5353
// InstaPayment cards begin with 637 through 639 and have 16 digits.
54-
'INSTAPAYMENT' => [
54+
CardScheme::INSTAPAYMENT => [
5555
'/^63[7-9][0-9]{13}$/',
5656
],
5757
// JCB cards beginning with 2131 or 1800 have 15 digits.
5858
// JCB cards beginning with 35 have 16 digits.
59-
'JCB' => [
59+
CardScheme::JCB => [
6060
'/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/',
6161
],
6262
// Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits.
63-
'LASER' => [
63+
CardScheme::LASER => [
6464
'/^(6304|670[69]|6771)[0-9]{12,15}$/',
6565
],
6666
// Maestro international cards begin with 675900..675999 and have between 12 and 19 digits.
6767
// Maestro UK cards begin with either 500000..509999 or 560000..699999 and have between 12 and 19 digits.
68-
'MAESTRO' => [
68+
CardScheme::MAESTRO => [
6969
'/^(6759[0-9]{2})[0-9]{6,13}$/',
7070
'/^(50[0-9]{4})[0-9]{6,13}$/',
7171
'/^5[6-9][0-9]{10,17}$/',
7272
'/^6[0-9]{11,18}$/',
7373
],
7474
// All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
7575
// October 2016 Ma 57AE sterCard numbers can also start with 222100 through 272099.
76-
'MASTERCARD' => [
76+
CardScheme::MASTERCARD => [
7777
'/^5[1-5][0-9]{14}$/',
7878
'/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/',
7979
],
8080
// Payment system MIR numbers start with 220, then 1 digit from 0 to 4, then 12 digits
81-
'MIR' => [
81+
CardScheme::MIR => [
8282
'/^220[0-4][0-9]{12}$/',
8383
],
8484
// All UATP card numbers start with a 1 and have a length of 15 digits.
85-
'UATP' => [
85+
CardScheme::UATP => [
8686
'/^1[0-9]{14}$/',
8787
],
8888
// All Visa card numbers start with a 4 and have a length of 13, 16, or 19 digits.
89-
'VISA' => [
89+
CardScheme::VISA => [
9090
'/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/',
9191
],
9292
];

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
*
2121
* @author Jules Pietri <jules@heahprod.com>
2222
*/
23+
#[\Attribute(\Attribute::TARGET_CLASS)]
2324
class Cascade extends Constraint
2425
{
25-
public function __construct($options = null)
26+
public function __construct(array $options = null)
2627
{
2728
if (\is_array($options) && \array_key_exists('groups', $options)) {
2829
throw new ConstraintDefinitionException(sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__));

0 commit comments

Comments
 (0)
0