8000 [Validator] Upgraded constraints to enable named arguments and attributes by derrabus · Pull Request #38499 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Validator] Upgraded constraints to enable named arguments and attributes #38499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Security\Core\Tests\Validator\Constraints;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;

class UserPasswordTest extends TestCase
{
public function testValidatedByStandardValidator()
{
$constraint = new UserPassword();

self::assertSame('security.validator.user_password', $constraint->validatedBy());
}

/**
* @dataProvider provideServiceValidatedConstraints
*/
public function testValidatedByService(UserPassword $constraint)
{
self::assertSame('my_service', $constraint->validatedBy());
}

public function provideServiceValidatedConstraints(): iterable
{
yield 'Doctrine style' => [new UserPassword(['service' => 'my_service'])];

if (\PHP_VERSION_ID < 80000) {
return;
}

yield 'named arguments' => [eval('return new \Symfony\Component\Security\Core\Validator\Constraints\User F438 Password(service: "my_service");')];

$metadata = new ClassMetadata(UserPasswordDummy::class);
self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata));

yield 'attribute' => [$metadata->properties['b']->constraints[0]];
}

/**
* @requires PHP 8
*/
public function testAttributes()
{
$metadata = new ClassMetadata(UserPasswordDummy::class);
self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata));

list($bConstraint) = $metadata->properties['b']->getConstraints();
self::assertSame('myMessage', $bConstraint->message);
self::assertSame(['Default', 'UserPasswordDummy'], $bConstraint->groups);
self::assertNull($bConstraint->payload);

list($cConstraint) = $metadata->properties['c']->getConstraints();
self::assertSame(['my_group'], $cConstraint->groups);
self::assertSame('some attached data', $cConstraint->payload);
}
}

class UserPasswordDummy
{
#[UserPassword]
private $a;

#[UserPassword(service: 'my_service', message: 'myMessage')]
private $b;

#[UserPassword(groups: ['my_group'], payload: 'some attached data')]
private $c;
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,11 @@ protected function setUp(): void
parent::setUp();
}

public function testPasswordIsValid()
/**
* @dataProvider provideConstraints
*/
public function testPasswordIsValid(UserPassword $constraint)
{
$constraint = new UserPassword([
'message' => 'myMessage',
]);

$this->encoder->expects($this->once())
->method('isPasswordValid')
->with(static::PASSWORD, 'secret', static::SALT)
Expand All @@ -72,12 +71,11 @@ public function testPasswordIsValid()
$this->assertNoViolation();
}

public function testPasswordIsNotValid()
/**
* @dataProvider provideConstraints
*/
public function testPasswordIsNotValid(UserPassword $constraint)
{
$constraint = new UserPassword([
'message' => 'myMessage',
]);

$this->encoder->expects($this->once())
->method('isPasswordValid')
->with(static::PASSWORD, 'secret', static::SALT)
Expand All @@ -89,6 +87,15 @@ public function testPasswordIsNotValid()
->assertRaised();
}

public functio 10000 n provideConstraints(): iterable
{
yield 'Doctrine style' => [new UserPassword(['message' => 'myMessage'])];

if (\PHP_VERSION_ID >= 80000) {
yield 'named arguments' => [eval('return new \Symfony\Component\Security\Core\Validator\Constraints\UserPassword(message: "myMessage");')];
}
}

/**
* @dataProvider emptyPasswordData
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class UserPassword extends Constraint
{
public $message = 'This value should be the user\'s current password.';
public $service = 'security.validator.user_password';

public function __construct(array $options = null, string $message = null, string $service = null, array $groups = null, $payload = null)
{
parent::__construct($options, $groups, $payload);

$this->message = $message ?? $this->message;
$this->service = $service ?? $this->service;
}

/**
* {@inheritdoc}
*/
Expand Down
5 changes: 3 additions & 2 deletions src/Symfony/Component/Security/Core/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
"symfony/expression-language": "^4.4|^5.0",
"symfony/http-foundation": "^4.4|^5.0",
"symfony/ldap": "^4.4|^5.0",
"symfony/validator": "^4.4|^5.0",
"symfony/validator": "^5.2",
"psr/log": "~1.0"
},
"conflict": {
"symfony/event-dispatcher": "<4.4",
"symfony/security-guard": "<4.4",
"symfony/ldap": "<4.4"
"symfony/ldap": "<4.4",
"symfony/validator": "<5.2"
},
"suggest": {
"psr/container-implementation": "To instantiate the Security class",
Expand Down
25 changes: 20 additions & 5 deletions src/Symfony/Component/Validator/Constraints/Bic.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Intl\Countries;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyPathInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
Expand All @@ -23,6 +24,7 @@
*
* @author Michael Hirschler <michael.vhirsch@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Bic extends Constraint
{
const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c';
Expand All @@ -45,20 +47,33 @@ class Bic extends Constraint
public $iban;
public $ibanPropertyPath;

public function __construct($options = null)
/**
* {@inheritdoc}
*
* @param string|PropertyPathInterface|null $ibanPropertyPath
*/
public function __construct(array $options = null, string $message = null, string $iban = null, $ibanPropertyPath = null, string $ibanMessage = null, array $groups = null, $payload = null)
{
if (!class_exists(Countries::class)) {
throw new LogicException('The Intl component is required to use the Bic constraint. Try running "composer require symfony/intl".');
}
if (null !== $ibanPropertyPath && !\is_string($ibanPropertyPath) && !$ibanPropertyPath instanceof PropertyPathInterface) {
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)));
}

parent::__construct($options, $groups, $payload);

if (isset($options['iban']) && isset($options['ibanPropertyPath'])) {
$this->message = $message ?? $this->message;
$this->ibanMessage = $ibanMessage ?? $this->ibanMessage;
$this->iban = $iban ?? $this->iban;
$this->ibanPropertyPath = $ibanPropertyPath ?? $this->ibanPropertyPath;

if (null !== $this->iban && null !== $this->ibanPropertyPath) {
throw new ConstraintDefinitionException('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time.');
}

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

parent::__construct($options);
}
}
32 changes: 32 additions & 0 deletions src/Symfony/Component/Validator/Constraints/CardScheme.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,22 @@
* @author Tim Nagel <t.nagel@infinite.net.au>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class CardScheme extends Constraint
{
const AMEX = 'AMEX';
const CHINA_UNIONPAY = 'CHINA_UNIONPAY';
const DINERS = 'DINERS';
const DISCOVER = 'DISCOVER';
const INSTAPAYMENT = 'INSTAPAYMENT';
const JCB = 'JCB';
const LASER = 'LASER';
const MAESTRO = 'MAESTRO';
const MASTERCARD = 'MASTERCARD';
const MIR = 'MIR';
const UATP = 'UATP';
const VISA = 'VISA';

const NOT_NUMERIC_ERROR = 'a2ad9231-e827-485f-8a1e-ef4d9a6d5c2e';
const INVALID_FORMAT_ERROR = 'a8faedbf-1c2f-4695-8d22-55783be8efed';

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

/**
* {@inheritdoc}
*
* @param array|string $schemes The schemes to validate against or a set of options
*/
public function __construct($schemes, string $message = null, array $groups = null, $payload = null, array $options = [])
{
if (\is_array($schemes) && \is_string(key($schemes))) {
$options = array_merge($schemes, $options);
} else {
$options['value'] = $schemes;
}

parent::__construct($options, $groups, $payload);

$this->message = $message ?? $this->message;
}

public function getDefaultOption()
{
return 'schemes';
Expand Down
24 changes: 12 additions & 12 deletions src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,65 +28,65 @@ class CardSchemeValidator extends ConstraintValidator
{
protected $schemes = [
// American Express card numbers start with 34 or 37 and have 15 digits.
'AMEX' => [
CardScheme::AMEX => [
'/^3[47][0-9]{13}$/',
],
// China UnionPay cards start with 62 and have between 16 and 19 digits.
// Please note that these cards do not follow Luhn Algorithm as a checksum.
'CHINA_UNIONPAY' => [
CardScheme::CHINA_UNIONPAY => [
'/^62[0-9]{14,17}$/',
],
// Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits.
// There are Diners Club cards that begin with 5 and have 16 digits.
// These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard.
'DINERS' => [
CardScheme::DINERS => [
'/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',
],
// Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65.
// All have 16 digits.
10000 'DISCOVER' => [
CardScheme::DISCOVER => [
'/^6011[0-9]{12}$/',
'/^64[4-9][0-9]{13}$/',
'/^65[0-9]{14}$/',
'/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/',
],
// InstaPayment cards begin with 637 through 639 and have 16 digits.
'INSTAPAYMENT' => [
CardScheme::INSTAPAYMENT => [
'/^63[7-9][0-9]{13}$/',
],
// JCB cards beginning with 2131 or 1800 have 15 digits.
// JCB cards beginning with 35 have 16 digits.
'JCB' => [
CardScheme::JCB => [
'/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/',
],
// Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits.
'LASER' => [
CardScheme::LASER => [
'/^(6304|670[69]|6771)[0-9]{12,15}$/',
],
// Maestro international cards begin with 675900..675999 and have between 12 and 19 digits.
// Maestro UK cards begin with either 500000..509999 or 560000..699999 and have between 12 and 19 digits.
'MAESTRO' => [
CardScheme::MAESTRO => [
'/^(6759[0-9]{2})[0-9]{6,13}$/',
'/^(50[0-9]{4})[0-9]{6,13}$/',
'/^5[6-9][0-9]{10,17}$/',
'/^6[0-9]{11,18}$/',
],
// All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
// October 2016 MasterCard numbers can also start with 222100 through 272099.
'MASTERCARD' => [
CardScheme::MASTERCARD => [
'/^5[1-5][0-9]{14}$/',
'/^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})$/',
],
// Payment system MIR numbers start with 220, then 1 digit from 0 to 4, then 12 digits
'MIR' => [
CardScheme::MIR => [
'/^220[0-4][0-9]{12}$/',
],
// All UATP card numbers start with a 1 and have a length of 15 digits.
'UATP' => [
CardScheme::UATP => [
'/^1[0-9]{14}$/',
],
// All Visa card numbers start with a 4 and have a length of 13, 16, or 19 digits.
'VISA' => [
CardScheme::VISA => [
'/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/',
],
];
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Component/Validator/Constraints/Cascade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
*
* @author Jules Pietri <jules@heahprod.com>
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class Cascade extends Constraint
{
public function __construct($options = null)
public function __construct(array $options = null)
{
if (\is_array($options) && \array_key_exists('groups', $options)) {
throw new ConstraintDefinitionException(sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__));
Expand Down
Loading
0