From d6527be5c6bfa4623f0cfd17e737240121cc78a5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:24:39 +0200 Subject: [PATCH 001/149] [7.0] Bump to PHP 8.2 minimum --- Tests/ConstraintValidatorTest.php | 5 +--- Tests/Constraints/WhenTest.php | 3 -- composer.json | 46 +++++++++++++++---------------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Tests/ConstraintValidatorTest.php b/Tests/ConstraintValidatorTest.php index 91e67cade..07a38e56f 100644 --- a/Tests/ConstraintValidatorTest.php +++ b/Tests/ConstraintValidatorTest.php @@ -47,12 +47,9 @@ public static function formatValueProvider() [class_exists(\IntlDateFormatter::class) ? 'Feb 2, 1971, 8:00 AM' : '1971-02-02 08:00:00', $dateTime, ConstraintValidator::PRETTY_DATE], [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 6:00 AM' : '1970-01-01 06:00:00', new \DateTimeImmutable('1970-01-01T06:00:00Z'), ConstraintValidator::PRETTY_DATE], [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 3:00 PM' : '1970-01-01 15:00:00', (new \DateTimeImmutable('1970-01-01T23:00:00'))->setTimezone(new \DateTimeZone('America/New_York')), ConstraintValidator::PRETTY_DATE], + ['FirstCase', TestEnum::FirstCase], ]; - if (\PHP_VERSION_ID >= 80100) { - $data[] = ['FirstCase', TestEnum::FirstCase]; - } - date_default_timezone_set($defaultTimezone); return $data; diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 91938140d..4f230e2df 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -103,9 +103,6 @@ public function testAnnotations() self::assertSame(['Default', 'WhenTestWithAnnotations'], $bazConstraint->groups); } - /** - * @requires PHP 8.1 - */ public function testAttributes() { $loader = new AnnotationLoader(new AnnotationReader()); diff --git a/composer.json b/composer.json index 20b1463b9..e362268ee 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", @@ -24,21 +24,21 @@ "symfony/translation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/intl": "^5.4|^6.0|^7.0", - "symfony/yaml": "^5.4|^6.0|^7.0", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/mime": "^5.4|^6.0|^7.0", - "symfony/property-access": "^5.4|^6.0|^7.0", - "symfony/property-info": "^5.4|^6.0|^7.0", - "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", "doctrine/annotations": "^1.13|^2", "egulias/email-validator": "^2.1.10|^3|^4" }, @@ -46,13 +46,13 @@ "doctrine/annotations": "<1.13", "doctrine/lexer": "<1.1", "phpunit/phpunit": "<5.4.3", - "symfony/dependency-injection": "<5.4", - "symfony/expression-language": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/intl": "<5.4", - "symfony/property-info": "<5.4", - "symfony/translation": "<5.4", - "symfony/yaml": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/expression-language": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/intl": "<6.4", + "symfony/property-info": "<6.4", + "symfony/translation": "<6.4", + "symfony/yaml": "<6.4" }, "autoload": { "psr-4": { "Symfony\\Component\\Validator\\": "" }, From c4e22fab9aaa9dbad054f1454ca278435ac897ba Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 Jun 2023 10:51:15 +0200 Subject: [PATCH 002/149] [7.0] Fix various `@psalm-return` annotations --- Test/ConstraintValidatorTestCase.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Test/ConstraintValidatorTestCase.php b/Test/ConstraintValidatorTestCase.php index 9712d35ac..3a4437b1d 100644 --- a/Test/ConstraintValidatorTestCase.php +++ b/Test/ConstraintValidatorTestCase.php @@ -286,11 +286,9 @@ protected function buildViolation(string|\Stringable $message): ConstraintViolat } /** - * @return ConstraintValidatorInterface - * - * @psalm-return T + * @return T */ - abstract protected function createValidator(); + abstract protected function createValidator(): ConstraintValidatorInterface; } final class ConstraintViolationAssertion From 39ee02f5157b091a372c42a3078eb86e8a5b06fb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 Jun 2023 18:53:02 +0200 Subject: [PATCH 003/149] Remove BC layers related to new methods and new parameters --- CHANGELOG.md | 7 +++++++ ConstraintViolationInterface.php | 19 +++++++++++++++---- ConstraintViolationListInterface.php | 7 +++++-- .../ConstraintViolationBuilderInterface.php | 7 +++++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b97146439..0f0879cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +7.0 +--- + + * Add methods `getConstraint()`, `getCause()` and `__toString()` to `ConstraintViolationInterface` + * Add method `__toString()` to `ConstraintViolationListInterface` + * Add method `disableTranslation()` to `ConstraintViolationBuilderInterface` + 6.4 --- diff --git a/ConstraintViolationInterface.php b/ConstraintViolationInterface.php index 6eb279740..a8b6227a2 100644 --- a/ConstraintViolationInterface.php +++ b/ConstraintViolationInterface.php @@ -30,10 +30,6 @@ * element is still the person, but the property path is "address.street". * * @author Bernhard Schussek - * - * @method Constraint|null getConstraint() Returns the constraint whose validation caused the violation. Not implementing it is deprecated since Symfony 6.3. - * @method mixed getCause() Returns the cause of the violation. Not implementing it is deprecated since Symfony 6.2. - * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1. */ interface ConstraintViolationInterface { @@ -113,4 +109,19 @@ public function getInvalidValue(): mixed; * Returns a machine-digestible error code for the violation. */ public function getCode(): ?string; + + /** + * Returns the constraint whose validation caused the violation. + */ + public function getConstraint(): ?Constraint; + + /** + * Returns the cause of the violation. + */ + public function getCause(): mixed; + + /** + * Converts the violation into a string for debugging purposes. + */ + public function __toString(): string; } diff --git a/ConstraintViolationListInterface.php b/ConstraintViolationListInterface.php index 1fdbf0bc3..1c4acd87e 100644 --- a/ConstraintViolationListInterface.php +++ b/ConstraintViolationListInterface.php @@ -20,8 +20,6 @@ * * @extends \ArrayAccess * @extends \Traversable - * - * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1. */ interface ConstraintViolationListInterface extends \Traversable, \Countable, \ArrayAccess { @@ -72,4 +70,9 @@ public function set(int $offset, ConstraintViolationInterface $violation); * @return void */ public function remove(int $offset); + + /** + * Converts the violation into a string for debugging purposes. + */ + public function __toString(): string; } diff --git a/Violation/ConstraintViolationBuilderInterface.php b/Violation/ConstraintViolationBuilderInterface.php index 02fbeb797..854a83538 100644 --- a/Violation/ConstraintViolationBuilderInterface.php +++ b/Violation/ConstraintViolationBuilderInterface.php @@ -20,8 +20,6 @@ * execution context. * * @author Bernhard Schussek - * - * @method $this disableTranslation() */ interface ConstraintViolationBuilderInterface { @@ -58,6 +56,11 @@ public function setParameter(string $key, string $value): static; */ public function setParameters(array $parameters): static; + /** + * @return $this + */ + public function disableTranslation(): static; + /** * Sets the translation domain which should be used for translating the * violation message. From dcbdccde9bc4649a4aba806ed332b5a3de588540 Mon Sep 17 00:00:00 2001 From: Frank Fiebig Date: Tue, 4 Jul 2023 11:21:50 +0200 Subject: [PATCH 004/149] [Lock] 7.0 remove deprecations in Lock Component --- CHANGELOG.md | 3 + Constraint.php | 13 +-- Constraints/AtLeastOneOf.php | 5 -- Constraints/Bic.php | 5 -- Constraints/Blank.php | 5 -- Constraints/CardScheme.php | 5 -- Constraints/Choice.php | 5 -- Constraints/Cidr.php | 5 -- Constraints/Collection.php | 5 -- Constraints/Count.php | 5 -- Constraints/Country.php | 5 -- Constraints/CssColor.php | 5 -- Constraints/Currency.php | 5 -- Constraints/Date.php | 5 -- Constraints/DateTime.php | 5 -- Constraints/DivisibleBy.php | 5 -- Constraints/Email.php | 14 ---- Constraints/EmailValidator.php | 8 +- Constraints/EqualTo.php | 5 -- Constraints/Expression.php | 5 -- Constraints/ExpressionLanguageSyntax.php | 57 ------------- .../ExpressionLanguageSyntaxValidator.php | 63 -------------- Constraints/File.php | 5 -- Constraints/GreaterThan.php | 5 -- Constraints/GreaterThanOrEqual.php | 5 -- Constraints/Hostname.php | 5 -- Constraints/Iban.php | 5 -- Constraints/IdenticalTo.php | 5 -- Constraints/Image.php | 5 -- Constraints/Ip.php | 14 +--- Constraints/IsFalse.php | 5 -- Constraints/IsNull.php | 5 -- Constraints/IsTrue.php | 5 -- Constraints/Isbn.php | 5 -- Constraints/Isin.php | 5 -- Constraints/Issn.php | 5 -- Constraints/Json.php | 5 -- Constraints/Language.php | 5 -- Constraints/Length.php | 5 -- Constraints/LessThan.php | 5 -- Constraints/LessThanOrEqual.php | 5 -- Constraints/Locale.php | 5 -- Constraints/Luhn.php | 5 -- Constraints/NotBlank.php | 5 -- Constraints/NotCompromisedPassword.php | 5 -- Constraints/NotEqualTo.php | 5 -- Constraints/NotIdenticalTo.php | 5 -- Constraints/NotNull.php | 5 -- Constraints/Range.php | 5 -- Constraints/Regex.php | 5 -- Constraints/Time.php | 5 -- Constraints/Timezone.php | 5 -- Constraints/Type.php | 5 -- Constraints/Ulid.php | 5 -- Constraints/Unique.php | 5 -- Constraints/Url.php | 5 -- Constraints/Uuid.php | 5 -- Tests/Constraints/EmailValidatorTest.php | 64 --------------- .../ExpressionLanguageSyntaxTest.php | 82 ------------------- .../ExpressionLanguageSyntaxValidatorTest.php | 72 ---------------- composer.json | 1 - 61 files changed, 7 insertions(+), 634 deletions(-) delete mode 100644 Constraints/ExpressionLanguageSyntax.php delete mode 100644 Constraints/ExpressionLanguageSyntaxValidator.php delete mode 100644 Tests/Constraints/ExpressionLanguageSyntaxTest.php delete mode 100644 Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0879cdf..836caf31a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ CHANGELOG * Add methods `getConstraint()`, `getCause()` and `__toString()` to `ConstraintViolationInterface` * Add method `__toString()` to `ConstraintViolationListInterface` * Add method `disableTranslation()` to `ConstraintViolationBuilderInterface` + * Remove static property `$errorNames` from all constraints, use const `ERROR_NAMES` instead + * Remove `VALIDATION_MODE_LOOSE` from `Email` constraint, use `VALIDATION_MODE_HTML5` instead + * Remove constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead 6.4 --- diff --git a/Constraint.php b/Constraint.php index d53bbb196..8156c9919 100644 --- a/Constraint.php +++ b/Constraint.php @@ -49,11 +49,6 @@ abstract class Constraint */ protected const ERROR_NAMES = []; - /** - * @deprecated since Symfony 6.1, use protected const ERROR_NAMES instead - */ - protected static $errorNames = []; - /** * Domain-specific data attached to a constraint. * @@ -79,13 +74,7 @@ public static function getErrorName(string $errorCode): string return static::ERROR_NAMES[$errorCode]; } - if (!isset(static::$errorNames[$errorCode])) { - throw new InvalidArgumentException(sprintf('The error code "%s" does not exist for constraint of type "%s".', $errorCode, static::class)); - } - - trigger_deprecation('symfony/validator', '6.1', 'The "%s::$errorNames" property is deprecated, use protected const ERROR_NAMES instead.', static::class); - - return static::$errorNames[$errorCode]; + throw new InvalidArgumentException(sprintf('The error code "%s" does not exist for constraint of type "%s".', $errorCode, static::class)); } /** diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index 33ed343af..48469d877 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -26,11 +26,6 @@ class AtLeastOneOf extends Composite self::AT_LEAST_ONE_OF_ERROR => 'AT_LEAST_ONE_OF_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $constraints = []; public $message = 'This value should satisfy at least one of the following constraints:'; public $messageCollection = 'Each element of this collection should satisfy its own set of constraints.'; diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 855ab348d..d976dd0a3 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -41,11 +41,6 @@ class Bic extends Constraint self::INVALID_CASE_ERROR => 'INVALID_CASE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This is not a valid Business Identifier Code (BIC).'; public $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.'; public $iban; diff --git a/Constraints/Blank.php b/Constraints/Blank.php index bd28e68bc..a7e612a29 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -28,11 +28,6 @@ class Blank extends Constraint self::NOT_BLANK_ERROR => 'NOT_BLANK_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be blank.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 067024769..76dbcb9d8 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -46,11 +46,6 @@ class CardScheme extends Constraint self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'Unsupported card type or invalid card number.'; public $schemes; diff --git a/Constraints/Choice.php b/Constraints/Choice.php index 5544688d0..83cb78b76 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -32,11 +32,6 @@ class Choice extends Constraint self::TOO_MANY_ERROR => 'TOO_MANY_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $choices; public $callback; public $multiple = false; diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index 03002a7b5..0a721a45c 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -40,11 +40,6 @@ class Cidr extends Constraint Ip::V6 => 128, ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $version = Ip::ALL; public $message = 'This value is not a valid CIDR notation.'; diff --git a/Constraints/Collection.php b/Constraints/Collection.php index ee50fca16..a857c2aa4 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -30,11 +30,6 @@ class Collection extends Composite self::NO_SUCH_FIELD_ERROR => 'NO_SUCH_FIELD_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $fields = []; public $allowExtraFields = false; public $allowMissingFields = false; diff --git a/Constraints/Count.php b/Constraints/Count.php index ea5d41828..89985c398 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -35,11 +35,6 @@ class Count extends Constraint self::NOT_DIVISIBLE_BY_ERROR => 'NOT_DIVISIBLE_BY_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $minMessage = 'This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.'; public $maxMessage = 'This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.'; public $exactMessage = 'This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.'; diff --git a/Constraints/Country.php b/Constraints/Country.php index 03df0206b..0ca6fa47d 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -30,11 +30,6 @@ class Country extends Constraint self::NO_SUCH_COUNTRY_ERROR => 'NO_SUCH_COUNTRY_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid country.'; public $alpha3 = false; diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index d1e2e2783..56f4e1b16 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -41,11 +41,6 @@ class CssColor extends Constraint self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - /** * @var string[] */ diff --git a/Constraints/Currency.php b/Constraints/Currency.php index 5713d803e..5e4d81567 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -31,11 +31,6 @@ class Currency extends Constraint self::NO_SUCH_CURRENCY_ERROR => 'NO_SUCH_CURRENCY_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid currency.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/Date.php b/Constraints/Date.php index 1ca3bee11..e836df8fd 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -30,11 +30,6 @@ class Date extends Constraint self::INVALID_DATE_ERROR => 'INVALID_DATE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid date.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index f187f8b26..d8f97c696 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -32,11 +32,6 @@ class DateTime extends Constraint self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $format = 'Y-m-d H:i:s'; public $message = 'This value is not a valid datetime.'; diff --git a/Constraints/DivisibleBy.php b/Constraints/DivisibleBy.php index 90164aab2..941b7e07c 100644 --- a/Constraints/DivisibleBy.php +++ b/Constraints/DivisibleBy.php @@ -26,10 +26,5 @@ class DivisibleBy extends AbstractComparison self::NOT_DIVISIBLE_BY => 'NOT_DIVISIBLE_BY', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be a multiple of {{ compared_value }}.'; } diff --git a/Constraints/Email.php b/Constraints/Email.php index 46928894f..a505d56dc 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -28,10 +28,6 @@ class Email extends Constraint public const VALIDATION_MODE_HTML5_ALLOW_NO_TLD = 'html5-allow-no-tld'; public const VALIDATION_MODE_HTML5 = 'html5'; public const VALIDATION_MODE_STRICT = 'strict'; - /** - * @deprecated since Symfony 6.2, use VALIDATION_MODE_HTML5 instead - */ - public const VALIDATION_MODE_LOOSE = 'loose'; public const INVALID_FORMAT_ERROR = 'bd79c0ab-ddba-46cc-a703-a7a4b08de310'; @@ -39,18 +35,12 @@ class Email extends Constraint self::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, self::VALIDATION_MODE_HTML5, self::VALIDATION_MODE_STRICT, - self::VALIDATION_MODE_LOOSE, ]; protected const ERROR_NAMES = [ self::INVALID_FORMAT_ERROR => 'STRICT_CHECK_FAILED_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid email address.'; public $mode; public $normalizer; @@ -73,10 +63,6 @@ public function __construct( $this->mode = $mode ?? $this->mode; $this->normalizer = $normalizer ?? $this->normalizer; - if (self::VALIDATION_MODE_LOOSE === $this->mode) { - trigger_deprecation('symfony/validator', '6.2', 'The "%s" mode is deprecated. It will be removed in 7.0 and the default mode will be changed to "%s".', self::VALIDATION_MODE_LOOSE, self::VALIDATION_MODE_HTML5); - } - if (self::VALIDATION_MODE_STRICT === $this->mode && !class_exists(StrictEmailValidator::class)) { throw new LogicException(sprintf('The "egulias/email-validator" component is required to use the "%s" constraint in strict mode. Try running "composer require egulias/email-validator".', __CLASS__)); } diff --git a/Constraints/EmailValidator.php b/Constraints/EmailValidator.php index 8c0ff7730..72765dfbf 100644 --- a/Constraints/EmailValidator.php +++ b/Constraints/EmailValidator.php @@ -28,26 +28,20 @@ class EmailValidator extends ConstraintValidator { private const PATTERN_HTML5_ALLOW_NO_TLD = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/'; private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/'; - private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/'; private const EMAIL_PATTERNS = [ - Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE, Email::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5, Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD => self::PATTERN_HTML5_ALLOW_NO_TLD, ]; private string $defaultMode; - public function __construct(string $defaultMode = Email::VALIDATION_MODE_LOOSE) + public function __construct(string $defaultMode = Email::VALIDATION_MODE_HTML5) { if (!\in_array($defaultMode, Email::VALIDATION_MODES, true)) { throw new InvalidArgumentException('The "defaultMode" parameter value is not valid.'); } - if (Email::VALIDATION_MODE_LOOSE === $defaultMode) { - trigger_deprecation('symfony/validator', '6.2', 'The "%s" mode is deprecated. It will be removed in 7.0 and the default mode will be changed to "%s".', Email::VALIDATION_MODE_LOOSE, Email::VALIDATION_MODE_HTML5); - } - $this->defaultMode = $defaultMode; } diff --git a/Constraints/EqualTo.php b/Constraints/EqualTo.php index 03769ce8a..a6c4d1d10 100644 --- a/Constraints/EqualTo.php +++ b/Constraints/EqualTo.php @@ -27,10 +27,5 @@ class EqualTo extends AbstractComparison self::NOT_EQUAL_ERROR => 'NOT_EQUAL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be equal to {{ compared_value }}.'; } diff --git a/Constraints/Expression.php b/Constraints/Expression.php index cdf3e5006..19218e7d8 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -32,11 +32,6 @@ class Expression extends Constraint self::EXPRESSION_FAILED_ERROR => 'EXPRESSION_FAILED_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not valid.'; public $expression; public $values = []; diff --git a/Constraints/ExpressionLanguageSyntax.php b/Constraints/ExpressionLanguageSyntax.php deleted file mode 100644 index bbbb5ebe2..000000000 --- a/Constraints/ExpressionLanguageSyntax.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Constraints; - -use Symfony\Component\Validator\Constraint; - -trigger_deprecation('symfony/validator', '6.1', 'The "%s" constraint is deprecated since symfony 6.1, use "ExpressionSyntax" instead.', ExpressionLanguageSyntax::class); - -/** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * - * @author Andrey Sevastianov - * - * @deprecated since symfony 6.1, use ExpressionSyntax instead - */ -#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class ExpressionLanguageSyntax extends Constraint -{ - public const EXPRESSION_LANGUAGE_SYNTAX_ERROR = '1766a3f3-ff03-40eb-b053-ab7aa23d988a'; - - protected const ERROR_NAMES = [ - self::EXPRESSION_LANGUAGE_SYNTAX_ERROR => 'EXPRESSION_LANGUAGE_SYNTAX_ERROR', - ]; - - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - - public $message = 'This value should be a valid expression.'; - public $service; - public $allowedVariables; - - public function __construct(array $options = null, string $message = null, string $service = null, array $allowedVariables = null, array $groups = null, mixed $payload = null) - { - parent::__construct($options, $groups, $payload); - - $this->message = $message ?? $this->message; - $this->service = $service ?? $this->service; - $this->allowedVariables = $allowedVariables ?? $this->allowedVariables; - } - - public function validatedBy(): string - { - return $this->service ?? static::class.'Validator'; - } -} diff --git a/Constraints/ExpressionLanguageSyntaxValidator.php b/Constraints/ExpressionLanguageSyntaxValidator.php deleted file mode 100644 index d7e9c046b..000000000 --- a/Constraints/ExpressionLanguageSyntaxValidator.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Constraints; - -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use Symfony\Component\ExpressionLanguage\SyntaxError; -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Symfony\Component\Validator\Exception\UnexpectedValueException; - -trigger_deprecation('symfony/validator', '6.1', 'The "%s" constraint is deprecated since symfony 6.1, use "ExpressionSyntaxValidator" instead.', ExpressionLanguageSyntaxValidator::class); - -/** - * @author Andrey Sevastianov - * - * @deprecated since symfony 6.1, use ExpressionSyntaxValidator instead - */ -class ExpressionLanguageSyntaxValidator extends ConstraintValidator -{ - private ?ExpressionLanguage $expressionLanguage; - - public function __construct(ExpressionLanguage $expressionLanguage = null) - { - if (!class_exists(ExpressionLanguage::class)) { - throw new \LogicException(sprintf('The "%s" class requires the "ExpressionLanguage" component. Try running "composer require symfony/expression-language".', self::class)); - } - - $this->expressionLanguage = $expressionLanguage; - } - - public function validate(mixed $expression, Constraint $constraint): void - { - if (!$constraint instanceof ExpressionLanguageSyntax) { - throw new UnexpectedTypeException($constraint, ExpressionLanguageSyntax::class); - } - - if (!\is_string($expression)) { - throw new UnexpectedValueException($expression, 'string'); - } - - $this->expressionLanguage ??= new ExpressionLanguage(); - - try { - $this->expressionLanguage->lint($expression, $constraint->allowedVariables); - } catch (SyntaxError $exception) { - $this->context->buildViolation($constraint->message) - ->setParameter('{{ syntax_error }}', $this->formatValue($exception->getMessage())) - ->setInvalidValue((string) $expression) - ->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR) - ->addViolation(); - } - } -} diff --git a/Constraints/File.php b/Constraints/File.php index ed145ff38..367ed10c8 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -44,11 +44,6 @@ class File extends Constraint self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $binaryFormat; public $mimeTypes = []; public ?int $filenameMaxLength = null; diff --git a/Constraints/GreaterThan.php b/Constraints/GreaterThan.php index ce56f1ac1..160aa2a62 100644 --- a/Constraints/GreaterThan.php +++ b/Constraints/GreaterThan.php @@ -27,10 +27,5 @@ class GreaterThan extends AbstractComparison self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be greater than {{ compared_value }}.'; } diff --git a/Constraints/GreaterThanOrEqual.php b/Constraints/GreaterThanOrEqual.php index c962f7964..b4bed95a1 100644 --- a/Constraints/GreaterThanOrEqual.php +++ b/Constraints/GreaterThanOrEqual.php @@ -27,10 +27,5 @@ class GreaterThanOrEqual extends AbstractComparison self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be greater than or equal to {{ compared_value }}.'; } diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index cbf33cd8d..c0463b335 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -28,11 +28,6 @@ class Hostname extends Constraint self::INVALID_HOSTNAME_ERROR => 'INVALID_HOSTNAME_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid hostname.'; public $requireTld = true; diff --git a/Constraints/Iban.php b/Constraints/Iban.php index 684df2e56..2fefd504c 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -38,11 +38,6 @@ class Iban extends Constraint self::NOT_SUPPORTED_COUNTRY_CODE_ERROR => 'NOT_SUPPORTED_COUNTRY_CODE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This is not a valid International Bank Account Number (IBAN).'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/IdenticalTo.php b/Constraints/IdenticalTo.php index 50ec5e129..982617aa3 100644 --- a/Constraints/IdenticalTo.php +++ b/Constraints/IdenticalTo.php @@ -27,10 +27,5 @@ class IdenticalTo extends AbstractComparison self::NOT_IDENTICAL_ERROR => 'NOT_IDENTICAL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be identical to {{ compared_value_type }} {{ compared_value }}.'; } diff --git a/Constraints/Image.php b/Constraints/Image.php index c61b40836..ed2d4fa60 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -58,11 +58,6 @@ class Image extends File self::CORRUPTED_IMAGE_ERROR => 'CORRUPTED_IMAGE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $mimeTypes = 'image/*'; public $minWidth; public $maxWidth; diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 94c4ca484..050f31ef3 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -70,16 +70,6 @@ class Ip extends Constraint self::INVALID_IP_ERROR => 'INVALID_IP_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const VERSIONS instead - */ - protected static $versions = self::VERSIONS; - - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $version = self::V4; public $message = 'This is not a valid IP address.'; @@ -100,8 +90,8 @@ public function __construct( $this->message = $message ?? $this->message; $this->normalizer = $normalizer ?? $this->normalizer; - if (!\in_array($this->version, self::$versions)) { - throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s".', implode('", "', self::$versions))); + if (!\in_array($this->version, static::VERSIONS, true)) { + throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s".', implode('", "', static::VERSIONS))); } if (null !== $this->normalizer && !\is_callable($this->normalizer)) { diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index 26042e2ee..9e86383b7 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -28,11 +28,6 @@ class IsFalse extends Constraint self::NOT_FALSE_ERROR => 'NOT_FALSE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be false.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index 5084c268a..b6d9eaa1a 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -28,11 +28,6 @@ class IsNull extends Constraint self::NOT_NULL_ERROR => 'NOT_NULL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be null.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index 8ee9f729d..0f3e2f189 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -28,11 +28,6 @@ class IsTrue extends Constraint self::NOT_TRUE_ERROR => 'NOT_TRUE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be true.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index 5b70c6fcb..18a8e7758 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -41,11 +41,6 @@ class Isbn extends Constraint self::TYPE_NOT_RECOGNIZED_ERROR => 'TYPE_NOT_RECOGNIZED_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $isbn10Message = 'This value is not a valid ISBN-10.'; public $isbn13Message = 'This value is not a valid ISBN-13.'; public $bothIsbnMessage = 'This value is neither a valid ISBN-10 nor a valid ISBN-13.'; diff --git a/Constraints/Isin.php b/Constraints/Isin.php index 1522044be..90a713158 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -35,11 +35,6 @@ class Isin extends Constraint self::INVALID_CHECKSUM_ERROR => 'INVALID_CHECKSUM_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid International Securities Identification Number (ISIN).'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/Issn.php b/Constraints/Issn.php index a11f022e6..e591960e9 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -39,11 +39,6 @@ class Issn extends Constraint self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid ISSN.'; public $caseSensitive = false; public $requireHyphen = false; diff --git a/Constraints/Json.php b/Constraints/Json.php index f2826d28e..6facc6dba 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -28,11 +28,6 @@ class Json extends Constraint self::INVALID_JSON_ERROR => 'INVALID_JSON_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be valid JSON.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/Language.php b/Constraints/Language.php index e3c2ce962..b0d18289e 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -30,11 +30,6 @@ class Language extends Constraint self::NO_SUCH_LANGUAGE_ERROR => 'NO_SUCH_LANGUAGE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid language.'; public $alpha3 = false; diff --git a/Constraints/Length.php b/Constraints/Length.php index 59360ace1..4daf59e50 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -46,11 +46,6 @@ class Length extends Constraint self::COUNT_GRAPHEMES, ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.'; public $minMessage = 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.'; public $exactMessage = 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.'; diff --git a/Constraints/LessThan.php b/Constraints/LessThan.php index cf4144d6d..2770c9b15 100644 --- a/Constraints/LessThan.php +++ b/Constraints/LessThan.php @@ -27,10 +27,5 @@ class LessThan extends AbstractComparison self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be less than {{ compared_value }}.'; } diff --git a/Constraints/LessThanOrEqual.php b/Constraints/LessThanOrEqual.php index 84e31abfc..e2f127f07 100644 --- a/Constraints/LessThanOrEqual.php +++ b/Constraints/LessThanOrEqual.php @@ -27,10 +27,5 @@ class LessThanOrEqual extends AbstractComparison self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be less than or equal to {{ compared_value }}.'; } diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 30f0ffd6e..946785b43 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -30,11 +30,6 @@ class Locale extends Constraint self::NO_SUCH_LOCALE_ERROR => 'NO_SUCH_LOCALE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid locale.'; public $canonicalize = true; diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index 198fb29ba..fb76ec9a0 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -34,11 +34,6 @@ class Luhn extends Constraint self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'Invalid card number.'; public function __construct( diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index 38637ad20..02d6d5c79 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -30,11 +30,6 @@ class NotBlank extends Constraint self::IS_BLANK_ERROR => 'IS_BLANK_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should not be blank.'; public $allowNull = false; public $normalizer; diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index 3329d3c1a..ae90925f7 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -30,11 +30,6 @@ class NotCompromisedPassword extends Constraint self::COMPROMISED_PASSWORD_ERROR => 'COMPROMISED_PASSWORD_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This password has been leaked in a data breach, it must not be used. Please use another password.'; public $threshold = 1; public $skipOnError = false; diff --git a/Constraints/NotEqualTo.php b/Constraints/NotEqualTo.php index 9a5c07b21..8ddc2d334 100644 --- a/Constraints/NotEqualTo.php +++ b/Constraints/NotEqualTo.php @@ -27,10 +27,5 @@ class NotEqualTo extends AbstractComparison self::IS_EQUAL_ERROR => 'IS_EQUAL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should not be equal to {{ compared_value }}.'; } diff --git a/Constraints/NotIdenticalTo.php b/Constraints/NotIdenticalTo.php index 206c10613..80628135a 100644 --- a/Constraints/NotIdenticalTo.php +++ b/Constraints/NotIdenticalTo.php @@ -27,10 +27,5 @@ class NotIdenticalTo extends AbstractComparison self::IS_IDENTICAL_ERROR => 'IS_IDENTICAL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should not be identical to {{ compared_value_type }} {{ compared_value }}.'; } diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 2fd4123cd..8d4f2e211 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -28,11 +28,6 @@ class NotNull extends Constraint self::IS_NULL_ERROR => 'IS_NULL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should not be null.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) diff --git a/Constraints/Range.php b/Constraints/Range.php index c4ae8b764..da01a5488 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -38,11 +38,6 @@ class Range extends Constraint self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $notInRangeMessage = 'This value should be between {{ min }} and {{ max }}.'; public $minMessage = 'This value should be {{ limit }} or more.'; public $maxMessage = 'This value should be {{ limit }} or less.'; diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 67dc8b37c..9062819e1 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -29,11 +29,6 @@ class Regex extends Constraint self::REGEX_FAILED_ERROR => 'REGEX_FAILED_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not valid.'; public $pattern; public $htmlPattern; diff --git a/Constraints/Time.php b/Constraints/Time.php index f281ae1ee..994473dc1 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -30,11 +30,6 @@ class Time extends Constraint self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid time.'; public function __construct( diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index 03bc19e7c..1ba24be9c 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -41,11 +41,6 @@ class Timezone extends Constraint self::TIMEZONE_IDENTIFIER_INTL_ERROR => 'TIMEZONE_IDENTIFIER_INTL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public function __construct( int|array $zone = null, string $message = null, diff --git a/Constraints/Type.php b/Constraints/Type.php index 97a5ef939..cf61476c4 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -28,11 +28,6 @@ class Type extends Constraint self::INVALID_TYPE_ERROR => 'INVALID_TYPE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value should be of type {{ type }}.'; public $type; diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index ece08c721..a1f2c086e 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -33,11 +33,6 @@ class Ulid extends Constraint self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This is not a valid ULID.'; public function __construct( diff --git a/Constraints/Unique.php b/Constraints/Unique.php index 6db31c0e4..d85de6c74 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -31,11 +31,6 @@ class Unique extends Constraint self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This collection should contain only unique elements.'; public $normalizer; diff --git a/Constraints/Url.php b/Constraints/Url.php index e3bea2e1b..5575e2c2b 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -29,11 +29,6 @@ class Url extends Constraint self::INVALID_URL_ERROR => 'INVALID_URL_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - public $message = 'This value is not a valid URL.'; public $protocols = ['http', 'https']; public $relativeProtocol = false; diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index a96d2ceb9..f397143b1 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -40,11 +40,6 @@ class Uuid extends Constraint self::INVALID_VARIANT_ERROR => 'INVALID_VARIANT_ERROR', ]; - /** - * @deprecated since Symfony 6.1, use const ERROR_NAMES instead - */ - protected static $errorNames = self::ERROR_NAMES; - // Possible versions defined by RFC 4122 public const V1_MAC = 1; public const V2_DCE = 2; diff --git a/Tests/Constraints/EmailValidatorTest.php b/Tests/Constraints/EmailValidatorTest.php index 6bf8fec6c..d894236e1 100644 --- a/Tests/Constraints/EmailValidatorTest.php +++ b/Tests/Constraints/EmailValidatorTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Validator\Tests\Constraints; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Constraints\EmailValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -22,8 +21,6 @@ */ class EmailValidatorTest extends ConstraintValidatorTestCase { - use ExpectDeprecationTrait; - protected function createValidator(): EmailValidator { return new EmailValidator(Email::VALIDATION_MODE_HTML5); @@ -82,30 +79,6 @@ public static function getValidEmails() ]; } - /** - * @group legacy - * - * @dataProvider getValidEmails - * @dataProvider getEmailsOnlyValidInLooseMode - */ - public function testValidInLooseModeEmails($email) - { - $this->validator->validate($email, new Email(['mode' => Email::VALIDATION_MODE_LOOSE])); - - $this->assertNoViolation(); - } - - public static function getEmailsOnlyValidInLooseMode() - { - return [ - ['example@example.co..uk'], - ['{}~!@!@£$%%^&*().!@£$%^&*()'], - ['example@example.co..uk'], - ['example@-example.com'], - [sprintf('example@%s.com', str_repeat('a', 64))], - ]; - } - /** * @dataProvider getValidEmailsWithWhitespaces */ @@ -124,29 +97,6 @@ public static function getValidEmailsWithWhitespaces() ]; } - /** - * @group legacy - * - * @dataProvider getValidEmailsWithWhitespaces - * @dataProvider getEmailsWithWhitespacesOnlyValidInLooseMode - */ - public function testValidNormalizedEmailsInLooseMode($email) - { - $this->validator->validate($email, new Email(['mode' => Email::VALIDATION_MODE_LOOSE, 'normalizer' => 'trim'])); - - $this->assertNoViolation(); - } - - public static function getEmailsWithWhitespacesOnlyValidInLooseMode() - { - return [ - ["\x09\x09example@example.co..uk\x09\x09"], - ["\x0A{}~!@!@£$%%^&*().!@£$%^&*()\x0A"], - ["\x0D\x0Dexample@example.co..uk\x0D\x0D"], - ["\x00example@-example.com"], - ]; - } - /** * @dataProvider getValidEmailsHtml5 */ @@ -293,20 +243,6 @@ public function testModeHtml5AllowNoTld() $this->assertNoViolation(); } - /** - * @group legacy - */ - public function testModeLoose() - { - $this->expectDeprecation('Since symfony/validator 6.2: The "loose" mode is deprecated. It will be removed in 7.0 and the default mode will be changed to "html5".'); - - $constraint = new Email(['mode' => Email::VALIDATION_MODE_LOOSE]); - - $this->validator->validate('example@example..com', $constraint); - - $this->assertNoViolation(); - } - public function testUnknownModesOnValidateTriggerException() { $this->expectException(\InvalidArgumentException::class); diff --git a/Tests/Constraints/ExpressionLanguageSyntaxTest.php b/Tests/Constraints/ExpressionLanguageSyntaxTest.php deleted file mode 100644 index 2fb8bf155..000000000 --- a/Tests/Constraints/ExpressionLanguageSyntaxTest.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Constraints; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntax; -use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator; -use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; - -/** - * @group legacy - */ -class ExpressionLanguageSyntaxTest extends TestCase -{ - public function testValidatedByStandardValidator() - { - $constraint = new ExpressionLanguageSyntax(); - - self::assertSame(ExpressionLanguageSyntaxValidator::class, $constraint->validatedBy()); - } - - /** - * @dataProvider provideServiceValidatedConstraints - */ - public function testValidatedByService(ExpressionLanguageSyntax $constraint) - { - self::assertSame('my_service', $constraint->validatedBy()); - } - - public static function provideServiceValidatedConstraints(): iterable - { - yield 'Doctrine style' => [new ExpressionLanguageSyntax(['service' => 'my_service'])]; - - yield 'named arguments' => [new ExpressionLanguageSyntax(service: 'my_service')]; - - $metadata = new ClassMetadata(ExpressionLanguageSyntaxDummy::class); - self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata)); - - yield 'attribute' => [$metadata->properties['b']->constraints[0]]; - } - - public function testAttributes() - { - $metadata = new ClassMetadata(ExpressionLanguageSyntaxDummy::class); - self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata)); - - [$aConstraint] = $metadata->properties['a']->getConstraints(); - self::assertNull($aConstraint->service); - self::assertNull($aConstraint->allowedVariables); - - [$bConstraint] = $metadata->properties['b']->getConstraints(); - self::assertSame('my_service', $bConstraint->service); - self::assertSame('myMessage', $bConstraint->message); - self::assertSame(['Default', 'ExpressionLanguageSyntaxDummy'], $bConstraint->groups); - - [$cConstraint] = $metadata->properties['c']->getConstraints(); - self::assertSame(['foo', 'bar'], $cConstraint->allowedVariables); - self::assertSame(['my_group'], $cConstraint->groups); - } -} - -class ExpressionLanguageSyntaxDummy -{ - #[ExpressionLanguageSyntax] - private $a; - - #[ExpressionLanguageSyntax(service: 'my_service', message: 'myMessage')] - private $b; - - #[ExpressionLanguageSyntax(allowedVariables: ['foo', 'bar'], groups: ['my_group'])] - private $c; -} diff --git a/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php b/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php deleted file mode 100644 index f44e606a4..000000000 --- a/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Constraints; - -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntax; -use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator; -use Symfony\Component\Validator\ConstraintValidatorInterface; -use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; - -/** - * @group legacy - */ -class ExpressionLanguageSyntaxValidatorTest extends ConstraintValidatorTestCase -{ - protected function createValidator(): ConstraintValidatorInterface - { - return new ExpressionLanguageSyntaxValidator(new ExpressionLanguage()); - } - - public function testExpressionValid() - { - $this->validator->validate('1 + 1', new ExpressionLanguageSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => [], - ])); - - $this->assertNoViolation(); - } - - public function testExpressionWithoutNames() - { - $this->validator->validate('1 + 1', new ExpressionLanguageSyntax([ - 'message' => 'myMessage', - ])); - - $this->assertNoViolation(); - } - - public function testExpressionWithAllowedVariableName() - { - $this->validator->validate('a + 1', new ExpressionLanguageSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => ['a'], - ])); - - $this->assertNoViolation(); - } - - public function testExpressionIsNotValid() - { - $this->validator->validate('a + 1', new ExpressionLanguageSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => [], - ])); - - $this->buildViolation('myMessage') - ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."') - ->setInvalidValue('a + 1') - ->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR) - ->assertRaised(); - } -} diff --git a/composer.json b/composer.json index 8e8326d32..898e318ad 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,6 @@ ], "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php83": "^1.27", From 975c11f6e514f85e61f04673b62072b70cd3b9ca Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 23:52:21 +0200 Subject: [PATCH 005/149] [Components] Convert to native return types --- Command/DebugCommand.php | 5 +--- Constraint.php | 22 +++++------------ ConstraintValidator.php | 5 +--- ConstraintValidatorInterface.php | 8 ++----- ConstraintViolationList.php | 20 ++++------------ ConstraintViolationListInterface.php | 16 ++++--------- Constraints/AbstractComparisonValidator.php | 5 +--- Constraints/AllValidator.php | 5 +--- Constraints/AtLeastOneOfValidator.php | 5 +--- Constraints/BicValidator.php | 5 +--- Constraints/BlankValidator.php | 5 +--- Constraints/CallbackValidator.php | 5 +--- Constraints/CardSchemeValidator.php | 4 +--- Constraints/Cascade.php | 2 +- Constraints/ChoiceValidator.php | 5 +--- Constraints/Collection.php | 5 +--- Constraints/CollectionValidator.php | 5 +--- Constraints/Composite.php | 8 ++----- Constraints/CompoundValidator.php | 5 +--- Constraints/CountValidator.php | 5 +--- Constraints/CountryValidator.php | 5 +--- Constraints/CurrencyValidator.php | 5 +--- Constraints/DateTimeValidator.php | 5 +--- Constraints/DateValidator.php | 5 +--- Constraints/EmailValidator.php | 5 +--- Constraints/ExpressionValidator.php | 5 +--- Constraints/File.php | 5 +--- Constraints/FileValidator.php | 5 +--- Constraints/HostnameValidator.php | 5 +--- Constraints/IbanValidator.php | 5 +--- Constraints/ImageValidator.php | 5 +--- Constraints/IpValidator.php | 5 +--- Constraints/IsFalseValidator.php | 5 +--- Constraints/IsNullValidator.php | 5 +--- Constraints/IsTrueValidator.php | 5 +--- Constraints/IsbnValidator.php | 20 ++++------------ Constraints/IsinValidator.php | 5 +--- Constraints/IssnValidator.php | 5 +--- Constraints/JsonValidator.php | 5 +--- Constraints/LanguageValidator.php | 5 +--- Constraints/LengthValidator.php | 5 +--- Constraints/LocaleValidator.php | 5 +--- Constraints/LuhnValidator.php | 5 +--- .../NoSuspiciousCharactersValidator.php | 5 +--- Constraints/NotBlankValidator.php | 5 +--- .../NotCompromisedPasswordValidator.php | 4 +--- Constraints/NotNullValidator.php | 5 +--- Constraints/RangeValidator.php | 5 +--- Constraints/RegexValidator.php | 5 +--- Constraints/SequentiallyValidator.php | 5 +--- Constraints/TimeValidator.php | 5 +--- Constraints/TimezoneValidator.php | 5 +--- Constraints/TypeValidator.php | 5 +--- Constraints/UlidValidator.php | 5 +--- Constraints/UniqueValidator.php | 5 +--- Constraints/UrlValidator.php | 5 +--- Constraints/UuidValidator.php | 5 +--- Constraints/Valid.php | 5 +--- Constraints/ValidValidator.php | 5 +--- Context/ExecutionContextInterface.php | 24 +++++-------------- .../AddAutoMappingConfigurationPass.php | 5 +--- .../AddConstraintValidatorsPass.php | 5 +--- .../AddValidatorInitializersPass.php | 5 +--- Exception/InvalidOptionsException.php | 5 +--- Exception/MissingOptionsException.php | 5 +--- Exception/ValidationFailedException.php | 5 +--- Mapping/ClassMetadata.php | 8 ++----- Mapping/Loader/AbstractLoader.php | 4 +--- Mapping/MemberMetadata.php | 5 +--- ObjectInitializerInterface.php | 5 +--- Resources/bin/sync-iban-formats.php | 2 +- .../NotCompromisedPasswordValidatorTest.php | 4 ++-- Validator/TraceableValidator.php | 5 +--- .../ConstraintViolationBuilderInterface.php | 4 +--- 74 files changed, 97 insertions(+), 348 deletions(-) diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index acb00d724..f4e945d8f 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -43,10 +43,7 @@ public function __construct(MetadataFactoryInterface $validator) $this->validator = $validator; } - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this ->addArgument('class', InputArgument::REQUIRED, 'A fully qualified class name or a path') diff --git a/Constraint.php b/Constraint.php index b480a810d..50314963f 100644 --- a/Constraint.php +++ b/Constraint.php @@ -51,8 +51,6 @@ abstract class Constraint /** * Domain-specific data attached to a constraint. - * - * @var mixed */ public $payload; @@ -181,11 +179,9 @@ protected function normalizeOptions(mixed $options): array * this method will be called at most once per constraint instance and * option name. * - * @return void - * * @throws InvalidOptionsException If an invalid option name is given */ - public function __set(string $option, mixed $value) + public function __set(string $option, mixed $value): void { if ('groups' === $option) { $this->groups = (array) $value; @@ -223,10 +219,8 @@ public function __isset(string $option): bool /** * Adds the given group if this constraint is in the Default group. - * - * @return void */ - public function addImplicitGroupName(string $group) + public function addImplicitGroupName(string $group): void { if (null === $this->groups && \array_key_exists('groups', (array) $this)) { throw new \LogicException(sprintf('"%s::$groups" is set to null. Did you forget to call "%s::__construct()"?', static::class, self::class)); @@ -242,11 +236,9 @@ public function addImplicitGroupName(string $group) * * Override this method to define a default option. * - * @return string|null - * * @see __construct() */ - public function getDefaultOption() + public function getDefaultOption(): ?string { return null; } @@ -260,7 +252,7 @@ public function getDefaultOption() * * @see __construct() */ - public function getRequiredOptions() + public function getRequiredOptions(): array { return []; } @@ -271,10 +263,8 @@ public function getRequiredOptions() * By default, this is the fully qualified name of the constraint class * suffixed with "Validator". You can override this method to change that * behavior. - * - * @return string */ - public function validatedBy() + public function validatedBy(): string { return static::class.'Validator'; } @@ -288,7 +278,7 @@ public function validatedBy() * * @return string|string[] One or more constant values */ - public function getTargets() + public function getTargets(): string|array { return self::PROPERTY_CONSTRAINT; } diff --git a/ConstraintValidator.php b/ConstraintValidator.php index 3c4167be3..f9dceaf54 100644 --- a/ConstraintValidator.php +++ b/ConstraintValidator.php @@ -36,10 +36,7 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface */ protected $context; - /** - * @return void - */ - public function initialize(ExecutionContextInterface $context) + public function initialize(ExecutionContextInterface $context): void { $this->context = $context; } diff --git a/ConstraintValidatorInterface.php b/ConstraintValidatorInterface.php index fe7da2e8f..68faace61 100644 --- a/ConstraintValidatorInterface.php +++ b/ConstraintValidatorInterface.php @@ -20,15 +20,11 @@ interface ConstraintValidatorInterface { /** * Initializes the constraint validator. - * - * @return void */ - public function initialize(ExecutionContextInterface $context); + public function initialize(ExecutionContextInterface $context): void; /** * Checks if the passed value is valid. - * - * @return void */ - public function validate(mixed $value, Constraint $constraint); + public function validate(mixed $value, Constraint $constraint): void; } diff --git a/ConstraintViolationList.php b/ConstraintViolationList.php index 88f90a0cd..96a93d583 100644 --- a/ConstraintViolationList.php +++ b/ConstraintViolationList.php @@ -58,18 +58,12 @@ public function __toString(): string return $string; } - /** - * @return void - */ - public function add(ConstraintViolationInterface $violation) + public function add(ConstraintViolationInterface $violation): void { $this->violations[] = $violation; } - /** - * @return void - */ - public function addAll(ConstraintViolationListInterface $otherList) + public function addAll(ConstraintViolationListInterface $otherList): void { foreach ($otherList as $violation) { $this->violations[] = $violation; @@ -90,18 +84,12 @@ public function has(int $offset): bool return isset($this->violations[$offset]); } - /** - * @return void - */ - public function set(int $offset, ConstraintViolationInterface $violation) + public function set(int $offset, ConstraintViolationInterface $violation): void { $this->violations[$offset] = $violation; } - /** - * @return void - */ - public function remove(int $offset) + public function remove(int $offset): void { unset($this->violations[$offset]); } diff --git a/ConstraintViolationListInterface.php b/ConstraintViolationListInterface.php index 1c4acd87e..d3be9f07e 100644 --- a/ConstraintViolationListInterface.php +++ b/ConstraintViolationListInterface.php @@ -25,17 +25,13 @@ interface ConstraintViolationListInterface extends \Traversable, \Countable, \Ar { /** * Adds a constraint violation to this list. - * - * @return void */ - public function add(ConstraintViolationInterface $violation); + public function add(ConstraintViolationInterface $violation): void; /** * Merges an existing violation list into this list. - * - * @return void */ - public function addAll(self $otherList); + public function addAll(self $otherList): void; /** * Returns the violation at a given offset. @@ -57,19 +53,15 @@ public function has(int $offset): bool; * Sets a violation at a given offset. * * @param int $offset The violation offset - * - * @return void */ - public function set(int $offset, ConstraintViolationInterface $violation); + public function set(int $offset, ConstraintViolationInterface $violation): void; /** * Removes a violation at a given offset. * * @param int $offset The offset to remove - * - * @return void */ - public function remove(int $offset); + public function remove(int $offset): void; /** * Converts the violation into a string for debugging purposes. diff --git a/Constraints/AbstractComparisonValidator.php b/Constraints/AbstractComparisonValidator.php index a81d589f2..2d358f47c 100644 --- a/Constraints/AbstractComparisonValidator.php +++ b/Constraints/AbstractComparisonValidator.php @@ -34,10 +34,7 @@ public function __construct(PropertyAccessorInterface $propertyAccessor = null) $this->propertyAccessor = $propertyAccessor; } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof AbstractComparison) { throw new UnexpectedTypeException($constraint, AbstractComparison::class); diff --git a/Constraints/AllValidator.php b/Constraints/AllValidator.php index 15896f29d..10ec0266f 100644 --- a/Constraints/AllValidator.php +++ b/Constraints/AllValidator.php @@ -21,10 +21,7 @@ */ class AllValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof All) { throw new UnexpectedTypeException($constraint, All::class); diff --git a/Constraints/AtLeastOneOfValidator.php b/Constraints/AtLeastOneOfValidator.php index 94ad5eaca..68c16c863 100644 --- a/Constraints/AtLeastOneOfValidator.php +++ b/Constraints/AtLeastOneOfValidator.php @@ -20,10 +20,7 @@ */ class AtLeastOneOfValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof AtLeastOneOf) { throw new UnexpectedTypeException($constraint, AtLeastOneOf::class); diff --git a/Constraints/BicValidator.php b/Constraints/BicValidator.php index ca0ed331c..b9eac610f 100644 --- a/Constraints/BicValidator.php +++ b/Constraints/BicValidator.php @@ -63,10 +63,7 @@ public function __construct(PropertyAccessor $propertyAccessor = null) $this->propertyAccessor = $propertyAccessor; } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Bic) { throw new UnexpectedTypeException($constraint, Bic::class); diff --git a/Constraints/BlankValidator.php b/Constraints/BlankValidator.php index 2551d5e24..ec4b7e445 100644 --- a/Constraints/BlankValidator.php +++ b/Constraints/BlankValidator.php @@ -20,10 +20,7 @@ */ class BlankValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Blank) { throw new UnexpectedTypeException($constraint, Blank::class); diff --git a/Constraints/CallbackValidator.php b/Constraints/CallbackValidator.php index ea62a5c82..417586c15 100644 --- a/Constraints/CallbackValidator.php +++ b/Constraints/CallbackValidator.php @@ -23,10 +23,7 @@ */ class CallbackValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $object, Constraint $constraint) + public function validate(mixed $object, Constraint $constraint): void { if (!$constraint instanceof Callback) { throw new UnexpectedTypeException($constraint, Callback::class); diff --git a/Constraints/CardSchemeValidator.php b/Constraints/CardSchemeValidator.php index 3e5105015..c21a93a9b 100644 --- a/Constraints/CardSchemeValidator.php +++ b/Constraints/CardSchemeValidator.php @@ -93,10 +93,8 @@ class CardSchemeValidator extends ConstraintValidator /** * Validates a creditcard belongs to a specified scheme. - * - * @return void */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof CardScheme) { throw new UnexpectedTypeException($constraint, CardScheme::class); diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index a1e03af41..42f8784ca 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -25,7 +25,7 @@ class Cascade extends Constraint { public array $exclude = []; - public function __construct(array|string|null $exclude = null, array $options = null) + public function __construct(array|string $exclude = null, array $options = null) { if (\is_array($exclude) && !array_is_list($exclude)) { $options = array_merge($exclude, $options); diff --git a/Constraints/ChoiceValidator.php b/Constraints/ChoiceValidator.php index 5528252c5..4c70fceb2 100644 --- a/Constraints/ChoiceValidator.php +++ b/Constraints/ChoiceValidator.php @@ -27,10 +27,7 @@ */ class ChoiceValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Choice) { throw new UnexpectedTypeException($constraint, Choice::class); diff --git a/Constraints/Collection.php b/Constraints/Collection.php index a857c2aa4..6a6bcf3cc 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -52,10 +52,7 @@ public function __construct(mixed $fields = null, array $groups = null, mixed $p $this->missingFieldsMessage = $missingFieldsMessage ?? $this->missingFieldsMessage; } - /** - * @return void - */ - protected function initializeNestedConstraints() + protected function initializeNestedConstraints(): void { parent::initializeNestedConstraints(); diff --git a/Constraints/CollectionValidator.php b/Constraints/CollectionValidator.php index 7bb63e7de..a44694345 100644 --- a/Constraints/CollectionValidator.php +++ b/Constraints/CollectionValidator.php @@ -21,10 +21,7 @@ */ class CollectionValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Collection) { throw new UnexpectedTypeException($constraint, Collection::class); diff --git a/Constraints/Composite.php b/Constraints/Composite.php index df1331538..c77f8cf88 100644 --- a/Constraints/Composite.php +++ b/Constraints/Composite.php @@ -110,10 +110,8 @@ public function __construct(mixed $options = null, array $groups = null, mixed $ /** * Implicit group names are forwarded to nested constraints. - * - * @return void */ - public function addImplicitGroupName(string $group) + public function addImplicitGroupName(string $group): void { parent::addImplicitGroupName($group); @@ -148,10 +146,8 @@ public function getNestedConstraints(): array * constraints passed to the constructor. * * @see Collection::initializeNestedConstraints() - * - * @return void */ - protected function initializeNestedConstraints() + protected function initializeNestedConstraints(): void { } } diff --git a/Constraints/CompoundValidator.php b/Constraints/CompoundValidator.php index 8f9c713c7..11ce4855b 100644 --- a/Constraints/CompoundValidator.php +++ b/Constraints/CompoundValidator.php @@ -20,10 +20,7 @@ */ class CompoundValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Compound) { throw new UnexpectedTypeException($constraint, Compound::class); diff --git a/Constraints/CountValidator.php b/Constraints/CountValidator.php index 3c5602353..6aa425bdf 100644 --- a/Constraints/CountValidator.php +++ b/Constraints/CountValidator.php @@ -21,10 +21,7 @@ */ class CountValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Count) { throw new UnexpectedTypeException($constraint, Count::class); diff --git a/Constraints/CountryValidator.php b/Constraints/CountryValidator.php index 54c8da0f9..067896922 100644 --- a/Constraints/CountryValidator.php +++ b/Constraints/CountryValidator.php @@ -24,10 +24,7 @@ */ class CountryValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Country) { throw new UnexpectedTypeException($constraint, Country::class); diff --git a/Constraints/CurrencyValidator.php b/Constraints/CurrencyValidator.php index a50ea62ab..ef662802f 100644 --- a/Constraints/CurrencyValidator.php +++ b/Constraints/CurrencyValidator.php @@ -25,10 +25,7 @@ */ class CurrencyValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Currency) { throw new UnexpectedTypeException($constraint, Currency::class); diff --git a/Constraints/DateTimeValidator.php b/Constraints/DateTimeValidator.php index c88732d4d..9784a5797 100644 --- a/Constraints/DateTimeValidator.php +++ b/Constraints/DateTimeValidator.php @@ -21,10 +21,7 @@ */ class DateTimeValidator extends DateValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof DateTime) { throw new UnexpectedTypeException($constraint, DateTime::class); diff --git a/Constraints/DateValidator.php b/Constraints/DateValidator.php index 0e3d84843..57dfed9f7 100644 --- a/Constraints/DateValidator.php +++ b/Constraints/DateValidator.php @@ -33,10 +33,7 @@ public static function checkDate(int $year, int $month, int $day): bool return checkdate($month, $day, $year); } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Date) { throw new UnexpectedTypeException($constraint, Date::class); diff --git a/Constraints/EmailValidator.php b/Constraints/EmailValidator.php index 72765dfbf..9b751811d 100644 --- a/Constraints/EmailValidator.php +++ b/Constraints/EmailValidator.php @@ -45,10 +45,7 @@ public function __construct(string $defaultMode = Email::VALIDATION_MODE_HTML5) $this->defaultMode = $defaultMode; } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Email) { throw new UnexpectedTypeException($constraint, Email::class); diff --git a/Constraints/ExpressionValidator.php b/Constraints/ExpressionValidator.php index d1fe60a79..c00875bcb 100644 --- a/Constraints/ExpressionValidator.php +++ b/Constraints/ExpressionValidator.php @@ -29,10 +29,7 @@ public function __construct(ExpressionLanguage $expressionLanguage = null) $this->expressionLanguage = $expressionLanguage; } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Expression) { throw new UnexpectedTypeException($constraint, Expression::class); diff --git a/Constraints/File.php b/Constraints/File.php index 4f50e6b8e..0716abe1c 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -125,10 +125,7 @@ public function __construct( } } - /** - * @return void - */ - public function __set(string $option, mixed $value) + public function __set(string $option, mixed $value): void { if ('maxSize' === $option) { $this->normalizeBinaryFormat($value); diff --git a/Constraints/FileValidator.php b/Constraints/FileValidator.php index 6346ad098..03c12b91e 100644 --- a/Constraints/FileValidator.php +++ b/Constraints/FileValidator.php @@ -38,10 +38,7 @@ class FileValidator extends ConstraintValidator self::MIB_BYTES => 'MiB', ]; - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof File) { throw new UnexpectedTypeException($constraint, File::class); diff --git a/Constraints/HostnameValidator.php b/Constraints/HostnameValidator.php index 8b0fa60e2..605a74f33 100644 --- a/Constraints/HostnameValidator.php +++ b/Constraints/HostnameValidator.php @@ -31,10 +31,7 @@ class HostnameValidator extends ConstraintValidator 'test', ]; - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Hostname) { throw new UnexpectedTypeException($constraint, Hostname::class); diff --git a/Constraints/IbanValidator.php b/Constraints/IbanValidator.php index b4744b4b5..47ddf17ea 100644 --- a/Constraints/IbanValidator.php +++ b/Constraints/IbanValidator.php @@ -163,10 +163,7 @@ class IbanValidator extends ConstraintValidator 'YT' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France ]; - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Iban) { throw new UnexpectedTypeException($constraint, Iban::class); diff --git a/Constraints/ImageValidator.php b/Constraints/ImageValidator.php index 93386e6f8..436eb212b 100644 --- a/Constraints/ImageValidator.php +++ b/Constraints/ImageValidator.php @@ -25,10 +25,7 @@ */ class ImageValidator extends FileValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Image) { throw new UnexpectedTypeException($constraint, Image::class); diff --git a/Constraints/IpValidator.php b/Constraints/IpValidator.php index 2f71a8804..8adba4bc9 100644 --- a/Constraints/IpValidator.php +++ b/Constraints/IpValidator.php @@ -24,10 +24,7 @@ */ class IpValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Ip) { throw new UnexpectedTypeException($constraint, Ip::class); diff --git a/Constraints/IsFalseValidator.php b/Constraints/IsFalseValidator.php index 76a24ad78..78c4c5a92 100644 --- a/Constraints/IsFalseValidator.php +++ b/Constraints/IsFalseValidator.php @@ -20,10 +20,7 @@ */ class IsFalseValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof IsFalse) { throw new UnexpectedTypeException($constraint, IsFalse::class); diff --git a/Constraints/IsNullValidator.php b/Constraints/IsNullValidator.php index 628aacf26..c292854bd 100644 --- a/Constraints/IsNullValidator.php +++ b/Constraints/IsNullValidator.php @@ -20,10 +20,7 @@ */ class IsNullValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof IsNull) { throw new UnexpectedTypeException($constraint, IsNull::class); diff --git a/Constraints/IsTrueValidator.php b/Constraints/IsTrueValidator.php index 644bbf70c..69e411402 100644 --- a/Constraints/IsTrueValidator.php +++ b/Constraints/IsTrueValidator.php @@ -20,10 +20,7 @@ */ class IsTrueValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof IsTrue) { throw new UnexpectedTypeException($constraint, IsTrue::class); diff --git a/Constraints/IsbnValidator.php b/Constraints/IsbnValidator.php index 3a64f11cf..e0a390c65 100644 --- a/Constraints/IsbnValidator.php +++ b/Constraints/IsbnValidator.php @@ -27,10 +27,7 @@ */ class IsbnValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Isbn) { throw new UnexpectedTypeException($constraint, Isbn::class); @@ -95,10 +92,7 @@ public function validate(mixed $value, Constraint $constraint) } } - /** - * @return string|bool - */ - protected function validateIsbn10(string $isbn) + protected function validateIsbn10(string $isbn): string|bool { // Choose an algorithm so that ERROR_INVALID_CHARACTERS is preferred // over ERROR_TOO_SHORT/ERROR_TOO_LONG @@ -138,10 +132,7 @@ protected function validateIsbn10(string $isbn) return 0 === $checkSum % 11 ? true : Isbn::CHECKSUM_FAILED_ERROR; } - /** - * @return string|bool - */ - protected function validateIsbn13(string $isbn) + protected function validateIsbn13(string $isbn): string|bool { // Error priority: // 1. ERROR_INVALID_CHARACTERS @@ -175,10 +166,7 @@ protected function validateIsbn13(string $isbn) return 0 === $checkSum % 10 ? true : Isbn::CHECKSUM_FAILED_ERROR; } - /** - * @return string - */ - protected function getMessage(Isbn $constraint, string $type = null) + protected function getMessage(Isbn $constraint, string $type = null): string { if (null !== $constraint->message) { return $constraint->message; diff --git a/Constraints/IsinValidator.php b/Constraints/IsinValidator.php index 21539cbcb..32b3b93d5 100644 --- a/Constraints/IsinValidator.php +++ b/Constraints/IsinValidator.php @@ -23,10 +23,7 @@ */ class IsinValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Isin) { throw new UnexpectedTypeException($constraint, Isin::class); diff --git a/Constraints/IssnValidator.php b/Constraints/IssnValidator.php index 1962322b4..f8668c63d 100644 --- a/Constraints/IssnValidator.php +++ b/Constraints/IssnValidator.php @@ -26,10 +26,7 @@ */ class IssnValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Issn) { throw new UnexpectedTypeException($constraint, Issn::class); diff --git a/Constraints/JsonValidator.php b/Constraints/JsonValidator.php index f12424ae8..0b89d4c90 100644 --- a/Constraints/JsonValidator.php +++ b/Constraints/JsonValidator.php @@ -20,10 +20,7 @@ */ class JsonValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Json) { throw new UnexpectedTypeException($constraint, Json::class); diff --git a/Constraints/LanguageValidator.php b/Constraints/LanguageValidator.php index 4706c3356..9f9832006 100644 --- a/Constraints/LanguageValidator.php +++ b/Constraints/LanguageValidator.php @@ -24,10 +24,7 @@ */ class LanguageValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Language) { throw new UnexpectedTypeException($constraint, Language::class); diff --git a/Constraints/LengthValidator.php b/Constraints/LengthValidator.php index c92fca0d5..ea35125ad 100644 --- a/Constraints/LengthValidator.php +++ b/Constraints/LengthValidator.php @@ -21,10 +21,7 @@ */ class LengthValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Length) { throw new UnexpectedTypeException($constraint, Length::class); diff --git a/Constraints/LocaleValidator.php b/Constraints/LocaleValidator.php index 4cd0b120b..87d0b2679 100644 --- a/Constraints/LocaleValidator.php +++ b/Constraints/LocaleValidator.php @@ -24,10 +24,7 @@ */ class LocaleValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Locale) { throw new UnexpectedTypeException($constraint, Locale::class); diff --git a/Constraints/LuhnValidator.php b/Constraints/LuhnValidator.php index a3f871e33..242b9ad4b 100644 --- a/Constraints/LuhnValidator.php +++ b/Constraints/LuhnValidator.php @@ -30,10 +30,7 @@ */ class LuhnValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Luhn) { throw new UnexpectedTypeException($constraint, Luhn::class); diff --git a/Constraints/NoSuspiciousCharactersValidator.php b/Constraints/NoSuspiciousCharactersValidator.php index 659de93f9..d55a765f5 100644 --- a/Constraints/NoSuspiciousCharactersValidator.php +++ b/Constraints/NoSuspiciousCharactersValidator.php @@ -56,10 +56,7 @@ public function __construct(private readonly array $defaultLocales = []) { } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof NoSuspiciousCharacters) { throw new UnexpectedTypeException($constraint, NoSuspiciousCharacters::class); diff --git a/Constraints/NotBlankValidator.php b/Constraints/NotBlankValidator.php index fa6c794c0..acc81040c 100644 --- a/Constraints/NotBlankValidator.php +++ b/Constraints/NotBlankValidator.php @@ -21,10 +21,7 @@ */ class NotBlankValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof NotBlank) { throw new UnexpectedTypeException($constraint, NotBlank::class); diff --git a/Constraints/NotCompromisedPasswordValidator.php b/Constraints/NotCompromisedPasswordValidator.php index 477e98fab..f5cf41a03 100644 --- a/Constraints/NotCompromisedPasswordValidator.php +++ b/Constraints/NotCompromisedPasswordValidator.php @@ -50,11 +50,9 @@ public function __construct(HttpClientInterface $httpClient = null, string $char } /** - * @return void - * * @throws ExceptionInterface */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof NotCompromisedPassword) { throw new UnexpectedTypeException($constraint, NotCompromisedPassword::class); diff --git a/Constraints/NotNullValidator.php b/Constraints/NotNullValidator.php index 3f8f95128..0fd773685 100644 --- a/Constraints/NotNullValidator.php +++ b/Constraints/NotNullValidator.php @@ -20,10 +20,7 @@ */ class NotNullValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof NotNull) { throw new UnexpectedTypeException($constraint, NotNull::class); diff --git a/Constraints/RangeValidator.php b/Constraints/RangeValidator.php index 73273d678..ee6d6acaa 100644 --- a/Constraints/RangeValidator.php +++ b/Constraints/RangeValidator.php @@ -31,10 +31,7 @@ public function __construct(PropertyAccessorInterface $propertyAccessor = null) $this->propertyAccessor = $propertyAccessor; } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Range) { throw new UnexpectedTypeException($constraint, Range::class); diff --git a/Constraints/RegexValidator.php b/Constraints/RegexValidator.php index 4e9ae9039..5823f5d7c 100644 --- a/Constraints/RegexValidator.php +++ b/Constraints/RegexValidator.php @@ -24,10 +24,7 @@ */ class RegexValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Regex) { throw new UnexpectedTypeException($constraint, Regex::class); diff --git a/Constraints/SequentiallyValidator.php b/Constraints/SequentiallyValidator.php index d076f3cfd..f5b4cb243 100644 --- a/Constraints/SequentiallyValidator.php +++ b/Constraints/SequentiallyValidator.php @@ -20,10 +20,7 @@ */ class SequentiallyValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Sequentially) { throw new UnexpectedTypeException($constraint, Sequentially::class); diff --git a/Constraints/TimeValidator.php b/Constraints/TimeValidator.php index c6d111cfe..54e278a53 100644 --- a/Constraints/TimeValidator.php +++ b/Constraints/TimeValidator.php @@ -33,10 +33,7 @@ public static function checkTime(int $hour, int $minute, float $second): bool return $hour >= 0 && $hour < 24 && $minute >= 0 && $minute < 60 && $second >= 0 && $second < 60; } - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Time) { throw new UnexpectedTypeException($constraint, Time::class); diff --git a/Constraints/TimezoneValidator.php b/Constraints/TimezoneValidator.php index 21481a5af..eee55d6e4 100644 --- a/Constraints/TimezoneValidator.php +++ b/Constraints/TimezoneValidator.php @@ -26,10 +26,7 @@ */ class TimezoneValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Timezone) { throw new UnexpectedTypeException($constraint, Timezone::class); diff --git a/Constraints/TypeValidator.php b/Constraints/TypeValidator.php index ad9e31457..5aab198b8 100644 --- a/Constraints/TypeValidator.php +++ b/Constraints/TypeValidator.php @@ -52,10 +52,7 @@ class TypeValidator extends ConstraintValidator 'xdigit' => 'ctype_xdigit', ]; - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Type) { throw new UnexpectedTypeException($constraint, Type::class); diff --git a/Constraints/UlidValidator.php b/Constraints/UlidValidator.php index ad47f66d4..91942721b 100644 --- a/Constraints/UlidValidator.php +++ b/Constraints/UlidValidator.php @@ -24,10 +24,7 @@ */ class UlidValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Ulid) { throw new UnexpectedTypeException($constraint, Ulid::class); diff --git a/Constraints/UniqueValidator.php b/Constraints/UniqueValidator.php index 1e692fe68..f4e4012c6 100644 --- a/Constraints/UniqueValidator.php +++ b/Constraints/UniqueValidator.php @@ -21,10 +21,7 @@ */ class UniqueValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Unique) { throw new UnexpectedTypeException($constraint, Unique::class); diff --git a/Constraints/UrlValidator.php b/Constraints/UrlValidator.php index 4a73040a2..a2d08e544 100644 --- a/Constraints/UrlValidator.php +++ b/Constraints/UrlValidator.php @@ -45,10 +45,7 @@ class UrlValidator extends ConstraintValidator (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional) $~ixu'; - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Url) { throw new UnexpectedTypeException($constraint, Url::class); diff --git a/Constraints/UuidValidator.php b/Constraints/UuidValidator.php index 1fc879123..b193f3f85 100644 --- a/Constraints/UuidValidator.php +++ b/Constraints/UuidValidator.php @@ -59,10 +59,7 @@ class UuidValidator extends ConstraintValidator public const LOOSE_MAX_LENGTH = 39; public const LOOSE_FIRST_HYPHEN_POSITION = 4; - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Uuid) { throw new UnexpectedTypeException($constraint, Uuid::class); diff --git a/Constraints/Valid.php b/Constraints/Valid.php index 46c5ee355..5acf87473 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -41,10 +41,7 @@ public function __get(string $option): mixed return parent::__get($option); } - /** - * @return void - */ - public function addImplicitGroupName(string $group) + public function addImplicitGroupName(string $group): void { if (null !== $this->groups) { parent::addImplicitGroupName($group); diff --git a/Constraints/ValidValidator.php b/Constraints/ValidValidator.php index 7c960ffee..b1a02d2e9 100644 --- a/Constraints/ValidValidator.php +++ b/Constraints/ValidValidator.php @@ -20,10 +20,7 @@ */ class ValidValidator extends ConstraintValidator { - /** - * @return void - */ - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Valid) { throw new UnexpectedTypeException($constraint, Valid::class); diff --git a/Context/ExecutionContextInterface.php b/Context/ExecutionContextInterface.php index b78b39b42..142aa6962 100644 --- a/Context/ExecutionContextInterface.php +++ b/Context/ExecutionContextInterface.php @@ -66,10 +66,8 @@ interface ExecutionContextInterface * * @param string|\Stringable $message The error message as a string or a stringable object * @param array $params The parameters substituted in the error message - * - * @return void */ - public function addViolation(string $message, array $params = []); + public function addViolation(string $message, array $params = []): void; /** * Returns a builder for adding a violation with extended information. @@ -123,26 +121,20 @@ public function getObject(): ?object; * * @param object|null $object The currently validated object * @param string $propertyPath The property path to the current value - * - * @return void */ - public function setNode(mixed $value, ?object $object, MetadataInterface $metadata = null, string $propertyPath); + public function setNode(mixed $value, ?object $object, MetadataInterface $metadata = null, string $propertyPath): void; /** * Warning: Should not be called by user code, to be used by the validator engine only. * * @param string|null $group The validated group - * - * @return void */ - public function setGroup(?string $group); + public function setGroup(?string $group): void; /** * Warning: Should not be called by user code, to be used by the validator engine only. - * - * @return void */ - public function setConstraint(Constraint $constraint); + public function setConstraint(Constraint $constraint): void; /** * Warning: Should not be called by user code, to be used by the validator engine only. @@ -150,10 +142,8 @@ public function setConstraint(Constraint $constraint); * @param string $cacheKey The hash of the object * @param string $groupHash The group's name or hash, if it is group * sequence - * - * @return void */ - public function markGroupAsValidated(string $cacheKey, string $groupHash); + public function markGroupAsValidated(string $cacheKey, string $groupHash): void; /** * Warning: Should not be called by user code, to be used by the validator engine only. @@ -169,10 +159,8 @@ public function isGroupValidated(string $cacheKey, string $groupHash): bool; * * @param string $cacheKey The hash of the object * @param string $constraintHash The hash of the constraint - * - * @return void */ - public function markConstraintAsValidated(string $cacheKey, string $constraintHash); + public function markConstraintAsValidated(string $cacheKey, string $constraintHash): void; /** * Warning: Should not be called by user code, to be used by the validator engine only. diff --git a/DependencyInjection/AddAutoMappingConfigurationPass.php b/DependencyInjection/AddAutoMappingConfigurationPass.php index b1680b7cd..db33aa5a7 100644 --- a/DependencyInjection/AddAutoMappingConfigurationPass.php +++ b/DependencyInjection/AddAutoMappingConfigurationPass.php @@ -22,10 +22,7 @@ */ class AddAutoMappingConfigurationPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasParameter('validator.auto_mapping') || !$container->hasDefinition('validator.builder')) { return; diff --git a/DependencyInjection/AddConstraintValidatorsPass.php b/DependencyInjection/AddConstraintValidatorsPass.php index 4bcae00ef..eb60c94cb 100644 --- a/DependencyInjection/AddConstraintValidatorsPass.php +++ b/DependencyInjection/AddConstraintValidatorsPass.php @@ -22,10 +22,7 @@ */ class AddConstraintValidatorsPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('validator.validator_factory')) { return; diff --git a/DependencyInjection/AddValidatorInitializersPass.php b/DependencyInjection/AddValidatorInitializersPass.php index d53e3c85e..df7385b0c 100644 --- a/DependencyInjection/AddValidatorInitializersPass.php +++ b/DependencyInjection/AddValidatorInitializersPass.php @@ -21,10 +21,7 @@ */ class AddValidatorInitializersPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('validator.builder')) { return; diff --git a/Exception/InvalidOptionsException.php b/Exception/InvalidOptionsException.php index 8a4fba25d..9b724506a 100644 --- a/Exception/InvalidOptionsException.php +++ b/Exception/InvalidOptionsException.php @@ -22,10 +22,7 @@ public function __construct(string $message, array $options) $this->options = $options; } - /** - * @return array - */ - public function getOptions() + public function getOptions(): array { return $this->options; } diff --git a/Exception/MissingOptionsException.php b/Exception/MissingOptionsException.php index a7eda1457..835a60fe0 100644 --- a/Exception/MissingOptionsException.php +++ b/Exception/MissingOptionsException.php @@ -22,10 +22,7 @@ public function __construct(string $message, array $options) $this->options = $options; } - /** - * @return array - */ - public function getOptions() + public function getOptions(): array { return $this->options; } diff --git a/Exception/ValidationFailedException.php b/Exception/ValidationFailedException.php index 2106e0a0b..57edbd9d5 100644 --- a/Exception/ValidationFailedException.php +++ b/Exception/ValidationFailedException.php @@ -28,10 +28,7 @@ public function __construct(mixed $value, ConstraintViolationListInterface $viol parent::__construct($violations); } - /** - * @return mixed - */ - public function getValue() + public function getValue(): mixed { return $this->value; } diff --git a/Mapping/ClassMetadata.php b/Mapping/ClassMetadata.php index 17f58466c..0bd567025 100644 --- a/Mapping/ClassMetadata.php +++ b/Mapping/ClassMetadata.php @@ -321,10 +321,8 @@ public function addGetterMethodConstraints(string $property, string $method, arr /** * Merges the constraints of the given metadata into this object. - * - * @return void */ - public function mergeConstraints(self $source) + public function mergeConstraints(self $source): void { if ($source->isGroupSequenceProvider()) { $this->setGroupSequenceProvider(true); @@ -430,11 +428,9 @@ public function getReflectionClass(): \ReflectionClass /** * Sets whether a group sequence provider should be used. * - * @return void - * * @throws GroupDefinitionException */ - public function setGroupSequenceProvider(bool $active) + public function setGroupSequenceProvider(bool $active): void { if ($this->hasGroupSequence()) { throw new GroupDefinitionException('Defining a group sequence provider is not allowed with a static group sequence.'); diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index 3f0c0aa46..30dfff3a2 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -49,10 +49,8 @@ abstract class AbstractLoader implements LoaderInterface * $this->addNamespaceAlias('mynamespace', '\\Acme\\Package\\Constraints\\'); * * $constraint = $this->newConstraint('mynamespace:NotNull'); - * - * @return void */ - protected function addNamespaceAlias(string $alias, string $namespace) + protected function addNamespaceAlias(string $alias, string $namespace): void { $this->namespaces[$alias] = $namespace; } diff --git a/Mapping/MemberMetadata.php b/Mapping/MemberMetadata.php index e7389f7b8..1a3e241cb 100644 --- a/Mapping/MemberMetadata.php +++ b/Mapping/MemberMetadata.php @@ -93,10 +93,7 @@ public function getName(): string return $this->name; } - /** - * @return string - */ - public function getClassName() + public function getClassName(): string { return $this->class; } diff --git a/ObjectInitializerInterface.php b/ObjectInitializerInterface.php index 629a214a0..4d7bd854b 100644 --- a/ObjectInitializerInterface.php +++ b/ObjectInitializerInterface.php @@ -22,8 +22,5 @@ */ interface ObjectInitializerInterface { - /** - * @return void - */ - public function initialize(object $object); + public function initialize(object $object): void; } diff --git a/Resources/bin/sync-iban-formats.php b/Resources/bin/sync-iban-formats.php index fa7ba520c..4042bddf2 100755 --- a/Resources/bin/sync-iban-formats.php +++ b/Resources/bin/sync-iban-formats.php @@ -187,7 +187,7 @@ private function readIbanFormatsTable(): array { $tablesResponse = file_get_contents('https://www.wikitable2json.com/api/International_Bank_Account_Number?table=3&keyRows=1&clearRef=true'); - return json_decode($tablesResponse, true, 512, JSON_THROW_ON_ERROR)[0]; + return json_decode($tablesResponse, true, 512, \JSON_THROW_ON_ERROR)[0]; } private function buildIbanRegexp(string $countryCode, string $bbanFormat): string diff --git a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php index 5c2991bc9..a7d7fd394 100644 --- a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php +++ b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Validator\Tests\Constraints; -use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\Constraints\Luhn; use Symfony\Component\Validator\Constraints\NotCompromisedPassword; use Symfony\Component\Validator\Constraints\NotCompromisedPasswordValidator; +use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; use Symfony\Contracts\HttpClient\Exception\ExceptionInterface; @@ -223,7 +223,7 @@ public static function provideErrorSkippingConstraints(): iterable yield 'named arguments' => [new NotCompromisedPassword(skipOnError: true)]; } - private function createHttpClientStub(?string $returnValue = null): HttpClientInterface + private function createHttpClientStub(string $returnValue = null): HttpClientInterface { $httpClientStub = $this->createMock(HttpClientInterface::class); $httpClientStub->method('request')->willReturnCallback( diff --git a/Validator/TraceableValidator.php b/Validator/TraceableValidator.php index 241ce901b..a717a0d41 100644 --- a/Validator/TraceableValidator.php +++ b/Validator/TraceableValidator.php @@ -38,10 +38,7 @@ public function getCollectedData(): array return $this->collectedData; } - /** - * @return void - */ - public function reset() + public function reset(): void { $this->collectedData = []; } diff --git a/Violation/ConstraintViolationBuilderInterface.php b/Violation/ConstraintViolationBuilderInterface.php index 854a83538..195dec924 100644 --- a/Violation/ConstraintViolationBuilderInterface.php +++ b/Violation/ConstraintViolationBuilderInterface.php @@ -112,8 +112,6 @@ public function setCause(mixed $cause): static; /** * Adds the violation to the current execution context. - * - * @return void */ - public function addViolation(); + public function addViolation(): void; } From 1c2291355c692e5d29b6a9938277571e9d6dcb99 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Jul 2023 16:19:44 +0200 Subject: [PATCH 006/149] [Validator] Revert native return types on ConstraintValidatorInterface --- ConstraintValidatorInterface.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ConstraintValidatorInterface.php b/ConstraintValidatorInterface.php index 68faace61..fe7da2e8f 100644 --- a/ConstraintValidatorInterface.php +++ b/ConstraintValidatorInterface.php @@ -20,11 +20,15 @@ interface ConstraintValidatorInterface { /** * Initializes the constraint validator. + * + * @return void */ - public function initialize(ExecutionContextInterface $context): void; + public function initialize(ExecutionContextInterface $context); /** * Checks if the passed value is valid. + * + * @return void */ - public function validate(mixed $value, Constraint $constraint): void; + public function validate(mixed $value, Constraint $constraint); } From 98d233350726d6da561508d41826b6e808b263a8 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 24 Jul 2023 16:03:38 +0200 Subject: [PATCH 007/149] [Validator] Remove Doctrine annotations support --- CHANGELOG.md | 4 + Constraints/All.php | 3 - Constraints/AtLeastOneOf.php | 3 - Constraints/Bic.php | 3 - Constraints/Blank.php | 3 - Constraints/Callback.php | 3 - Constraints/CardScheme.php | 3 - Constraints/Cascade.php | 3 - Constraints/Choice.php | 3 - Constraints/Cidr.php | 3 - Constraints/Collection.php | 3 - Constraints/Count.php | 3 - Constraints/Country.php | 3 - Constraints/CssColor.php | 3 - Constraints/Currency.php | 3 - Constraints/Date.php | 3 - Constraints/DateTime.php | 3 - Constraints/DisableAutoMapping.php | 4 +- Constraints/DivisibleBy.php | 3 - Constraints/Email.php | 3 - Constraints/EnableAutoMapping.php | 4 +- Constraints/EqualTo.php | 3 - Constraints/Expression.php | 3 - Constraints/ExpressionSyntax.php | 3 - Constraints/File.php | 3 - Constraints/GreaterThan.php | 3 - Constraints/GreaterThanOrEqual.php | 3 - Constraints/GroupSequence.php | 4 - Constraints/GroupSequenceProvider.php | 4 - Constraints/Hostname.php | 3 - Constraints/Iban.php | 3 - Constraints/IdenticalTo.php | 3 - Constraints/Image.php | 3 - Constraints/Ip.php | 3 - Constraints/IsFalse.php | 3 - Constraints/IsNull.php | 3 - Constraints/IsTrue.php | 3 - Constraints/Isbn.php | 3 - Constraints/Isin.php | 3 - Constraints/Issn.php | 3 - Constraints/Json.php | 3 - Constraints/Language.php | 3 - Constraints/Length.php | 3 - Constraints/LessThan.php | 3 - Constraints/LessThanOrEqual.php | 3 - Constraints/Locale.php | 3 - Constraints/Luhn.php | 3 - Constraints/Negative.php | 3 - Constraints/NegativeOrZero.php | 3 - Constraints/NoSuspiciousCharacters.php | 3 - Constraints/NotBlank.php | 3 - Constraints/NotCompromisedPassword.php | 3 - Constraints/NotEqualTo.php | 3 - Constraints/NotIdenticalTo.php | 3 - Constraints/NotNull.php | 3 - Constraints/Optional.php | 3 - Constraints/PasswordStrength.php | 4 - Constraints/Positive.php | 3 - Constraints/PositiveOrZero.php | 3 - Constraints/Range.php | 3 - Constraints/Regex.php | 3 - Constraints/Required.php | 3 - Constraints/Sequentially.php | 3 - Constraints/Time.php | 3 - Constraints/Timezone.php | 3 - Constraints/Traverse.php | 2 - Constraints/Type.php | 3 - Constraints/Ulid.php | 2 - Constraints/Unique.php | 3 - Constraints/Url.php | 3 - Constraints/Uuid.php | 2 - Constraints/Valid.php | 3 - Constraints/When.php | 2 - Mapping/Loader/AnnotationLoader.php | 54 +----- Tests/Constraints/WhenTest.php | 120 ------------- Tests/Fixtures/Annotation/Entity.php | 160 ------------------ Tests/Fixtures/Annotation/EntityParent.php | 38 ----- .../GroupSequenceProviderEntity.php | 37 ---- Tests/Fixtures/Attribute/Entity.php | 150 ---------------- Tests/Fixtures/Attribute/EntityParent.php | 36 ---- .../Attribute/GroupSequenceProviderEntity.php | 35 ---- Tests/Fixtures/ConstraintA.php | 1 - Tests/Fixtures/ConstraintB.php | 1 - Tests/Fixtures/ConstraintC.php | 1 - Tests/Fixtures/ConstraintWithValue.php | 1 - .../Fixtures/ConstraintWithValueAsDefault.php | 1 - ...otationLoaderWithHybridAnnotationsTest.php | 82 --------- ...otationLoaderWithLegacyAnnotationsTest.php | 106 ------------ Tests/ValidatorBuilderTest.php | 46 ----- ValidatorBuilder.php | 50 +----- composer.json | 2 - 91 files changed, 8 insertions(+), 1134 deletions(-) delete mode 100644 Tests/Fixtures/Annotation/Entity.php delete mode 100644 Tests/Fixtures/Annotation/EntityParent.php delete mode 100644 Tests/Fixtures/Annotation/GroupSequenceProviderEntity.php delete mode 100644 Tests/Fixtures/Attribute/Entity.php delete mode 100644 Tests/Fixtures/Attribute/EntityParent.php delete mode 100644 Tests/Fixtures/Attribute/GroupSequenceProviderEntity.php delete mode 100644 Tests/Mapping/Loader/AnnotationLoaderWithHybridAnnotationsTest.php delete mode 100644 Tests/Mapping/Loader/AnnotationLoaderWithLegacyAnnotationsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b8c0777bd..4e6fe7900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ CHANGELOG * Remove static property `$errorNames` from all constraints, use const `ERROR_NAMES` instead * Remove `VALIDATION_MODE_LOOSE` from `Email` constraint, use `VALIDATION_MODE_HTML5` instead * Remove constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead + * Remove Doctrine annotations support in favor of native attributes + * Remove the annotation reader parameter from the constructor signature of `AnnotationLoader` + * Remove `ValidatorBuilder::setDoctrineAnnotationReader()` + * Remove `ValidatorBuilder::addDefaultDoctrineAnnotationReader()` 6.4 --- diff --git a/Constraints/All.php b/Constraints/All.php index 1461b0348..63d2d5b96 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index 48469d877..bfa33533d 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Przemysław Bogusz */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Bic.php b/Constraints/Bic.php index d976dd0a3..faeb63939 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -18,9 +18,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Michael Hirschler */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Blank.php b/Constraints/Blank.php index a7e612a29..f620815ed 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Callback.php b/Constraints/Callback.php index 82f67962b..2f0defe2c 100644 --- a/Constraints/Callback.php +++ b/Constraints/Callback.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 76dbcb9d8..260bdd4ac 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -16,9 +16,6 @@ /** * Metadata for the CardSchemeValidator. * - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Tim Nagel * @author Bernhard Schussek */ diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index 42f8784ca..763e9ac74 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** - * @Annotation - * @Target({"CLASS"}) - * * @author Jules Pietri */ #[\Attribute(\Attribute::TARGET_CLASS)] diff --git a/Constraints/Choice.php b/Constraints/Choice.php index 83cb78b76..4d9cda004 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index 0a721a45c..84d48af73 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -17,9 +17,6 @@ /** * Validates that a value is a valid CIDR notation. * - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Sorin Pop * @author Calin Bolea */ diff --git a/Constraints/Collection.php b/Constraints/Collection.php index 6a6bcf3cc..635822ca4 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Count.php b/Constraints/Count.php index 89985c398..92c9b240f 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\MissingOptionsException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Country.php b/Constraints/Country.php index 0ca6fa47d..a4a63aa48 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -16,9 +16,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 56f4e1b16..177d86756 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Mathieu Santostefano */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Currency.php b/Constraints/Currency.php index 5e4d81567..5defeac1c 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -16,9 +16,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Miha Vrhovnik * @author Bernhard Schussek */ diff --git a/Constraints/Date.php b/Constraints/Date.php index e836df8fd..de402ade4 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index d8f97c696..62f772c79 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/DisableAutoMapping.php b/Constraints/DisableAutoMapping.php index 1c09123d0..68646f5c0 100644 --- a/Constraints/DisableAutoMapping.php +++ b/Constraints/DisableAutoMapping.php @@ -17,11 +17,9 @@ /** * Disables auto mapping. * - * Using the annotations on a property has higher precedence than using it on a class, + * Using the attribute on a property has higher precedence than using it on a class, * which has higher precedence than any configuration that might be defined outside the class. * - * @Annotation - * * @author Kévin Dunglas */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)] diff --git a/Constraints/DivisibleBy.php b/Constraints/DivisibleBy.php index 941b7e07c..84355d891 100644 --- a/Constraints/DivisibleBy.php +++ b/Constraints/DivisibleBy.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Colin O'Dell */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Email.php b/Constraints/Email.php index a505d56dc..6360aab6f 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -17,9 +17,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/EnableAutoMapping.php b/Constraints/EnableAutoMapping.php index 1adefda29..fbedddd5d 100644 --- a/Constraints/EnableAutoMapping.php +++ b/Constraints/EnableAutoMapping.php @@ -17,11 +17,9 @@ /** * Enables auto mapping. * - * Using the annotations on a property has higher precedence than using it on a class, + * Using the attribute on a property has higher precedence than using it on a class, * which has higher precedence than any configuration that might be defined outside the class. * - * @Annotation - * * @author Kévin Dunglas */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)] diff --git a/Constraints/EqualTo.php b/Constraints/EqualTo.php index a6c4d1d10..fedece0e0 100644 --- a/Constraints/EqualTo.php +++ b/Constraints/EqualTo.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Expression.php b/Constraints/Expression.php index 19218e7d8..1f0f35b86 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -17,9 +17,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Fabien Potencier * @author Bernhard Schussek */ diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index a614ae43f..e3940cf38 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Andrey Sevastianov */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/File.php b/Constraints/File.php index 0716abe1c..cb774269a 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @property int $maxSize * * @author Bernhard Schussek diff --git a/Constraints/GreaterThan.php b/Constraints/GreaterThan.php index 160aa2a62..d010b0de7 100644 --- a/Constraints/GreaterThan.php +++ b/Constraints/GreaterThan.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/GreaterThanOrEqual.php b/Constraints/GreaterThanOrEqual.php index b4bed95a1..faf1c48d0 100644 --- a/Constraints/GreaterThanOrEqual.php +++ b/Constraints/GreaterThanOrEqual.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/GroupSequence.php b/Constraints/GroupSequence.php index 57122b02e..5c9a41b7b 100644 --- a/Constraints/GroupSequence.php +++ b/Constraints/GroupSequence.php @@ -44,10 +44,6 @@ * * $validator->validate($address, null, "Address") * - * @Annotation - * - * @Target({"CLASS", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_CLASS)] diff --git a/Constraints/GroupSequenceProvider.php b/Constraints/GroupSequenceProvider.php index 9b125470f..00bfc5713 100644 --- a/Constraints/GroupSequenceProvider.php +++ b/Constraints/GroupSequenceProvider.php @@ -14,10 +14,6 @@ /** * Attribute to define a group sequence provider. * - * @Annotation - * - * @Target({"CLASS", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_CLASS)] diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index c0463b335..d24166de5 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Dmitrii Poddubnyi */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Iban.php b/Constraints/Iban.php index 2fefd504c..00287f831 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Manuel Reinhard * @author Michael Schummel * @author Bernhard Schussek diff --git a/Constraints/IdenticalTo.php b/Constraints/IdenticalTo.php index 982617aa3..7ef73a047 100644 --- a/Constraints/IdenticalTo.php +++ b/Constraints/IdenticalTo.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Image.php b/Constraints/Image.php index ed2d4fa60..7306dd7bd 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Benjamin Dulau * @author Bernhard Schussek */ diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 050f31ef3..6ea33486a 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -18,9 +18,6 @@ /** * Validates that a value is a valid IP address. * - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek * @author Joseph Bielawski */ diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index 9e86383b7..f87180fb3 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index b6d9eaa1a..018ad1507 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index 0f3e2f189..045da66e6 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index 18a8e7758..b8373f591 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author The Whole Life To Learn * @author Manuel Reinhard * @author Bernhard Schussek diff --git a/Constraints/Isin.php b/Constraints/Isin.php index 90a713158..b800dec86 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Laurent Masforné */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Issn.php b/Constraints/Issn.php index e591960e9..cafb242ec 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Antonio J. García Lagar * @author Bernhard Schussek */ diff --git a/Constraints/Json.php b/Constraints/Json.php index 6facc6dba..3ef40d94d 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Imad ZAIRIG */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Language.php b/Constraints/Language.php index b0d18289e..0ff135b2b 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -16,9 +16,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Length.php b/Constraints/Length.php index 4daf59e50..9400c5ec2 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -16,9 +16,6 @@ use Symfony\Component\Validator\Exception\MissingOptionsException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/LessThan.php b/Constraints/LessThan.php index 2770c9b15..ef5dedc0e 100644 --- a/Constraints/LessThan.php +++ b/Constraints/LessThan.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/LessThanOrEqual.php b/Constraints/LessThanOrEqual.php index e2f127f07..0c2b5c851 100644 --- a/Constraints/LessThanOrEqual.php +++ b/Constraints/LessThanOrEqual.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 946785b43..43bd4127e 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -16,9 +16,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index fb76ec9a0..2fc5c8bc5 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -16,9 +16,6 @@ /** * Metadata for the LuhnValidator. * - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Tim Nagel * @author Greg Knapp http://gregk.me/2011/php-implementation-of-bank-card-luhn-algorithm/ * @author Bernhard Schussek diff --git a/Constraints/Negative.php b/Constraints/Negative.php index c13ebcb4a..0a47120d3 100644 --- a/Constraints/Negative.php +++ b/Constraints/Negative.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/NegativeOrZero.php b/Constraints/NegativeOrZero.php index 5be735c31..8161f3b02 100644 --- a/Constraints/NegativeOrZero.php +++ b/Constraints/NegativeOrZero.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/NoSuspiciousCharacters.php b/Constraints/NoSuspiciousCharacters.php index 7548703f3..07cae710d 100644 --- a/Constraints/NoSuspiciousCharacters.php +++ b/Constraints/NoSuspiciousCharacters.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Mathieu Lechat */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index 02d6d5c79..a9d7d6360 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek * @author Kévin Dunglas */ diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index ae90925f7..afd957167 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -16,9 +16,6 @@ /** * Checks if a password has been leaked in a data breach. * - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Kévin Dunglas */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/NotEqualTo.php b/Constraints/NotEqualTo.php index 8ddc2d334..aad8beb1c 100644 --- a/Constraints/NotEqualTo.php +++ b/Constraints/NotEqualTo.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/NotIdenticalTo.php b/Constraints/NotIdenticalTo.php index 80628135a..7107c8b1e 100644 --- a/Constraints/NotIdenticalTo.php +++ b/Constraints/NotIdenticalTo.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 8d4f2e211..8b3ebc16c 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Optional.php b/Constraints/Optional.php index dab8b4371..df331a676 100644 --- a/Constraints/Optional.php +++ b/Constraints/Optional.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"ANNOTATION"}) - * * @author Bernhard Schussek */ class Optional extends Existence diff --git a/Constraints/PasswordStrength.php b/Constraints/PasswordStrength.php index 816708d67..cb5792800 100644 --- a/Constraints/PasswordStrength.php +++ b/Constraints/PasswordStrength.php @@ -15,10 +15,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** - * @Annotation - * - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Florent Morselli */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Positive.php b/Constraints/Positive.php index 951e944c9..5385dc503 100644 --- a/Constraints/Positive.php +++ b/Constraints/Positive.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/PositiveOrZero.php b/Constraints/PositiveOrZero.php index a7669c610..d73228193 100644 --- a/Constraints/PositiveOrZero.php +++ b/Constraints/PositiveOrZero.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Range.php b/Constraints/Range.php index da01a5488..97203957c 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -18,9 +18,6 @@ use Symfony\Component\Validator\Exception\MissingOptionsException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 9062819e1..ac740b802 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Required.php b/Constraints/Required.php index bd77a909f..2a1a27f6a 100644 --- a/Constraints/Required.php +++ b/Constraints/Required.php @@ -12,9 +12,6 @@ namespace Symfony\Component\Validator\Constraints; /** - * @Annotation - * @Target({"ANNOTATION"}) - * * @author Bernhard Schussek */ class Required extends Existence diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index 5eea1ebba..0ee6fb1f1 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -15,9 +15,6 @@ * Use this constraint to sequentially validate nested constraints. * Validation for the nested constraints collection will stop at first violation. * - * @Annotation - * @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Maxime Steinhausser */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Time.php b/Constraints/Time.php index 994473dc1..35e2dcd61 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index 1ba24be9c..9c82c98b5 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Javier Spagnoletti * @author Hugo Hamon */ diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index 2b9930661..ab862cd79 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -15,8 +15,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** - * @Annotation - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_CLASS)] diff --git a/Constraints/Type.php b/Constraints/Type.php index cf61476c4..eabb22d43 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index a1f2c086e..19055ee0a 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -14,8 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * * @author Laurent Clouet */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Unique.php b/Constraints/Unique.php index d85de6c74..1d4b23b67 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Yevgeniy Zholkevskiy */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Url.php b/Constraints/Url.php index 5575e2c2b..08fffae7a 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -15,9 +15,6 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index f397143b1..f34413b2b 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -15,8 +15,6 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** - * @Annotation - * * @author Colin O'Dell * @author Bernhard Schussek */ diff --git a/Constraints/Valid.php b/Constraints/Valid.php index 5acf87473..a924c304e 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -14,9 +14,6 @@ use Symfony\Component\Validator\Constraint; /** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/When.php b/Constraints/When.php index bf10049e4..a28ee8b4a 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -17,8 +17,6 @@ use Symfony\Component\Validator\Exception\LogicException; /** - * @Annotation - * * @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"}) */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Mapping/Loader/AnnotationLoader.php b/Mapping/Loader/AnnotationLoader.php index a3cdfb120..a8d09da2a 100644 --- a/Mapping/Loader/AnnotationLoader.php +++ b/Mapping/Loader/AnnotationLoader.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Validator\Mapping\Loader; -use Doctrine\Common\Annotations\Reader; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\GroupSequence; @@ -20,29 +19,13 @@ use Symfony\Component\Validator\Mapping\ClassMetadata; /** - * Loads validation metadata using a Doctrine annotation {@link Reader} or using PHP 8 attributes. + * Loads validation metadata using PHP attributes. * * @author Bernhard Schussek * @author Alexander M. Turek */ class AnnotationLoader implements LoaderInterface { - /** - * @deprecated since Symfony 6.4, this property will be removed in 7.0 - * - * @var Reader|null - */ - protected $reader; - - public function __construct(Reader $reader = null) - { - if ($reader) { - trigger_deprecation('symfony/validator', '6.4', 'Passing a "%s" instance as argument 1 to "%s()" is deprecated, pass null or omit the parameter instead.', get_debug_type($reader), __METHOD__); - } - - $this->reader = $reader; - } - public function loadClassMetadata(ClassMetadata $metadata): bool { $reflClass = $metadata->getReflectionClass(); @@ -98,49 +81,14 @@ public function loadClassMetadata(ClassMetadata $metadata): bool private function getAnnotations(\ReflectionMethod|\ReflectionClass|\ReflectionProperty $reflection): iterable { - $dedup = []; - foreach ($reflection->getAttributes(GroupSequence::class) as $attribute) { - $dedup[] = $attribute->newInstance(); yield $attribute->newInstance(); } foreach ($reflection->getAttributes(GroupSequenceProvider::class) as $attribute) { - $dedup[] = $attribute->newInstance(); yield $attribute->newInstance(); } foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { - $dedup[] = $attribute->newInstance(); yield $attribute->newInstance(); } - if (!$this->reader) { - return; - } - - $annotations = []; - - if ($reflection instanceof \ReflectionClass && $annotations = $this->reader->getClassAnnotations($reflection)) { - trigger_deprecation('symfony/validator', '6.4', 'Class "%s" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.', $reflection->getName()); - } - if ($reflection instanceof \ReflectionMethod && $annotations = $this->reader->getMethodAnnotations($reflection)) { - trigger_deprecation('symfony/validator', '6.4', 'Method "%s::%s()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.', $reflection->getDeclaringClass()->getName(), $reflection->getName()); - } - if ($reflection instanceof \ReflectionProperty && $annotations = $this->reader->getPropertyAnnotations($reflection)) { - trigger_deprecation('symfony/validator', '6.4', 'Property "%s::$%s" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.', $reflection->getDeclaringClass()->getName(), $reflection->getName()); - } - - foreach ($dedup as $annotation) { - if ($annotation instanceof Constraint) { - $annotation->groups; // trigger initialization of the "groups" property - } - } - - foreach ($annotations as $annotation) { - if ($annotation instanceof Constraint) { - $annotation->groups; // trigger initialization of the "groups" property - } - if (!\in_array($annotation, $dedup, false)) { - yield $annotation; - } - } } } diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index b3305b3c4..db7d615b5 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -11,9 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; -use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; @@ -26,8 +24,6 @@ final class WhenTest extends TestCase { - use ExpectDeprecationTrait; - public function testMissingOptionsExceptionIsThrown() { $this->expectException(MissingOptionsException::class); @@ -45,89 +41,6 @@ public function testNonConstraintsAreRejected() ]); } - /** - * @group legacy - */ - public function testAnnotations() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - - $loader = new AnnotationLoader(new AnnotationReader()); - $metadata = new ClassMetadata(WhenTestWithAnnotations::class); - - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Constraints\WhenTestWithAnnotations" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Constraints\WhenTestWithAnnotations::$foo" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Constraints\WhenTestWithAnnotations::$bar" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Constraints\WhenTestWithAnnotations::$qux" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Constraints\WhenTestWithAnnotations::getBaz()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - self::assertTrue($loader->loadClassMetadata($metadata)); - - [$classConstraint] = $metadata->getConstraints(); - - self::assertInstanceOf(When::class, $classConstraint); - self::assertSame('true', $classConstraint->expression); - self::assertEquals([ - new Callback([ - 'callback' => 'callback', - 'groups' => ['Default', 'WhenTestWithAnnotations'], - ]), - ], $classConstraint->constraints); - - [$fooConstraint] = $metadata->properties['foo']->getConstraints(); - - self::assertInstanceOf(When::class, $fooConstraint); - self::assertSame('true', $fooConstraint->expression); - self::assertEquals([ - new NotNull([ - 'groups' => ['Default', 'WhenTestWithAnnotations'], - ]), - new NotBlank([ - 'groups' => ['Default', 'WhenTestWithAnnotations'], - ]), - ], $fooConstraint->constraints); - self::assertSame(['Default', 'WhenTestWithAnnotations'], $fooConstraint->groups); - - [$barConstraint] = $metadata->properties['bar']->getConstraints(); - - self::assertInstanceOf(When::class, $fooConstraint); - self::assertSame('false', $barConstraint->expression); - self::assertEquals([ - new NotNull([ - 'groups' => ['foo'], - ]), - new NotBlank([ - 'groups' => ['foo'], - ]), - ], $barConstraint->constraints); - self::assertSame(['foo'], $barConstraint->groups); - - [$quxConstraint] = $metadata->properties['qux']->getConstraints(); - - self::assertInstanceOf(When::class, $quxConstraint); - self::assertSame('true', $quxConstraint->expression); - self::assertEquals([ - new NotNull([ - 'groups' => ['foo'], - ]), - ], $quxConstraint->constraints); - self::assertSame(['foo'], $quxConstraint->groups); - - [$bazConstraint] = $metadata->getters['baz']->getConstraints(); - - self::assertInstanceOf(When::class, $bazConstraint); - self::assertSame('true', $bazConstraint->expression); - self::assertEquals([ - new NotNull([ - 'groups' => ['Default', 'WhenTestWithAnnotations'], - ]), - new NotBlank([ - 'groups' => ['Default', 'WhenTestWithAnnotations'], - ]), - ], $bazConstraint->constraints); - self::assertSame(['Default', 'WhenTestWithAnnotations'], $bazConstraint->groups); - } - public function testAttributes() { $loader = new AnnotationLoader(); @@ -200,36 +113,3 @@ public function testAttributes() self::assertSame(['Default', 'WhenTestWithAttributes'], $bazConstraint->groups); } } - -/** - * @When(expression="true", constraints={@Callback("callback")}) - */ -class WhenTestWithAnnotations -{ - /** - * @When(expression="true", constraints={@NotNull, @NotBlank}) - */ - private $foo; - - /** - * @When(expression="false", constraints={@NotNull, @NotBlank}, groups={"foo"}) - */ - private $bar; - - /** - * @When(expression="true", constraints=@NotNull, groups={"foo"}) - */ - private $qux; - - /** - * @When(expression="true", constraints={@NotNull, @NotBlank}) - */ - public function getBaz() - { - return null; - } - - public function callback() - { - } -} diff --git a/Tests/Fixtures/Annotation/Entity.php b/Tests/Fixtures/Annotation/Entity.php deleted file mode 100644 index bbfa452b6..000000000 --- a/Tests/Fixtures/Annotation/Entity.php +++ /dev/null @@ -1,160 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Fixtures\Annotation; - -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceB; - -/** - * @Symfony\Component\Validator\Tests\Fixtures\ConstraintA - * @Assert\GroupSequence({"Foo", "Entity"}) - * @Assert\Callback({"Symfony\Component\Validator\Tests\Fixtures\CallbackClass", "callback"}) - * @Assert\Sequentially({ - * @Assert\Expression("this.getFirstName() != null") - * }) - */ -class Entity extends EntityParent implements EntityInterfaceB -{ - /** - * @Assert\NotNull - * @Assert\Range(min=3) - * @Assert\All({@Assert\NotNull, @Assert\Range(min=3)}), - * @Assert\All(constraints={@Assert\NotNull, @Assert\Range(min=3)}) - * @Assert\Collection(fields={ - * "foo" = {@Assert\NotNull, @Assert\Range(min=3)}, - * "bar" = @Assert\Range(min=5), - * "baz" = @Assert\Required({@Assert\Email()}), - * "qux" = @Assert\Optional({@Assert\NotBlank()}) - * }, allowExtraFields=true) - * @Assert\Choice(choices={"A", "B"}, message="Must be one of %choices%") - * @Assert\AtLeastOneOf({@Assert\NotNull, @Assert\Range(min=3)}, message="foo", includeInternalMessages=false) - * @Assert\Sequentially({@Assert\NotBlank, @Assert\Range(min=5)}) - */ - public $firstName; - /** - * @Assert\Valid - */ - public $childA; - /** - * @Assert\Valid - */ - public $childB; - protected $lastName; - public $reference; - public $reference2; - private $internal; - public $data = 'Overridden data'; - public $initialized = false; - /** - * @Assert\Type("integer") - */ - protected ?int $other; - - public function __construct($internal = null) - { - $this->internal = $internal; - } - - public function getFirstName() - { - return $this->firstName; - } - - public function getInternal() - { - return $this->internal.' from getter'; - } - - public function setLastName($lastName) - { - $this->lastName = $lastName; - } - - /** - * @Assert\NotNull - */ - public function getLastName() - { - return $this->lastName; - } - - public function getValid() - { - } - - /** - * @Assert\IsTrue - */ - public function isValid() - { - return 'valid'; - } - - /** - * @Assert\IsTrue - */ - public function hasPermissions() - { - return 'permissions'; - } - - public function getData() - { - return 'Overridden data'; - } - - /** - * @Assert\Callback(payload="foo") - */ - public function validateMe(ExecutionContextInterface $context) - { - } - - /** - * @Assert\Callback - */ - public static function validateMeStatic($object, ExecutionContextInterface $context) - { - } - - public function getChildA(): mixed - { - return $this->childA; - } - - /** - * @param mixed $childA - */ - public function setChildA($childA) - { - $this->childA = $childA; - } - - public function getChildB(): mixed - { - return $this->childB; - } - - /** - * @param mixed $childB - */ - public function setChildB($childB) - { - $this->childB = $childB; - } - - public function getReference() - { - return $this->reference; - } -} diff --git a/Tests/Fixtures/Annotation/EntityParent.php b/Tests/Fixtures/Annotation/EntityParent.php deleted file mode 100644 index d938043ca..000000000 --- a/Tests/Fixtures/Annotation/EntityParent.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Fixtures\Annotation; - -use Symfony\Component\Validator\Constraints\NotNull; -use Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceA; - -class EntityParent implements EntityInterfaceA -{ - protected $firstName; - private $internal; - private $data = 'Data'; - private $child; - - /** - * @NotNull - */ - protected ?int $other; - - public function getData() - { - return 'Data'; - } - - public function getChild() - { - return $this->child; - } -} diff --git a/Tests/Fixtures/Annotation/GroupSequenceProviderEntity.php b/Tests/Fixtures/Annotation/GroupSequenceProviderEntity.php deleted file mode 100644 index 447cb6f35..000000000 --- a/Tests/Fixtures/Annotation/GroupSequenceProviderEntity.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Fixtures\Annotation; - -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\Constraints\GroupSequence; -use Symfony\Component\Validator\GroupSequenceProviderInterface; - -/** - * @Assert\GroupSequenceProvider - */ -class GroupSequenceProviderEntity implements GroupSequenceProviderInterface -{ - public $firstName; - public $lastName; - - protected $sequence = []; - - public function __construct($sequence) - { - $this->sequence = $sequence; - } - - public function getGroupSequence(): array|GroupSequence - { - return $this->sequence; - } -} diff --git a/Tests/Fixtures/Attribute/Entity.php b/Tests/Fixtures/Attribute/Entity.php deleted file mode 100644 index aa954144c..000000000 --- a/Tests/Fixtures/Attribute/Entity.php +++ /dev/null @@ -1,150 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Fixtures\Attribute; - -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Tests\Fixtures\CallbackClass; -use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; -use Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceB; - -#[ - ConstraintA, - Assert\GroupSequence(['Foo', 'Entity']), - Assert\Callback([CallbackClass::class, 'callback']), -] -/** - * @Assert\Sequentially({ - * @Assert\Expression("this.getFirstName() != null") - * }) - */ -class Entity extends EntityParent implements EntityInterfaceB -{ - /** - * @Assert\All({@Assert\NotNull, @Assert\Range(min=3)}), - * @Assert\All(constraints={@Assert\NotNull, @Assert\Range(min=3)}) - * @Assert\Collection(fields={ - * "foo" = {@Assert\NotNull, @Assert\Range(min=3)}, - * "bar" = @Assert\Range(min=5), - * "baz" = @Assert\Required({@Assert\Email()}), - * "qux" = @Assert\Optional({@Assert\NotBlank()}) - * }, allowExtraFields=true) - * @Assert\Choice(choices={"A", "B"}, message="Must be one of %choices%") - * @Assert\AtLeastOneOf({@Assert\NotNull, @Assert\Range(min=3)}, message="foo", includeInternalMessages=false) - * @Assert\Sequentially({@Assert\NotBlank, @Assert\Range(min=5)}) - */ - #[ - Assert\NotNull, - Assert\Range(min: 3), - ] - public string $firstName; - #[Assert\Valid] - public $childA; - #[Assert\Valid] - public $childB; - protected $lastName; - public $reference; - public $reference2; - private $internal; - public $data = 'Overridden data'; - public $initialized = false; - #[Assert\Type('integer')] - protected ?int $other; - - public function __construct($internal = null) - { - $this->internal = $internal; - } - - public function getFirstName() - { - return $this->firstName; - } - - public function getInternal() - { - return $this->internal.' from getter'; - } - - public function setLastName($lastName) - { - $this->lastName = $lastName; - } - - #[Assert\NotNull] - public function getLastName() - { - return $this->lastName; - } - - public function getValid() - { - } - - #[Assert\IsTrue] - public function isValid() - { - return 'valid'; - } - - #[Assert\IsTrue] - public function hasPermissions() - { - return 'permissions'; - } - - public function getData() - { - return 'Overridden data'; - } - - #[Assert\Callback(payload: 'foo')] - public function validateMe(ExecutionContextInterface $context) - { - } - - #[Assert\Callback] - public static function validateMeStatic($object, ExecutionContextInterface $context) - { - } - - public function getChildA(): mixed - { - return $this->childA; - } - - /** - * @param mixed $childA - */ - public function setChildA($childA) - { - $this->childA = $childA; - } - - public function getChildB(): mixed - { - return $this->childB; - } - - /** - * @param mixed $childB - */ - public function setChildB($childB) - { - $this->childB = $childB; - } - - public function getReference() - { - return $this->reference; - } -} diff --git a/Tests/Fixtures/Attribute/EntityParent.php b/Tests/Fixtures/Attribute/EntityParent.php deleted file mode 100644 index ca72c0fa9..000000000 --- a/Tests/Fixtures/Attribute/EntityParent.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Fixtures\Attribute; - -use Symfony\Component\Validator\Constraints\NotNull; -use Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceA; - -class EntityParent implements EntityInterfaceA -{ - protected string $firstName; - private $internal; - private $data = 'Data'; - private $child; - - #[NotNull] - protected ?int $other; - - public function getData() - { - return 'Data'; - } - - public function getChild() - { - return $this->child; - } -} diff --git a/Tests/Fixtures/Attribute/GroupSequenceProviderEntity.php b/Tests/Fixtures/Attribute/GroupSequenceProviderEntity.php deleted file mode 100644 index db16eef46..000000000 --- a/Tests/Fixtures/Attribute/GroupSequenceProviderEntity.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Fixtures\Attribute; - -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\Constraints\GroupSequence; -use Symfony\Component\Validator\GroupSequenceProviderInterface; - -#[Assert\GroupSequenceProvider] -class GroupSequenceProviderEntity implements GroupSequenceProviderInterface -{ - public $firstName; - public $lastName; - - protected $sequence = []; - - public function __construct($sequence) - { - $this->sequence = $sequence; - } - - public function getGroupSequence(): array|GroupSequence - { - return $this->sequence; - } -} diff --git a/Tests/Fixtures/ConstraintA.php b/Tests/Fixtures/ConstraintA.php index b5cf5ac76..51e8ae6a7 100644 --- a/Tests/Fixtures/ConstraintA.php +++ b/Tests/Fixtures/ConstraintA.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; -/** @Annotation */ #[\Attribute] class ConstraintA extends Constraint { diff --git a/Tests/Fixtures/ConstraintB.php b/Tests/Fixtures/ConstraintB.php index 53d43d01f..c70329b8e 100644 --- a/Tests/Fixtures/ConstraintB.php +++ b/Tests/Fixtures/ConstraintB.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; -/** @Annotation */ class ConstraintB extends Constraint { public function getTargets(): string|array diff --git a/Tests/Fixtures/ConstraintC.php b/Tests/Fixtures/ConstraintC.php index e87ed28f4..8143420ac 100644 --- a/Tests/Fixtures/ConstraintC.php +++ b/Tests/Fixtures/ConstraintC.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; -/** @Annotation */ class ConstraintC extends Constraint { public $option1; diff --git a/Tests/Fixtures/ConstraintWithValue.php b/Tests/Fixtures/ConstraintWithValue.php index c1f672793..ef64a655f 100644 --- a/Tests/Fixtures/ConstraintWithValue.php +++ b/Tests/Fixtures/ConstraintWithValue.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; -/** @Annotation */ class ConstraintWithValue extends Constraint { public $property; diff --git a/Tests/Fixtures/ConstraintWithValueAsDefault.php b/Tests/Fixtures/ConstraintWithValueAsDefault.php index 1cddd4a55..8a4944c46 100644 --- a/Tests/Fixtures/ConstraintWithValueAsDefault.php +++ b/Tests/Fixtures/ConstraintWithValueAsDefault.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; -/** @Annotation */ class ConstraintWithValueAsDefault extends Constraint { public $property; diff --git a/Tests/Mapping/Loader/AnnotationLoaderWithHybridAnnotationsTest.php b/Tests/Mapping/Loader/AnnotationLoaderWithHybridAnnotationsTest.php deleted file mode 100644 index 2e34d7d6e..000000000 --- a/Tests/Mapping/Loader/AnnotationLoaderWithHybridAnnotationsTest.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Mapping\Loader; - -use Doctrine\Common\Annotations\AnnotationReader; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; - -/** - * @group legacy - */ -class AnnotationLoaderWithHybridAnnotationsTest extends AnnotationLoaderTest -{ - use ExpectDeprecationTrait; - - public function testLoadClassMetadataReturnsTrueIfSuccessful() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Attribute\Entity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Attribute\Entity::$firstName" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadClassMetadataReturnsTrueIfSuccessful(); - } - - public function testLoadClassMetadataReturnsFalseIfNotSuccessful() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - - parent::testLoadClassMetadataReturnsFalseIfNotSuccessful(); - } - - public function testLoadClassMetadata() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Attribute\Entity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Attribute\Entity::$firstName" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadClassMetadata(); - } - - public function testLoadParentClassMetadata() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - - parent::testLoadParentClassMetadata(); - } - - public function testLoadClassMetadataAndMerge() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Attribute\Entity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Attribute\Entity::$firstName" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadClassMetadataAndMerge(); - } - - public function testLoadGroupSequenceProviderAnnotation() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - - parent::testLoadGroupSequenceProviderAnnotation(); - } - - protected function createAnnotationLoader(): AnnotationLoader - { - return new AnnotationLoader(new AnnotationReader()); - } - - protected function getFixtureNamespace(): string - { - return 'Symfony\Component\Validator\Tests\Fixtures\Attribute'; - } -} diff --git a/Tests/Mapping/Loader/AnnotationLoaderWithLegacyAnnotationsTest.php b/Tests/Mapping/Loader/AnnotationLoaderWithLegacyAnnotationsTest.php deleted file mode 100644 index 821fce12b..000000000 --- a/Tests/Mapping/Loader/AnnotationLoaderWithLegacyAnnotationsTest.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Mapping\Loader; - -use Doctrine\Common\Annotations\AnnotationReader; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; - -/** - * @group legacy - */ -class AnnotationLoaderWithLegacyAnnotationsTest extends AnnotationLoaderTest -{ - use ExpectDeprecationTrait; - - public function testLoadClassMetadataReturnsTrueIfSuccessful() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$firstName" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$childA" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$childB" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::getLastName()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::isValid()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::hasPermissions()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::validateMe()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::validateMeStatic()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadClassMetadataReturnsTrueIfSuccessful(); - } - - public function testLoadClassMetadataReturnsFalseIfNotSuccessful() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - - parent::testLoadClassMetadataReturnsFalseIfNotSuccessful(); - } - - public function testLoadClassMetadata() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$firstName" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$childA" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$childB" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::getLastName()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::isValid()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::hasPermissions()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::validateMe()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::validateMeStatic()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadClassMetadata(); - } - - public function testLoadParentClassMetadata() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\EntityParent::$other" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadParentClassMetadata(); - } - - public function testLoadClassMetadataAndMerge() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\EntityParent::$other" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$firstName" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$childA" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Property "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::$childB" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::getLastName()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::isValid()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::hasPermissions()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::validateMe()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity::validateMeStatic()" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadClassMetadataAndMerge(); - } - - public function testLoadGroupSequenceProviderAnnotation() - { - $this->expectDeprecation('Since symfony/validator 6.4: Passing a "Doctrine\Common\Annotations\AnnotationReader" instance as argument 1 to "Symfony\Component\Validator\Mapping\Loader\AnnotationLoader::__construct()" is deprecated, pass null or omit the parameter instead.'); - $this->expectDeprecation('Since symfony/validator 6.4: Class "Symfony\Component\Validator\Tests\Fixtures\Annotation\GroupSequenceProviderEntity" uses Doctrine Annotations to configure validation constraints, which is deprecated. Use PHP attributes instead.'); - - parent::testLoadGroupSequenceProviderAnnotation(); - } - - protected function createAnnotationLoader(): AnnotationLoader - { - return new AnnotationLoader(new AnnotationReader()); - } - - protected function getFixtureNamespace(): string - { - return 'Symfony\Component\Validator\Tests\Fixtures\Annotation'; - } -} diff --git a/Tests/ValidatorBuilderTest.php b/Tests/ValidatorBuilderTest.php index 9d0b1b514..ac3d698af 100644 --- a/Tests/ValidatorBuilderTest.php +++ b/Tests/ValidatorBuilderTest.php @@ -11,13 +11,9 @@ namespace Symfony\Component\Validator\Tests; -use Doctrine\Common\Annotations\PsrCachedReader; -use Doctrine\Common\Annotations\Reader; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\Validator\Validator\RecursiveValidator; use Symfony\Component\Validator\ValidatorBuilder; @@ -25,8 +21,6 @@ class ValidatorBuilderTest extends TestCase { - use ExpectDeprecationTrait; - private ValidatorBuilder $builder; protected function setUp(): void @@ -81,46 +75,6 @@ public function testAddMethodMappings() $this->assertSame($this->builder, $this->builder->addMethodMappings([])); } - /** - * @group legacy - */ - public function testEnableAnnotationMappingWithDefaultDoctrineAnnotationReader() - { - $this->assertSame($this->builder, $this->builder->enableAnnotationMapping()); - - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\ValidatorBuilder::addDefaultDoctrineAnnotationReader()" is deprecated without replacement.'); - $this->assertSame($this->builder, $this->builder->addDefaultDoctrineAnnotationReader()); - - $loaders = $this->builder->getLoaders(); - $this->assertCount(1, $loaders); - $this->assertInstanceOf(AnnotationLoader::class, $loaders[0]); - - $r = new \ReflectionProperty(AnnotationLoader::class, 'reader'); - - $this->assertInstanceOf(PsrCachedReader::class, $r->getValue($loaders[0])); - } - - /** - * @group legacy - */ - public function testEnableAnnotationMappingWithCustomDoctrineAnnotationReader() - { - $reader = $this->createMock(Reader::class); - - $this->assertSame($this->builder, $this->builder->enableAnnotationMapping()); - - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\ValidatorBuilder::setDoctrineAnnotationReader()" is deprecated without replacement.'); - $this->assertSame($this->builder, $this->builder->setDoctrineAnnotationReader($reader)); - - $loaders = $this->builder->getLoaders(); - $this->assertCount(1, $loaders); - $this->assertInstanceOf(AnnotationLoader::class, $loaders[0]); - - $r = new \ReflectionProperty(AnnotationLoader::class, 'reader'); - - $this->assertSame($reader, $r->getValue($loaders[0])); - } - public function testDisableAnnotationMapping() { $this->assertSame($this->builder, $this->builder->disableAnnotationMapping()); diff --git a/ValidatorBuilder.php b/ValidatorBuilder.php index 88fd21645..f0184a58b 100644 --- a/ValidatorBuilder.php +++ b/ValidatorBuilder.php @@ -11,13 +11,8 @@ namespace Symfony\Component\Validator; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\PsrCachedReader; -use Doctrine\Common\Annotations\Reader; use Psr\Cache\CacheItemPoolInterface; -use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Validator\Context\ExecutionContextFactory; -use Symfony\Component\Validator\Exception\LogicException; use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; @@ -48,7 +43,6 @@ class ValidatorBuilder private array $xmlMappings = []; private array $yamlMappings = []; private array $methodMappings = []; - private ?Reader $annotationReader = null; private bool $enableAnnotationMapping = false; private ?MetadataFactoryInterface $metadataFactory = null; private ConstraintValidatorFactoryInterface $validatorFactory; @@ -210,35 +204,6 @@ public function enableAnnotationMapping(): static public function disableAnnotationMapping(): static { $this->enableAnnotationMapping = false; - $this->annotationReader = null; - - return $this; - } - - /** - * @deprecated since Symfony 6.4 without replacement - * - * @return $this - */ - public function setDoctrineAnnotationReader(?Reader $reader): static - { - trigger_deprecation('symfony/validator', '6.4', 'Method "%s()" is deprecated without replacement.', __METHOD__); - - $this->annotationReader = $reader; - - return $this; - } - - /** - * @deprecated since Symfony 6.4 without replacement - * - * @return $this - */ - public function addDefaultDoctrineAnnotationReader(): static - { - trigger_deprecation('symfony/validator', '6.4', 'Method "%s()" is deprecated without replacement.', __METHOD__); - - $this->annotationReader = $this->createAnnotationReader(); return $this; } @@ -345,7 +310,7 @@ public function getLoaders(): array } if ($this->enableAnnotationMapping) { - $loaders[] = new AnnotationLoader($this->annotationReader); + $loaders[] = new AnnotationLoader(); } return array_merge($loaders, $this->loaders); @@ -389,17 +354,4 @@ public function getValidator(): ValidatorInterface return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers); } - - private function createAnnotationReader(): Reader - { - if (!class_exists(AnnotationReader::class)) { - throw new LogicException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and symfony/cache to be installed.'); - } - - if (class_exists(ArrayAdapter::class)) { - return new PsrCachedReader(new AnnotationReader(), new ArrayAdapter()); - } - - throw new LogicException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and symfony/cache to be installed.'); - } } diff --git a/composer.json b/composer.json index 898e318ad..a9d5cced1 100644 --- a/composer.json +++ b/composer.json @@ -38,11 +38,9 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", - "doctrine/annotations": "^1.13|^2", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { - "doctrine/annotations": "<1.13", "doctrine/lexer": "<1.1", "symfony/dependency-injection": "<6.4", "symfony/expression-language": "<6.4", From d847f3377ba23aedffbe75bdfa95fe3b3c1d0339 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 26 Jul 2023 14:06:14 +0200 Subject: [PATCH 008/149] fix changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6fe7900..039af1fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Add method `__toString()` to `ConstraintViolationListInterface` * Add method `disableTranslation()` to `ConstraintViolationBuilderInterface` * Remove static property `$errorNames` from all constraints, use const `ERROR_NAMES` instead + * Remove static property `$versions` from the `Ip` constraint, use the `VERSIONS` constant instead * Remove `VALIDATION_MODE_LOOSE` from `Email` constraint, use `VALIDATION_MODE_HTML5` instead * Remove constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead * Remove Doctrine annotations support in favor of native attributes From 6521a8599eb781220ac0cb1370a77ab1bc9d1d95 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 15:36:26 +0200 Subject: [PATCH 009/149] Add types to public and protected properties --- Constraint.php | 4 +- ConstraintValidator.php | 5 +- ConstraintValidatorFactory.php | 2 +- Constraints/AbstractComparison.php | 6 +- Constraints/All.php | 4 +- Constraints/AtLeastOneOf.php | 10 ++- Constraints/Bic.php | 8 +- Constraints/Blank.php | 2 +- Constraints/CardScheme.php | 4 +- Constraints/CardSchemeValidator.php | 2 +- Constraints/Choice.php | 18 ++--- Constraints/Cidr.php | 14 ++-- Constraints/Collection.php | 18 ++--- Constraints/Compound.php | 2 +- Constraints/Count.php | 14 ++-- Constraints/Country.php | 4 +- Constraints/CssColor.php | 6 +- Constraints/Currency.php | 2 +- Constraints/Date.php | 2 +- Constraints/DateTime.php | 4 +- Constraints/DivisibleBy.php | 2 +- Constraints/Email.php | 4 +- Constraints/EqualTo.php | 2 +- Constraints/Existence.php | 4 +- Constraints/Expression.php | 6 +- Constraints/ExpressionSyntax.php | 6 +- Constraints/File.php | 40 +++++----- Constraints/GreaterThan.php | 2 +- Constraints/GreaterThanOrEqual.php | 2 +- Constraints/GroupSequence.php | 6 +- Constraints/Hostname.php | 4 +- Constraints/Iban.php | 2 +- Constraints/IdenticalTo.php | 2 +- Constraints/Image.php | 54 ++++++------- Constraints/Ip.php | 6 +- Constraints/IsFalse.php | 2 +- Constraints/IsNull.php | 2 +- Constraints/IsTrue.php | 2 +- Constraints/Isbn.php | 10 +-- Constraints/Isin.php | 2 +- Constraints/Issn.php | 6 +- Constraints/Json.php | 2 +- Constraints/Language.php | 4 +- Constraints/Length.php | 14 ++-- Constraints/LessThan.php | 2 +- Constraints/LessThanOrEqual.php | 2 +- Constraints/Locale.php | 4 +- Constraints/Luhn.php | 2 +- Constraints/Negative.php | 2 +- Constraints/NegativeOrZero.php | 2 +- Constraints/NotBlank.php | 4 +- Constraints/NotCompromisedPassword.php | 6 +- Constraints/NotEqualTo.php | 2 +- Constraints/NotIdenticalTo.php | 2 +- Constraints/NotNull.php | 2 +- Constraints/Positive.php | 2 +- Constraints/PositiveOrZero.php | 2 +- Constraints/Range.php | 18 ++--- Constraints/Regex.php | 8 +- Constraints/Sequentially.php | 4 +- Constraints/Time.php | 2 +- Constraints/Timezone.php | 8 +- Constraints/Traverse.php | 2 +- Constraints/Type.php | 4 +- Constraints/Ulid.php | 2 +- Constraints/Unique.php | 2 +- Constraints/Url.php | 6 +- Constraints/Uuid.php | 10 +-- Constraints/Valid.php | 2 +- Constraints/When.php | 6 +- .../Factory/LazyLoadingMetadataFactory.php | 6 +- Mapping/Loader/AbstractLoader.php | 2 +- Mapping/Loader/FileLoader.php | 2 +- Mapping/Loader/LoaderChain.php | 2 +- Mapping/Loader/StaticMethodLoader.php | 2 +- Mapping/Loader/XmlFileLoader.php | 4 +- Mapping/Loader/YamlFileLoader.php | 7 +- Test/ConstraintValidatorTestCase.php | 27 +++---- Tests/Constraints/CollectionTest.php | 16 ---- Tests/Constraints/ImageValidatorTest.php | 80 ------------------- Validator/RecursiveValidator.php | 8 +- 81 files changed, 233 insertions(+), 348 deletions(-) diff --git a/Constraint.php b/Constraint.php index 50314963f..902ff04b3 100644 --- a/Constraint.php +++ b/Constraint.php @@ -52,14 +52,14 @@ abstract class Constraint /** * Domain-specific data attached to a constraint. */ - public $payload; + public mixed $payload; /** * The groups that the constraint belongs to. * * @var string[] */ - public $groups; + public ?array $groups = null; /** * Returns the name of the given error code. diff --git a/ConstraintValidator.php b/ConstraintValidator.php index f9dceaf54..75f3195b8 100644 --- a/ConstraintValidator.php +++ b/ConstraintValidator.php @@ -31,10 +31,7 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface */ public const OBJECT_TO_STRING = 2; - /** - * @var ExecutionContextInterface - */ - protected $context; + protected ExecutionContextInterface $context; public function initialize(ExecutionContextInterface $context): void { diff --git a/ConstraintValidatorFactory.php b/ConstraintValidatorFactory.php index 778e202a8..51ea6e07a 100644 --- a/ConstraintValidatorFactory.php +++ b/ConstraintValidatorFactory.php @@ -24,7 +24,7 @@ */ class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface { - protected $validators = []; + protected array $validators; public function __construct(array $validators = []) { diff --git a/Constraints/AbstractComparison.php b/Constraints/AbstractComparison.php index d13031793..c7b87ba0e 100644 --- a/Constraints/AbstractComparison.php +++ b/Constraints/AbstractComparison.php @@ -24,9 +24,9 @@ */ abstract class AbstractComparison extends Constraint { - public $message; - public $value; - public $propertyPath; + public string $message; + public mixed $value = null; + public ?string $propertyPath = null; public function __construct(mixed $value = null, string $propertyPath = null, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { diff --git a/Constraints/All.php b/Constraints/All.php index 63d2d5b96..95f882601 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -11,13 +11,15 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Constraint; + /** * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class All extends Composite { - public $constraints = []; + public array|Constraint $constraints = []; public function __construct(mixed $constraints = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index bfa33533d..60f5e31a6 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Constraint; + /** * @author Przemysław Bogusz */ @@ -23,10 +25,10 @@ class AtLeastOneOf extends Composite self::AT_LEAST_ONE_OF_ERROR => 'AT_LEAST_ONE_OF_ERROR', ]; - public $constraints = []; - public $message = 'This value should satisfy at least one of the following constraints:'; - public $messageCollection = 'Each element of this collection should satisfy its own set of constraints.'; - public $includeInternalMessages = true; + public array|Constraint $constraints = []; + public string $message = 'This value should satisfy at least one of the following constraints:'; + public string $messageCollection = 'Each element of this collection should satisfy its own set of constraints.'; + public bool $includeInternalMessages = true; public function __construct(mixed $constraints = null, array $groups = null, mixed $payload = null, string $message = null, string $messageCollection = null, bool $includeInternalMessages = null) { diff --git a/Constraints/Bic.php b/Constraints/Bic.php index faeb63939..47f523f89 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -38,10 +38,10 @@ class Bic extends Constraint self::INVALID_CASE_ERROR => 'INVALID_CASE_ERROR', ]; - public $message = 'This is not a valid Business Identifier Code (BIC).'; - public $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.'; - public $iban; - public $ibanPropertyPath; + public string $message = 'This is not a valid Business Identifier Code (BIC).'; + public string $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.'; + public ?string $iban = null; + public ?string $ibanPropertyPath = null; public function __construct(array $options = null, string $message = null, string $iban = null, string $ibanPropertyPath = null, string $ibanMessage = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Blank.php b/Constraints/Blank.php index f620815ed..9f0b2be82 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -25,7 +25,7 @@ class Blank extends Constraint self::NOT_BLANK_ERROR => 'NOT_BLANK_ERROR', ]; - public $message = 'This value should be blank.'; + public string $message = 'This value should be blank.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 260bdd4ac..3097ef8a6 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -43,8 +43,8 @@ class CardScheme extends Constraint self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', ]; - public $message = 'Unsupported card type or invalid card number.'; - public $schemes; + public string $message = 'Unsupported card type or invalid card number.'; + public array|string|null $schemes = null; public function __construct(array|string|null $schemes, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { diff --git a/Constraints/CardSchemeValidator.php b/Constraints/CardSchemeValidator.php index c21a93a9b..ffc44a538 100644 --- a/Constraints/CardSchemeValidator.php +++ b/Constraints/CardSchemeValidator.php @@ -26,7 +26,7 @@ */ class CardSchemeValidator extends ConstraintValidator { - protected $schemes = [ + protected array $schemes = [ // American Express card numbers start with 34 or 37 and have 15 digits. CardScheme::AMEX => [ '/^3[47][0-9]{13}$/', diff --git a/Constraints/Choice.php b/Constraints/Choice.php index 66ac0df9c..de1d87f5f 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -29,17 +29,17 @@ class Choice extends Constraint self::TOO_MANY_ERROR => 'TOO_MANY_ERROR', ]; - public $choices; + public ?array $choices = null; /** @var callable|string|null */ public $callback; - public $multiple = false; - public $strict = true; - public $min; - public $max; - public $message = 'The value you selected is not a valid choice.'; - public $multipleMessage = 'One or more of the given values is invalid.'; - public $minMessage = 'You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.'; - public $maxMessage = 'You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.'; + public bool $multiple = false; + public bool $strict = true; + public ?int $min = null; + public ?int $max = null; + public string $message = 'The value you selected is not a valid choice.'; + public string $multipleMessage = 'One or more of the given values is invalid.'; + public string $minMessage = 'You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.'; + public string $maxMessage = 'You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.'; public bool $match = true; public function getDefaultOption(): ?string diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index 84d48af73..b62e6c122 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -37,15 +37,11 @@ class Cidr extends Constraint Ip::V6 => 128, ]; - public $version = Ip::ALL; - - public $message = 'This value is not a valid CIDR notation.'; - - public $netmaskRangeViolationMessage = 'The value of the netmask should be between {{ min }} and {{ max }}.'; - - public $netmaskMin = 0; - - public $netmaskMax; + public string $version = Ip::ALL; + public string $message = 'This value is not a valid CIDR notation.'; + public string $netmaskRangeViolationMessage = 'The value of the netmask should be between {{ min }} and {{ max }}.'; + public int $netmaskMin = 0; + public int $netmaskMax; public function __construct( array $options = null, diff --git a/Constraints/Collection.php b/Constraints/Collection.php index 635822ca4..57c277933 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Validator\Constraints; -use Symfony\Component\Validator\Exception\ConstraintDefinitionException; - /** * @author Bernhard Schussek */ @@ -27,13 +25,13 @@ class Collection extends Composite self::NO_SUCH_FIELD_ERROR => 'NO_SUCH_FIELD_ERROR', ]; - public $fields = []; - public $allowExtraFields = false; - public $allowMissingFields = false; - public $extraFieldsMessage = 'This field was not expected.'; - public $missingFieldsMessage = 'This field is missing.'; + public array $fields = []; + public bool $allowExtraFields = false; + public bool $allowMissingFields = false; + public string $extraFieldsMessage = 'This field was not expected.'; + public string $missingFieldsMessage = 'This field is missing.'; - public function __construct(mixed $fields = null, array $groups = null, mixed $payload = null, bool $allowExtraFields = null, bool $allowMissingFields = null, string $extraFieldsMessage = null, string $missingFieldsMessage = null) + public function __construct(array $fields = null, array $groups = null, mixed $payload = null, bool $allowExtraFields = null, bool $allowMissingFields = null, string $extraFieldsMessage = null, string $missingFieldsMessage = null) { // no known options set? $fields is the fields array if (\is_array($fields) @@ -53,10 +51,6 @@ protected function initializeNestedConstraints(): void { parent::initializeNestedConstraints(); - if (!\is_array($this->fields)) { - throw new ConstraintDefinitionException(sprintf('The option "fields" is expected to be an array in constraint "%s".', __CLASS__)); - } - foreach ($this->fields as $fieldName => $field) { // the XmlFileLoader and YamlFileLoader pass the field Optional // and Required constraint as an array with exactly one element diff --git a/Constraints/Compound.php b/Constraints/Compound.php index de0080fec..f6e875c01 100644 --- a/Constraints/Compound.php +++ b/Constraints/Compound.php @@ -22,7 +22,7 @@ abstract class Compound extends Composite { /** @var Constraint[] */ - public $constraints = []; + public array $constraints = []; public function __construct(mixed $options = null) { diff --git a/Constraints/Count.php b/Constraints/Count.php index 92c9b240f..f18c99c84 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -32,13 +32,13 @@ class Count extends Constraint self::NOT_DIVISIBLE_BY_ERROR => 'NOT_DIVISIBLE_BY_ERROR', ]; - public $minMessage = 'This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.'; - public $maxMessage = 'This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.'; - public $exactMessage = 'This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.'; - public $divisibleByMessage = 'The number of elements in this collection should be a multiple of {{ compared_value }}.'; - public $min; - public $max; - public $divisibleBy; + public string $minMessage = 'This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.'; + public string $maxMessage = 'This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.'; + public string $exactMessage = 'This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.'; + public string $divisibleByMessage = 'The number of elements in this collection should be a multiple of {{ compared_value }}.'; + public ?int $min = null; + public ?int $max = null; + public ?int $divisibleBy = null; public function __construct( int|array $exactly = null, diff --git a/Constraints/Country.php b/Constraints/Country.php index a4a63aa48..f3dfeaa9a 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -27,8 +27,8 @@ class Country extends Constraint self::NO_SUCH_COUNTRY_ERROR => 'NO_SUCH_COUNTRY_ERROR', ]; - public $message = 'This value is not a valid country.'; - public $alpha3 = false; + public string $message = 'This value is not a valid country.'; + public bool $alpha3 = false; public function __construct( array $options = null, diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 81934884a..73b7c0543 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -56,13 +56,13 @@ class CssColor extends Constraint self::HSLA, ]; - public $message = 'This value is not a valid CSS color.'; - public $formats; + public string $message = 'This value is not a valid CSS color.'; + public array|string $formats; /** * @param array|string $formats The types of CSS colors allowed (e.g. hexadecimal only, RGB and HSL only, etc.). */ - public function __construct($formats = [], string $message = null, array $groups = null, $payload = null, array $options = null) + public function __construct(array|string $formats = [], string $message = null, array $groups = null, $payload = null, array $options = null) { $validationModesAsString = implode(', ', self::$validationModes); diff --git a/Constraints/Currency.php b/Constraints/Currency.php index 5defeac1c..f75412c72 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -28,7 +28,7 @@ class Currency extends Constraint self::NO_SUCH_CURRENCY_ERROR => 'NO_SUCH_CURRENCY_ERROR', ]; - public $message = 'This value is not a valid currency.'; + public string $message = 'This value is not a valid currency.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Date.php b/Constraints/Date.php index de402ade4..b54f9b2ef 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -27,7 +27,7 @@ class Date extends Constraint self::INVALID_DATE_ERROR => 'INVALID_DATE_ERROR', ]; - public $message = 'This value is not a valid date.'; + public string $message = 'This value is not a valid date.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index 62f772c79..5f434346f 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -29,8 +29,8 @@ class DateTime extends Constraint self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR', ]; - public $format = 'Y-m-d H:i:s'; - public $message = 'This value is not a valid datetime.'; + public string $format = 'Y-m-d H:i:s'; + public string $message = 'This value is not a valid datetime.'; public function __construct(string|array $format = null, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { diff --git a/Constraints/DivisibleBy.php b/Constraints/DivisibleBy.php index 84355d891..f1490358d 100644 --- a/Constraints/DivisibleBy.php +++ b/Constraints/DivisibleBy.php @@ -23,5 +23,5 @@ class DivisibleBy extends AbstractComparison self::NOT_DIVISIBLE_BY => 'NOT_DIVISIBLE_BY', ]; - public $message = 'This value should be a multiple of {{ compared_value }}.'; + public string $message = 'This value should be a multiple of {{ compared_value }}.'; } diff --git a/Constraints/Email.php b/Constraints/Email.php index 337aece2f..6edcca3e2 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -38,8 +38,8 @@ class Email extends Constraint self::INVALID_FORMAT_ERROR => 'STRICT_CHECK_FAILED_ERROR', ]; - public $message = 'This value is not a valid email address.'; - public $mode; + public string $message = 'This value is not a valid email address.'; + public ?string $mode = null; /** @var callable|null */ public $normalizer; diff --git a/Constraints/EqualTo.php b/Constraints/EqualTo.php index fedece0e0..3a6f8f828 100644 --- a/Constraints/EqualTo.php +++ b/Constraints/EqualTo.php @@ -24,5 +24,5 @@ class EqualTo extends AbstractComparison self::NOT_EQUAL_ERROR => 'NOT_EQUAL_ERROR', ]; - public $message = 'This value should be equal to {{ compared_value }}.'; + public string $message = 'This value should be equal to {{ compared_value }}.'; } diff --git a/Constraints/Existence.php b/Constraints/Existence.php index a0d6ebd60..72bc1da61 100644 --- a/Constraints/Existence.php +++ b/Constraints/Existence.php @@ -11,12 +11,14 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Constraint; + /** * @author Bernhard Schussek */ abstract class Existence extends Composite { - public $constraints = []; + public array|Constraint $constraints = []; public function getDefaultOption(): ?string { diff --git a/Constraints/Expression.php b/Constraints/Expression.php index 1f0f35b86..a6bd83141 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -29,9 +29,9 @@ class Expression extends Constraint self::EXPRESSION_FAILED_ERROR => 'EXPRESSION_FAILED_ERROR', ]; - public $message = 'This value is not valid.'; - public $expression; - public $values = []; + public string $message = 'This value is not valid.'; + public string|ExpressionObject|null $expression = null; + public array $values = []; public bool $negate = true; public function __construct( diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index e3940cf38..b0255743e 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -25,9 +25,9 @@ class ExpressionSyntax extends Constraint self::EXPRESSION_SYNTAX_ERROR => 'EXPRESSION_SYNTAX_ERROR', ]; - public $message = 'This value should be a valid expression.'; - public $service; - public $allowedVariables; + public string $message = 'This value should be a valid expression.'; + public ?string $service = null; + public ?array $allowedVariables = null; public function __construct(array $options = null, string $message = null, string $service = null, array $allowedVariables = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/File.php b/Constraints/File.php index cb774269a..1f11d8290 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -41,28 +41,28 @@ class File extends Constraint self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG', ]; - public $binaryFormat; - public $mimeTypes = []; + public ?bool $binaryFormat = null; + public array|string $mimeTypes = []; public ?int $filenameMaxLength = null; - public array|string|null $extensions = []; - public $notFoundMessage = 'The file could not be found.'; - public $notReadableMessage = 'The file is not readable.'; - public $maxSizeMessage = 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.'; - public $mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.'; + public array|string $extensions = []; + public string $notFoundMessage = 'The file could not be found.'; + public string $notReadableMessage = 'The file is not readable.'; + public string $maxSizeMessage = 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.'; + public string $mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.'; public string $extensionsMessage = 'The extension of the file is invalid ({{ extension }}). Allowed extensions are {{ extensions }}.'; - public $disallowEmptyMessage = 'An empty file is not allowed.'; - public $filenameTooLongMessage = 'The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.'; - - public $uploadIniSizeErrorMessage = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'; - public $uploadFormSizeErrorMessage = 'The file is too large.'; - public $uploadPartialErrorMessage = 'The file was only partially uploaded.'; - public $uploadNoFileErrorMessage = 'No file was uploaded.'; - public $uploadNoTmpDirErrorMessage = 'No temporary folder was configured in php.ini.'; - public $uploadCantWriteErrorMessage = 'Cannot write temporary file to disk.'; - public $uploadExtensionErrorMessage = 'A PHP extension caused the upload to fail.'; - public $uploadErrorMessage = 'The file could not be uploaded.'; - - protected $maxSize; + public string $disallowEmptyMessage = 'An empty file is not allowed.'; + public string $filenameTooLongMessage = 'The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.'; + + public string $uploadIniSizeErrorMessage = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'; + public string $uploadFormSizeErrorMessage = 'The file is too large.'; + public string $uploadPartialErrorMessage = 'The file was only partially uploaded.'; + public string $uploadNoFileErrorMessage = 'No file was uploaded.'; + public string $uploadNoTmpDirErrorMessage = 'No temporary folder was configured in php.ini.'; + public string $uploadCantWriteErrorMessage = 'Cannot write temporary file to disk.'; + public string $uploadExtensionErrorMessage = 'A PHP extension caused the upload to fail.'; + public string $uploadErrorMessage = 'The file could not be uploaded.'; + + protected int|string|null $maxSize = null; /** * @param array|string[]|string $extensions diff --git a/Constraints/GreaterThan.php b/Constraints/GreaterThan.php index d010b0de7..99785aee0 100644 --- a/Constraints/GreaterThan.php +++ b/Constraints/GreaterThan.php @@ -24,5 +24,5 @@ class GreaterThan extends AbstractComparison self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ]; - public $message = 'This value should be greater than {{ compared_value }}.'; + public string $message = 'This value should be greater than {{ compared_value }}.'; } diff --git a/Constraints/GreaterThanOrEqual.php b/Constraints/GreaterThanOrEqual.php index faf1c48d0..43c36e80e 100644 --- a/Constraints/GreaterThanOrEqual.php +++ b/Constraints/GreaterThanOrEqual.php @@ -24,5 +24,5 @@ class GreaterThanOrEqual extends AbstractComparison self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ]; - public $message = 'This value should be greater than or equal to {{ compared_value }}.'; + public string $message = 'This value should be greater than or equal to {{ compared_value }}.'; } diff --git a/Constraints/GroupSequence.php b/Constraints/GroupSequence.php index 5c9a41b7b..b3a8a91bf 100644 --- a/Constraints/GroupSequence.php +++ b/Constraints/GroupSequence.php @@ -54,7 +54,7 @@ class GroupSequence * * @var array */ - public $groups; + public array $groups; /** * The group in which cascaded objects are validated when validating @@ -67,10 +67,8 @@ class GroupSequence * "Default" group. When validating that class in the "Default" group, the * group sequence is used instead, but still the "Default" group should be * cascaded to other objects. - * - * @var string|GroupSequence */ - public $cascadedGroup; + public string|GroupSequence $cascadedGroup; /** * Creates a new group sequence. diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index d24166de5..28f2a9b2d 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -25,8 +25,8 @@ class Hostname extends Constraint self::INVALID_HOSTNAME_ERROR => 'INVALID_HOSTNAME_ERROR', ]; - public $message = 'This value is not a valid hostname.'; - public $requireTld = true; + public string $message = 'This value is not a valid hostname.'; + public bool $requireTld = true; public function __construct( array $options = null, diff --git a/Constraints/Iban.php b/Constraints/Iban.php index 00287f831..601e2b01c 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -35,7 +35,7 @@ class Iban extends Constraint self::NOT_SUPPORTED_COUNTRY_CODE_ERROR => 'NOT_SUPPORTED_COUNTRY_CODE_ERROR', ]; - public $message = 'This is not a valid International Bank Account Number (IBAN).'; + public string $message = 'This is not a valid International Bank Account Number (IBAN).'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/IdenticalTo.php b/Constraints/IdenticalTo.php index 7ef73a047..6a8b7bdf2 100644 --- a/Constraints/IdenticalTo.php +++ b/Constraints/IdenticalTo.php @@ -24,5 +24,5 @@ class IdenticalTo extends AbstractComparison self::NOT_IDENTICAL_ERROR => 'NOT_IDENTICAL_ERROR', ]; - public $message = 'This value should be identical to {{ compared_value_type }} {{ compared_value }}.'; + public string $message = 'This value should be identical to {{ compared_value_type }} {{ compared_value }}.'; } diff --git a/Constraints/Image.php b/Constraints/Image.php index 7306dd7bd..1b37a54d5 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -55,35 +55,35 @@ class Image extends File self::CORRUPTED_IMAGE_ERROR => 'CORRUPTED_IMAGE_ERROR', ]; - public $mimeTypes = 'image/*'; - public $minWidth; - public $maxWidth; - public $maxHeight; - public $minHeight; - public $maxRatio; - public $minRatio; - public $minPixels; - public $maxPixels; - public $allowSquare = true; - public $allowLandscape = true; - public $allowPortrait = true; - public $detectCorrupted = false; + public array|string $mimeTypes = 'image/*'; + public ?int $minWidth = null; + public ?int $maxWidth = null; + public ?int $maxHeight = null; + public ?int $minHeight = null; + public int|float|null $maxRatio = null; + public int|float|null $minRatio = null; + public int|float|null $minPixels = null; + public int|float|null $maxPixels = null; + public bool $allowSquare = true; + public bool $allowLandscape = true; + public bool $allowPortrait = true; + public bool $detectCorrupted = false; // The constant for a wrong MIME type is taken from the parent class. - public $mimeTypesMessage = 'This file is not a valid image.'; - public $sizeNotDetectedMessage = 'The size of the image could not be detected.'; - public $maxWidthMessage = 'The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.'; - public $minWidthMessage = 'The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.'; - public $maxHeightMessage = 'The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.'; - public $minHeightMessage = 'The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.'; - public $minPixelsMessage = 'The image has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels.'; - public $maxPixelsMessage = 'The image has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels.'; - public $maxRatioMessage = 'The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.'; - public $minRatioMessage = 'The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.'; - public $allowSquareMessage = 'The image is square ({{ width }}x{{ height }}px). Square images are not allowed.'; - public $allowLandscapeMessage = 'The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.'; - public $allowPortraitMessage = 'The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.'; - public $corruptedMessage = 'The image file is corrupted.'; + public string $mimeTypesMessage = 'This file is not a valid image.'; + public string $sizeNotDetectedMessage = 'The size of the image could not be detected.'; + public string $maxWidthMessage = 'The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.'; + public string $minWidthMessage = 'The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.'; + public string $maxHeightMessage = 'The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.'; + public string $minHeightMessage = 'The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.'; + public string $minPixelsMessage = 'The image has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels.'; + public string $maxPixelsMessage = 'The image has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels.'; + public string $maxRatioMessage = 'The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.'; + public string $minRatioMessage = 'The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.'; + public string $allowSquareMessage = 'The image is square ({{ width }}x{{ height }}px). Square images are not allowed.'; + public string $allowLandscapeMessage = 'The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.'; + public string $allowPortraitMessage = 'The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.'; + public string $corruptedMessage = 'The image file is corrupted.'; public function __construct( array $options = null, diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 5d942f12c..35e47deea 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -67,10 +67,8 @@ class Ip extends Constraint self::INVALID_IP_ERROR => 'INVALID_IP_ERROR', ]; - public $version = self::V4; - - public $message = 'This is not a valid IP address.'; - + public string $version = self::V4; + public string $message = 'This is not a valid IP address.'; /** @var callable|null */ public $normalizer; diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index f87180fb3..62dfc42e1 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -25,7 +25,7 @@ class IsFalse extends Constraint self::NOT_FALSE_ERROR => 'NOT_FALSE_ERROR', ]; - public $message = 'This value should be false.'; + public string $message = 'This value should be false.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index 018ad1507..cbac402fa 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -25,7 +25,7 @@ class IsNull extends Constraint self::NOT_NULL_ERROR => 'NOT_NULL_ERROR', ]; - public $message = 'This value should be null.'; + public string $message = 'This value should be null.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index 045da66e6..c574ffb37 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -25,7 +25,7 @@ class IsTrue extends Constraint self::NOT_TRUE_ERROR => 'NOT_TRUE_ERROR', ]; - public $message = 'This value should be true.'; + public string $message = 'This value should be true.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index b8373f591..9b7c42ab8 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -38,11 +38,11 @@ class Isbn extends Constraint self::TYPE_NOT_RECOGNIZED_ERROR => 'TYPE_NOT_RECOGNIZED_ERROR', ]; - public $isbn10Message = 'This value is not a valid ISBN-10.'; - public $isbn13Message = 'This value is not a valid ISBN-13.'; - public $bothIsbnMessage = 'This value is neither a valid ISBN-10 nor a valid ISBN-13.'; - public $type; - public $message; + public string $isbn10Message = 'This value is not a valid ISBN-10.'; + public string $isbn13Message = 'This value is not a valid ISBN-13.'; + public string $bothIsbnMessage = 'This value is neither a valid ISBN-10 nor a valid ISBN-13.'; + public ?string $type = null; + public ?string $message = null; public function __construct( string|array $type = null, diff --git a/Constraints/Isin.php b/Constraints/Isin.php index b800dec86..496e3d903 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -32,7 +32,7 @@ class Isin extends Constraint self::INVALID_CHECKSUM_ERROR => 'INVALID_CHECKSUM_ERROR', ]; - public $message = 'This value is not a valid International Securities Identification Number (ISIN).'; + public string $message = 'This value is not a valid International Securities Identification Number (ISIN).'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Issn.php b/Constraints/Issn.php index cafb242ec..d03b835c6 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -36,9 +36,9 @@ class Issn extends Constraint self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', ]; - public $message = 'This value is not a valid ISSN.'; - public $caseSensitive = false; - public $requireHyphen = false; + public string $message = 'This value is not a valid ISSN.'; + public bool $caseSensitive = false; + public bool $requireHyphen = false; public function __construct( array $options = null, diff --git a/Constraints/Json.php b/Constraints/Json.php index 3ef40d94d..6656e4a5b 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -25,7 +25,7 @@ class Json extends Constraint self::INVALID_JSON_ERROR => 'INVALID_JSON_ERROR', ]; - public $message = 'This value should be valid JSON.'; + public string $message = 'This value should be valid JSON.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Language.php b/Constraints/Language.php index 0ff135b2b..fe38adcf8 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -27,8 +27,8 @@ class Language extends Constraint self::NO_SUCH_LANGUAGE_ERROR => 'NO_SUCH_LANGUAGE_ERROR', ]; - public $message = 'This value is not a valid language.'; - public $alpha3 = false; + public string $message = 'This value is not a valid language.'; + public bool $alpha3 = false; public function __construct( array $options = null, diff --git a/Constraints/Length.php b/Constraints/Length.php index 607f7462e..33d62fe94 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -43,13 +43,13 @@ class Length extends Constraint self::COUNT_GRAPHEMES, ]; - public $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.'; - public $minMessage = 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.'; - public $exactMessage = 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.'; - public $charsetMessage = 'This value does not match the expected {{ charset }} charset.'; - public $max; - public $min; - public $charset = 'UTF-8'; + public string $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.'; + public string $minMessage = 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.'; + public string $exactMessage = 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.'; + public string $charsetMessage = 'This value does not match the expected {{ charset }} charset.'; + public ?int $max = null; + public ?int $min = null; + public string $charset = 'UTF-8'; /** @var callable|null */ public $normalizer; /** @var self::COUNT_* */ diff --git a/Constraints/LessThan.php b/Constraints/LessThan.php index ef5dedc0e..da07673f6 100644 --- a/Constraints/LessThan.php +++ b/Constraints/LessThan.php @@ -24,5 +24,5 @@ class LessThan extends AbstractComparison self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', ]; - public $message = 'This value should be less than {{ compared_value }}.'; + public string $message = 'This value should be less than {{ compared_value }}.'; } diff --git a/Constraints/LessThanOrEqual.php b/Constraints/LessThanOrEqual.php index 0c2b5c851..4d838830f 100644 --- a/Constraints/LessThanOrEqual.php +++ b/Constraints/LessThanOrEqual.php @@ -24,5 +24,5 @@ class LessThanOrEqual extends AbstractComparison self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', ]; - public $message = 'This value should be less than or equal to {{ compared_value }}.'; + public string $message = 'This value should be less than or equal to {{ compared_value }}.'; } diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 43bd4127e..964c81da7 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -27,8 +27,8 @@ class Locale extends Constraint self::NO_SUCH_LOCALE_ERROR => 'NO_SUCH_LOCALE_ERROR', ]; - public $message = 'This value is not a valid locale.'; - public $canonicalize = true; + public string $message = 'This value is not a valid locale.'; + public bool $canonicalize = true; public function __construct( array $options = null, diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index 2fc5c8bc5..f370067bd 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -31,7 +31,7 @@ class Luhn extends Constraint self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', ]; - public $message = 'Invalid card number.'; + public string $message = 'Invalid card number.'; public function __construct( array $options = null, diff --git a/Constraints/Negative.php b/Constraints/Negative.php index 0a47120d3..eeb336ac1 100644 --- a/Constraints/Negative.php +++ b/Constraints/Negative.php @@ -19,5 +19,5 @@ class Negative extends LessThan { use ZeroComparisonConstraintTrait; - public $message = 'This value should be negative.'; + public string $message = 'This value should be negative.'; } diff --git a/Constraints/NegativeOrZero.php b/Constraints/NegativeOrZero.php index 8161f3b02..740e582ca 100644 --- a/Constraints/NegativeOrZero.php +++ b/Constraints/NegativeOrZero.php @@ -19,5 +19,5 @@ class NegativeOrZero extends LessThanOrEqual { use ZeroComparisonConstraintTrait; - public $message = 'This value should be either negative or zero.'; + public string $message = 'This value should be either negative or zero.'; } diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index 8fad02082..fca14cf78 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -27,8 +27,8 @@ class NotBlank extends Constraint self::IS_BLANK_ERROR => 'IS_BLANK_ERROR', ]; - public $message = 'This value should not be blank.'; - public $allowNull = false; + public string $message = 'This value should not be blank.'; + public bool $allowNull = false; /** @var callable|null */ public $normalizer; diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index afd957167..2c3924e87 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -27,9 +27,9 @@ class NotCompromisedPassword extends Constraint self::COMPROMISED_PASSWORD_ERROR => 'COMPROMISED_PASSWORD_ERROR', ]; - public $message = 'This password has been leaked in a data breach, it must not be used. Please use another password.'; - public $threshold = 1; - public $skipOnError = false; + public string $message = 'This password has been leaked in a data breach, it must not be used. Please use another password.'; + public int $threshold = 1; + public bool $skipOnError = false; public function __construct( array $options = null, diff --git a/Constraints/NotEqualTo.php b/Constraints/NotEqualTo.php index aad8beb1c..65967433f 100644 --- a/Constraints/NotEqualTo.php +++ b/Constraints/NotEqualTo.php @@ -24,5 +24,5 @@ class NotEqualTo extends AbstractComparison self::IS_EQUAL_ERROR => 'IS_EQUAL_ERROR', ]; - public $message = 'This value should not be equal to {{ compared_value }}.'; + public string $message = 'This value should not be equal to {{ compared_value }}.'; } diff --git a/Constraints/NotIdenticalTo.php b/Constraints/NotIdenticalTo.php index 7107c8b1e..c7500196e 100644 --- a/Constraints/NotIdenticalTo.php +++ b/Constraints/NotIdenticalTo.php @@ -24,5 +24,5 @@ class NotIdenticalTo extends AbstractComparison self::IS_IDENTICAL_ERROR => 'IS_IDENTICAL_ERROR', ]; - public $message = 'This value should not be identical to {{ compared_value_type }} {{ compared_value }}.'; + public string $message = 'This value should not be identical to {{ compared_value_type }} {{ compared_value }}.'; } diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 8b3ebc16c..14f82be19 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -25,7 +25,7 @@ class NotNull extends Constraint self::IS_NULL_ERROR => 'IS_NULL_ERROR', ]; - public $message = 'This value should not be null.'; + public string $message = 'This value should not be null.'; public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Positive.php b/Constraints/Positive.php index 5385dc503..4f5e2da82 100644 --- a/Constraints/Positive.php +++ b/Constraints/Positive.php @@ -19,5 +19,5 @@ class Positive extends GreaterThan { use ZeroComparisonConstraintTrait; - public $message = 'This value should be positive.'; + public string $message = 'This value should be positive.'; } diff --git a/Constraints/PositiveOrZero.php b/Constraints/PositiveOrZero.php index d73228193..2df48d329 100644 --- a/Constraints/PositiveOrZero.php +++ b/Constraints/PositiveOrZero.php @@ -19,5 +19,5 @@ class PositiveOrZero extends GreaterThanOrEqual { use ZeroComparisonConstraintTrait; - public $message = 'This value should be either positive or zero.'; + public string $message = 'This value should be either positive or zero.'; } diff --git a/Constraints/Range.php b/Constraints/Range.php index 97203957c..8d8d1dfa8 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -35,15 +35,15 @@ class Range extends Constraint self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ]; - public $notInRangeMessage = 'This value should be between {{ min }} and {{ max }}.'; - public $minMessage = 'This value should be {{ limit }} or more.'; - public $maxMessage = 'This value should be {{ limit }} or less.'; - public $invalidMessage = 'This value should be a valid number.'; - public $invalidDateTimeMessage = 'This value should be a valid datetime.'; - public $min; - public $minPropertyPath; - public $max; - public $maxPropertyPath; + public string $notInRangeMessage = 'This value should be between {{ min }} and {{ max }}.'; + public string $minMessage = 'This value should be {{ limit }} or more.'; + public string $maxMessage = 'This value should be {{ limit }} or less.'; + public string $invalidMessage = 'This value should be a valid number.'; + public string $invalidDateTimeMessage = 'This value should be a valid datetime.'; + public mixed $min = null; + public ?string $minPropertyPath = null; + public mixed $max = null; + public ?string $maxPropertyPath = null; public function __construct( array $options = null, diff --git a/Constraints/Regex.php b/Constraints/Regex.php index c98da744d..53a525fa3 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -26,10 +26,10 @@ class Regex extends Constraint self::REGEX_FAILED_ERROR => 'REGEX_FAILED_ERROR', ]; - public $message = 'This value is not valid.'; - public $pattern; - public $htmlPattern; - public $match = true; + public string $message = 'This value is not valid.'; + public ?string $pattern = null; + public ?string $htmlPattern = null; + public bool $match = true; /** @var callable|null */ public $normalizer; diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index 0ee6fb1f1..4a57c98ab 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Constraint; + /** * Use this constraint to sequentially validate nested constraints. * Validation for the nested constraints collection will stop at first violation. @@ -20,7 +22,7 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Sequentially extends Composite { - public $constraints = []; + public array|Constraint $constraints = []; public function __construct(mixed $constraints = null, array $groups = null, mixed $payload = null) { diff --git a/Constraints/Time.php b/Constraints/Time.php index 35e2dcd61..b78a7d3e9 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -27,7 +27,7 @@ class Time extends Constraint self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR', ]; - public $message = 'This value is not a valid time.'; + public string $message = 'This value is not a valid time.'; public function __construct( array $options = null, diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index 9c82c98b5..5fc5701cf 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -26,10 +26,10 @@ class Timezone extends Constraint public const TIMEZONE_IDENTIFIER_IN_COUNTRY_ERROR = 'c4a22222-dc92-4fc0-abb0-d95b268c7d0b'; public const TIMEZONE_IDENTIFIER_INTL_ERROR = '45863c26-88dc-41ba-bf53-c73bd1f7e90d'; - public $zone = \DateTimeZone::ALL; - public $countryCode; - public $intlCompatible = false; - public $message = 'This value is not a valid timezone.'; + public int $zone = \DateTimeZone::ALL; + public ?string $countryCode = null; + public bool $intlCompatible = false; + public string $message = 'This value is not a valid timezone.'; protected const ERROR_NAMES = [ self::TIMEZONE_IDENTIFIER_ERROR => 'TIMEZONE_IDENTIFIER_ERROR', diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index ab862cd79..cac8f4b54 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -20,7 +20,7 @@ #[\Attribute(\Attribute::TARGET_CLASS)] class Traverse extends Constraint { - public $traverse = true; + public bool $traverse = true; public function __construct(bool|array $traverse = null) { diff --git a/Constraints/Type.php b/Constraints/Type.php index eabb22d43..de94a458b 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -25,8 +25,8 @@ class Type extends Constraint self::INVALID_TYPE_ERROR => 'INVALID_TYPE_ERROR', ]; - public $message = 'This value should be of type {{ type }}.'; - public $type; + public string $message = 'This value should be of type {{ type }}.'; + public string|array|null $type = null; public function __construct(string|array|null $type, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index 19055ee0a..bbee1417b 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -31,7 +31,7 @@ class Ulid extends Constraint self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', ]; - public $message = 'This is not a valid ULID.'; + public string $message = 'This is not a valid ULID.'; public function __construct( array $options = null, diff --git a/Constraints/Unique.php b/Constraints/Unique.php index ae6fb286c..e80c2d3e8 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -28,7 +28,7 @@ class Unique extends Constraint self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE', ]; - public $message = 'This collection should contain only unique elements.'; + public string $message = 'This collection should contain only unique elements.'; /** @var callable|null */ public $normalizer; diff --git a/Constraints/Url.php b/Constraints/Url.php index fb75b234b..65cbffc0a 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -26,9 +26,9 @@ class Url extends Constraint self::INVALID_URL_ERROR => 'INVALID_URL_ERROR', ]; - public $message = 'This value is not a valid URL.'; - public $protocols = ['http', 'https']; - public $relativeProtocol = false; + public string $message = 'This value is not a valid URL.'; + public array $protocols = ['http', 'https']; + public bool $relativeProtocol = false; /** @var callable|null */ public $normalizer; diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index f31b472f1..f497b00dc 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -67,19 +67,15 @@ class Uuid extends Constraint /** * Message to display when validation fails. - * - * @var string */ - public $message = 'This is not a valid UUID.'; + public string $message = 'This is not a valid UUID.'; /** * Strict mode only allows UUIDs that meet the formal definition and formatting per RFC 4122. * * Set this to `false` to allow legacy formats with different dash positioning or wrapping characters - * - * @var bool */ - public $strict = true; + public bool $strict = true; /** * Array of allowed versions (see version constants above). @@ -88,7 +84,7 @@ class Uuid extends Constraint * * @var int[] */ - public $versions = self::ALL_VERSIONS; + public array $versions = self::ALL_VERSIONS; /** @var callable|null */ public $normalizer; diff --git a/Constraints/Valid.php b/Constraints/Valid.php index a924c304e..95422310a 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -19,7 +19,7 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Valid extends Constraint { - public $traverse = true; + public bool $traverse = true; public function __construct(array $options = null, array $groups = null, $payload = null, bool $traverse = null) { diff --git a/Constraints/When.php b/Constraints/When.php index a28ee8b4a..5dc9a8ed1 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -22,9 +22,9 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class When extends Composite { - public $expression; - public $constraints = []; - public $values = []; + public string|Expression $expression; + public array|Constraint $constraints = []; + public array $values = []; public function __construct(string|Expression|array $expression, array|Constraint $constraints = null, array $values = null, array $groups = null, $payload = null, array $options = []) { diff --git a/Mapping/Factory/LazyLoadingMetadataFactory.php b/Mapping/Factory/LazyLoadingMetadataFactory.php index 565c04bd6..4c2998116 100644 --- a/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -39,15 +39,15 @@ */ class LazyLoadingMetadataFactory implements MetadataFactoryInterface { - protected $loader; - protected $cache; + protected ?LoaderInterface $loader; + protected ?CacheItemPoolInterface $cache; /** * The loaded metadata, indexed by class name. * * @var ClassMetadata[] */ - protected $loadedClasses = []; + protected array $loadedClasses = []; public function __construct(LoaderInterface $loader = null, CacheItemPoolInterface $cache = null) { diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index 30dfff3a2..1d6ffb788 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -33,7 +33,7 @@ abstract class AbstractLoader implements LoaderInterface */ public const DEFAULT_NAMESPACE = '\\Symfony\\Component\\Validator\\Constraints\\'; - protected $namespaces = []; + protected array $namespaces = []; /** * @var array diff --git a/Mapping/Loader/FileLoader.php b/Mapping/Loader/FileLoader.php index 0fc017298..9932ab244 100644 --- a/Mapping/Loader/FileLoader.php +++ b/Mapping/Loader/FileLoader.php @@ -23,7 +23,7 @@ */ abstract class FileLoader extends AbstractLoader { - protected $file; + protected string $file; /** * Creates a new loader. diff --git a/Mapping/Loader/LoaderChain.php b/Mapping/Loader/LoaderChain.php index d9721483a..7a0cb8c47 100644 --- a/Mapping/Loader/LoaderChain.php +++ b/Mapping/Loader/LoaderChain.php @@ -25,7 +25,7 @@ */ class LoaderChain implements LoaderInterface { - protected $loaders; + protected array $loaders; /** * @param LoaderInterface[] $loaders The metadata loaders to use diff --git a/Mapping/Loader/StaticMethodLoader.php b/Mapping/Loader/StaticMethodLoader.php index 0d15e5f6e..d7548cd94 100644 --- a/Mapping/Loader/StaticMethodLoader.php +++ b/Mapping/Loader/StaticMethodLoader.php @@ -21,7 +21,7 @@ */ class StaticMethodLoader implements LoaderInterface { - protected $methodName; + protected string $methodName; /** * Creates a new loader. diff --git a/Mapping/Loader/XmlFileLoader.php b/Mapping/Loader/XmlFileLoader.php index bf36b15f3..e5196e48d 100644 --- a/Mapping/Loader/XmlFileLoader.php +++ b/Mapping/Loader/XmlFileLoader.php @@ -26,9 +26,9 @@ class XmlFileLoader extends FileLoader /** * The XML nodes of the mapping file. * - * @var \SimpleXMLElement[]|null + * @var \SimpleXMLElement[] */ - protected $classes; + protected array $classes; public function __construct(string $file) { diff --git a/Mapping/Loader/YamlFileLoader.php b/Mapping/Loader/YamlFileLoader.php index aa30b9542..8dfd6a84e 100644 --- a/Mapping/Loader/YamlFileLoader.php +++ b/Mapping/Loader/YamlFileLoader.php @@ -24,12 +24,7 @@ */ class YamlFileLoader extends FileLoader { - /** - * An array of YAML class descriptions. - * - * @var array - */ - protected $classes; + protected array $classes; public function __construct(string $file) { diff --git a/Test/ConstraintValidatorTestCase.php b/Test/ConstraintValidatorTestCase.php index 3a4437b1d..f808e49ba 100644 --- a/Test/ConstraintValidatorTestCase.php +++ b/Test/ConstraintValidatorTestCase.php @@ -30,6 +30,7 @@ use Symfony\Component\Validator\Context\ExecutionContext; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\MetadataInterface; use Symfony\Component\Validator\Mapping\PropertyMetadata; use Symfony\Component\Validator\Validator\ContextualValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -44,24 +45,22 @@ */ abstract class ConstraintValidatorTestCase extends TestCase { - /** - * @var ExecutionContextInterface - */ - protected $context; + protected ExecutionContextInterface $context; /** * @var T */ - protected $validator; - - protected $group; - protected $metadata; - protected $object; - protected $value; - protected $root; - protected $propertyPath; - protected $constraint; - protected $defaultTimezone; + protected ConstraintValidatorInterface $validator; + + protected string $group; + protected ?MetadataInterface $metadata; + protected mixed $object; + protected mixed $value; + protected mixed $root; + protected string $propertyPath; + protected Constraint $constraint; + protected ?string $defaultTimezone = null; + private string $defaultLocale; private array $expectedViolations; private int $call; diff --git a/Tests/Constraints/CollectionTest.php b/Tests/Constraints/CollectionTest.php index a362e96ce..b1ed59673 100644 --- a/Tests/Constraints/CollectionTest.php +++ b/Tests/Constraints/CollectionTest.php @@ -24,22 +24,6 @@ */ class CollectionTest extends TestCase { - public function testRejectInvalidFieldsOption() - { - $this->expectException(ConstraintDefinitionException::class); - new Collection([ - 'fields' => 'foo', - ]); - } - - public function testRejectNonConstraints() - { - $this->expectException(ConstraintDefinitionException::class); - new Collection([ - 'foo' => 'bar', - ]); - } - public function testRejectValidConstraint() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index 3e646cfa3..180b00cd3 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -264,66 +264,6 @@ public static function provideMaxPixelsConstraints(): iterable ]; } - public function testInvalidMinWidth() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'minWidth' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - - public function testInvalidMaxWidth() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'maxWidth' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - - public function testInvalidMinHeight() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'minHeight' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - - public function testInvalidMaxHeight() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'maxHeight' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - - public function testInvalidMinPixels() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'minPixels' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - - public function testInvalidMaxPixels() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'maxPixels' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - /** * @dataProvider provideMinRatioConstraints */ @@ -407,26 +347,6 @@ public function testMaxRatioUsesInputMoreDecimals() $this->assertNoViolation(); } - public function testInvalidMinRatio() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'minRatio' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - - public function testInvalidMaxRatio() - { - $this->expectException(ConstraintDefinitionException::class); - $constraint = new Image([ - 'maxRatio' => '1abc', - ]); - - $this->validator->validate($this->image, $constraint); - } - /** * @dataProvider provideAllowSquareConstraints */ diff --git a/Validator/RecursiveValidator.php b/Validator/RecursiveValidator.php index a6897c85a..bd303eba1 100644 --- a/Validator/RecursiveValidator.php +++ b/Validator/RecursiveValidator.php @@ -28,10 +28,10 @@ */ class RecursiveValidator implements ValidatorInterface { - protected $contextFactory; - protected $metadataFactory; - protected $validatorFactory; - protected $objectInitializers; + protected ExecutionContextFactoryInterface $contextFactory; + protected MetadataFactoryInterface $metadataFactory; + protected ConstraintValidatorFactoryInterface $validatorFactory; + protected array $objectInitializers; /** * Creates a new validator. From c7a457f0509f3cdd5dfcb36482632a99ff75a0f5 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 22 Aug 2023 15:45:13 +0200 Subject: [PATCH 010/149] [FrameworkBundle][Validator] Remove remaining deprecations --- CHANGELOG.md | 3 + Constraints/Callback.php | 2 +- Constraints/When.php | 3 - Mapping/Loader/AnnotationLoader.php | 96 -------------------- Mapping/Loader/AttributeLoader.php | 74 ++++++++++++++- Tests/ConstraintTest.php | 2 +- Tests/Constraints/CallbackValidatorTest.php | 6 +- Tests/Constraints/CountValidatorTestCase.php | 2 +- Tests/Constraints/LengthTest.php | 2 +- Tests/Constraints/WhenTest.php | 1 - Tests/Mapping/Loader/AttributeLoaderTest.php | 21 ++--- Tests/ValidatorBuilderTest.php | 21 ----- ValidatorBuilder.php | 25 ----- 13 files changed, 91 insertions(+), 167 deletions(-) delete mode 100644 Mapping/Loader/AnnotationLoader.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 17770d654..e2b0747a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ CHANGELOG * Remove the annotation reader parameter from the constructor signature of `AnnotationLoader` * Remove `ValidatorBuilder::setDoctrineAnnotationReader()` * Remove `ValidatorBuilder::addDefaultDoctrineAnnotationReader()` + * Remove `ValidatorBuilder::enableAnnotationMapping()`, use `ValidatorBuilder::enableAttributeMapping()` instead + * Remove `ValidatorBuilder::disableAnnotationMapping()`, use `ValidatorBuilder::disableAttributeMapping()` instead + * Remove `AnnotationLoader`, use `AttributeLoader` instead 6.4 --- diff --git a/Constraints/Callback.php b/Constraints/Callback.php index 2f0defe2c..c4bf70ea9 100644 --- a/Constraints/Callback.php +++ b/Constraints/Callback.php @@ -26,7 +26,7 @@ class Callback extends Constraint public function __construct(array|string|callable $callback = null, array $groups = null, mixed $payload = null, array $options = []) { - // Invocation through annotations with an array parameter only + // Invocation through attributes with an array parameter only if (\is_array($callback) && 1 === \count($callback) && isset($callback['value'])) { $callback = $callback['value']; } diff --git a/Constraints/When.php b/Constraints/When.php index 5dc9a8ed1..807410d81 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -16,9 +16,6 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; -/** - * @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"}) - */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class When extends Composite { diff --git a/Mapping/Loader/AnnotationLoader.php b/Mapping/Loader/AnnotationLoader.php deleted file mode 100644 index 1db0c5e6b..000000000 --- a/Mapping/Loader/AnnotationLoader.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Mapping\Loader; - -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\Callback; -use Symfony\Component\Validator\Constraints\GroupSequence; -use Symfony\Component\Validator\Constraints\GroupSequenceProvider; -use Symfony\Component\Validator\Exception\MappingException; -use Symfony\Component\Validator\Mapping\ClassMetadata; - -/** - * Loads validation metadata using PHP attributes. - * - * @deprecated since Symfony 6.4, use {@see AttributeLoader} instead - * - * @author Bernhard Schussek - * @author Alexander M. Turek - */ -class AnnotationLoader implements LoaderInterface -{ - public function loadClassMetadata(ClassMetadata $metadata): bool - { - $reflClass = $metadata->getReflectionClass(); - $className = $reflClass->name; - $success = false; - - foreach ($this->getAnnotations($reflClass) as $constraint) { - if ($constraint instanceof GroupSequence) { - $metadata->setGroupSequence($constraint->groups); - } elseif ($constraint instanceof GroupSequenceProvider) { - $metadata->setGroupSequenceProvider(true); - } elseif ($constraint instanceof Constraint) { - $metadata->addConstraint($constraint); - } - - $success = true; - } - - foreach ($reflClass->getProperties() as $property) { - if ($property->getDeclaringClass()->name === $className) { - foreach ($this->getAnnotations($property) as $constraint) { - if ($constraint instanceof Constraint) { - $metadata->addPropertyConstraint($property->name, $constraint); - } - - $success = true; - } - } - } - - foreach ($reflClass->getMethods() as $method) { - if ($method->getDeclaringClass()->name === $className) { - foreach ($this->getAnnotations($method) as $constraint) { - if ($constraint instanceof Callback) { - $constraint->callback = $method->getName(); - - $metadata->addConstraint($constraint); - } elseif ($constraint instanceof Constraint) { - if (preg_match('/^(get|is|has)(.+)$/i', $method->name, $matches)) { - $metadata->addGetterMethodConstraint(lcfirst($matches[2]), $matches[0], $constraint); - } else { - throw new MappingException(sprintf('The constraint on "%s::%s()" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name)); - } - } - - $success = true; - } - } - } - - return $success; - } - - private function getAnnotations(\ReflectionMethod|\ReflectionClass|\ReflectionProperty $reflection): iterable - { - foreach ($reflection->getAttributes(GroupSequence::class) as $attribute) { - yield $attribute->newInstance(); - } - foreach ($reflection->getAttributes(GroupSequenceProvider::class) as $attribute) { - yield $attribute->newInstance(); - } - foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { - yield $attribute->newInstance(); - } - } -} diff --git a/Mapping/Loader/AttributeLoader.php b/Mapping/Loader/AttributeLoader.php index 2a8a75e1e..9674122b6 100644 --- a/Mapping/Loader/AttributeLoader.php +++ b/Mapping/Loader/AttributeLoader.php @@ -11,6 +11,13 @@ namespace Symfony\Component\Validator\Mapping\Loader; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Constraints\GroupSequence; +use Symfony\Component\Validator\Constraints\GroupSequenceProvider; +use Symfony\Component\Validator\Exception\MappingException; +use Symfony\Component\Validator\Mapping\ClassMetadata; + /** * Loads validation metadata using PHP attributes. * @@ -18,10 +25,71 @@ * @author Alexander M. Turek * @author Alexandre Daubois */ -class AttributeLoader extends AnnotationLoader +class AttributeLoader implements LoaderInterface { - public function __construct() + public function loadClassMetadata(ClassMetadata $metadata): bool + { + $reflClass = $metadata->getReflectionClass(); + $className = $reflClass->name; + $success = false; + + foreach ($this->getAttributes($reflClass) as $constraint) { + if ($constraint instanceof GroupSequence) { + $metadata->setGroupSequence($constraint->groups); + } elseif ($constraint instanceof GroupSequenceProvider) { + $metadata->setGroupSequenceProvider(true); + } elseif ($constraint instanceof Constraint) { + $metadata->addConstraint($constraint); + } + + $success = true; + } + + foreach ($reflClass->getProperties() as $property) { + if ($property->getDeclaringClass()->name === $className) { + foreach ($this->getAttributes($property) as $constraint) { + if ($constraint instanceof Constraint) { + $metadata->addPropertyConstraint($property->name, $constraint); + } + + $success = true; + } + } + } + + foreach ($reflClass->getMethods() as $method) { + if ($method->getDeclaringClass()->name === $className) { + foreach ($this->getAttributes($method) as $constraint) { + if ($constraint instanceof Callback) { + $constraint->callback = $method->getName(); + + $metadata->addConstraint($constraint); + } elseif ($constraint instanceof Constraint) { + if (preg_match('/^(get|is|has)(.+)$/i', $method->name, $matches)) { + $metadata->addGetterMethodConstraint(lcfirst($matches[2]), $matches[0], $constraint); + } else { + throw new MappingException(sprintf('The constraint on "%s::%s()" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name)); + } + } + + $success = true; + } + } + } + + return $success; + } + + private function getAttributes(\ReflectionMethod|\ReflectionClass|\ReflectionProperty $reflection): iterable { - parent::__construct(null); + foreach ($reflection->getAttributes(GroupSequence::class) as $attribute) { + yield $attribute->newInstance(); + } + foreach ($reflection->getAttributes(GroupSequenceProvider::class) as $attribute) { + yield $attribute->newInstance(); + } + foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { + yield $attribute->newInstance(); + } } } diff --git a/Tests/ConstraintTest.php b/Tests/ConstraintTest.php index 3d233c178..80e33c7b7 100644 --- a/Tests/ConstraintTest.php +++ b/Tests/ConstraintTest.php @@ -245,7 +245,7 @@ public function testOptionsWithInvalidInternalPointer() $this->assertEquals('foo', $constraint->property1); } - public function testAnnotationSetUndefinedDefaultOption() + public function testAttributeSetUndefinedDefaultOption() { $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('No default option is configured for constraint "Symfony\Component\Validator\Tests\Fixtures\ConstraintB".'); diff --git a/Tests/Constraints/CallbackValidatorTest.php b/Tests/Constraints/CallbackValidatorTest.php index 084b192b6..e888baa7a 100644 --- a/Tests/Constraints/CallbackValidatorTest.php +++ b/Tests/Constraints/CallbackValidatorTest.php @@ -205,7 +205,7 @@ public function testConstraintGetTargets() $this->assertEquals($targets, $constraint->getTargets()); } - // Should succeed. Needed when defining constraints as annotations. + // Should succeed. Needed when defining constraints as attributes. public function testNoConstructorArguments() { $constraint = new Callback(); @@ -213,14 +213,14 @@ public function testNoConstructorArguments() $this->assertSame([Constraint::CLASS_CONSTRAINT, Constraint::PROPERTY_CONSTRAINT], $constraint->getTargets()); } - public function testAnnotationInvocationSingleValued() + public function testAttributeInvocationSingleValued() { $constraint = new Callback(['value' => 'validateStatic']); $this->assertEquals(new Callback('validateStatic'), $constraint); } - public function testAnnotationInvocationMultiValued() + public function testAttributeInvocationMultiValued() { $constraint = new Callback(['value' => [__CLASS__.'_Class', 'validateCallback']]); diff --git a/Tests/Constraints/CountValidatorTestCase.php b/Tests/Constraints/CountValidatorTestCase.php index 104c90773..c52cd4e69 100644 --- a/Tests/Constraints/CountValidatorTestCase.php +++ b/Tests/Constraints/CountValidatorTestCase.php @@ -283,7 +283,7 @@ public function testDefaultOption() $this->assertEquals(5, $constraint->max); } - public function testConstraintAnnotationDefaultOption() + public function testConstraintAttributeDefaultOption() { $constraint = new Count(['value' => 5, 'exactMessage' => 'message']); diff --git a/Tests/Constraints/LengthTest.php b/Tests/Constraints/LengthTest.php index 793491fc2..03bd37674 100644 --- a/Tests/Constraints/LengthTest.php +++ b/Tests/Constraints/LengthTest.php @@ -70,7 +70,7 @@ public function testConstraintDefaultOption() self::assertEquals(5, $constraint->max); } - public function testConstraintAnnotationDefaultOption() + public function testConstraintAttributeDefaultOption() { $constraint = new Length(['value' => 5, 'exactMessage' => 'message']); diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 03a856c6a..12d2bd146 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -19,7 +19,6 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\MissingOptionsException; use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; use Symfony\Component\Validator\Tests\Constraints\Fixtures\WhenTestWithAttributes; diff --git a/Tests/Mapping/Loader/AttributeLoaderTest.php b/Tests/Mapping/Loader/AttributeLoaderTest.php index c8975dbd2..f9cb0da9b 100644 --- a/Tests/Mapping/Loader/AttributeLoaderTest.php +++ b/Tests/Mapping/Loader/AttributeLoaderTest.php @@ -29,7 +29,6 @@ use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; @@ -37,7 +36,7 @@ class AttributeLoaderTest extends TestCase { public function testLoadClassMetadataReturnsTrueIfSuccessful() { - $loader = $this->createAnnotationLoader(); + $loader = $this->createAttributeLoader(); $metadata = new ClassMetadata($this->getFixtureNamespace().'\Entity'); $this->assertTrue($loader->loadClassMetadata($metadata)); @@ -45,7 +44,7 @@ public function testLoadClassMetadataReturnsTrueIfSuccessful() public function testLoadClassMetadataReturnsFalseIfNotSuccessful() { - $loader = $this->createAnnotationLoader(); + $loader = $this->createAttributeLoader(); $metadata = new ClassMetadata('\stdClass'); $this->assertFalse($loader->loadClassMetadata($metadata)); @@ -53,7 +52,7 @@ public function testLoadClassMetadataReturnsFalseIfNotSuccessful() public function testLoadClassMetadata() { - $loader = $this->createAnnotationLoader(); + $loader = $this->createAttributeLoader(); $namespace = $this->getFixtureNamespace(); $metadata = new ClassMetadata($namespace.'\Entity'); @@ -105,11 +104,11 @@ public function testLoadClassMetadata() } /** - * Test MetaData merge with parent annotation. + * Test MetaData merge with parent attribute. */ public function testLoadParentClassMetadata() { - $loader = $this->createAnnotationLoader(); + $loader = $this->createAttributeLoader(); $namespace = $this->getFixtureNamespace(); // Load Parent MetaData @@ -124,11 +123,11 @@ public function testLoadParentClassMetadata() } /** - * Test MetaData merge with parent annotation. + * Test MetaData merge with parent attribute. */ public function testLoadClassMetadataAndMerge() { - $loader = $this->createAnnotationLoader(); + $loader = $this->createAttributeLoader(); $namespace = $this->getFixtureNamespace(); // Load Parent MetaData @@ -196,9 +195,9 @@ public function testLoadClassMetadataAndMerge() $this->assertInstanceOf(NotNull::class, $otherMetadata[1]->getConstraints()[0]); } - public function testLoadGroupSequenceProviderAnnotation() + public function testLoadGroupSequenceProviderAttribute() { - $loader = $this->createAnnotationLoader(); + $loader = $this->createAttributeLoader(); $namespace = $this->getFixtureNamespace(); $metadata = new ClassMetadata($namespace.'\GroupSequenceProviderEntity'); @@ -211,7 +210,7 @@ public function testLoadGroupSequenceProviderAnnotation() $this->assertEquals($expected, $metadata); } - protected function createAnnotationLoader(): AnnotationLoader + protected function createAttributeLoader(): AttributeLoader { return new AttributeLoader(); } diff --git a/Tests/ValidatorBuilderTest.php b/Tests/ValidatorBuilderTest.php index f2253697c..c57a507e2 100644 --- a/Tests/ValidatorBuilderTest.php +++ b/Tests/ValidatorBuilderTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\Validator\Validator\RecursiveValidator; @@ -22,8 +21,6 @@ class ValidatorBuilderTest extends TestCase { - use ExpectDeprecationTrait; - private ValidatorBuilder $builder; protected function setUp(): void @@ -73,24 +70,6 @@ public function testAddMethodMappings() $this->assertSame($this->builder, $this->builder->addMethodMappings([])); } - /** - * @group legacy - */ - public function testExpectDeprecationWhenEnablingAnnotationMapping() - { - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\ValidatorBuilder::enableAnnotationMapping()" is deprecated, use "enableAttributeMapping()" instead.'); - $this->assertSame($this->builder, $this->builder->enableAnnotationMapping()); - } - - /** - * @group legacy - */ - public function testExpectDeprecationWhenDisablingAnnotationMapping() - { - $this->expectDeprecation('Since symfony/validator 6.4: Method "Symfony\Component\Validator\ValidatorBuilder::disableAnnotationMapping()" is deprecated, use "disableAttributeMapping()" instead.'); - $this->assertSame($this->builder, $this->builder->disableAnnotationMapping()); - } - public function testDisableAttributeMapping() { $this->assertSame($this->builder, $this->builder->disableAttributeMapping()); diff --git a/ValidatorBuilder.php b/ValidatorBuilder.php index ec7e6e77b..44f7161fa 100644 --- a/ValidatorBuilder.php +++ b/ValidatorBuilder.php @@ -16,7 +16,6 @@ use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; use Symfony\Component\Validator\Mapping\Loader\LoaderChain; use Symfony\Component\Validator\Mapping\Loader\LoaderInterface; @@ -181,18 +180,6 @@ public function addMethodMappings(array $methodNames): static return $this; } - /** - * @deprecated since Symfony 6.4, use "enableAttributeMapping()" instead. - * - * @return $this - */ - public function enableAnnotationMapping(): static - { - trigger_deprecation('symfony/validator', '6.4', 'Method "%s()" is deprecated, use "enableAttributeMapping()" instead.', __METHOD__); - - return $this->enableAttributeMapping(); - } - /** * Enables attribute-based constraint mapping. * @@ -209,18 +196,6 @@ public function enableAttributeMapping(): static return $this; } - /** - * @deprecated since Symfony 6.4, use "disableAttributeMapping()" instead - * - * @return $this - */ - public function disableAnnotationMapping(): static - { - trigger_deprecation('symfony/validator', '6.4', 'Method "%s()" is deprecated, use "disableAttributeMapping()" instead.', __METHOD__); - - return $this->disableAttributeMapping(); - } - /** * Disables attribute-based constraint mapping. * From cbc6d13c7bf767eaa70d079ad8acdf74c218d87f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 12 Sep 2023 14:19:20 +0200 Subject: [PATCH 011/149] [Validator] Make v7 conflict with doctrine-bridge < v7 --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index a9d5cced1..8e050441c 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "conflict": { "doctrine/lexer": "<1.1", "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<7.0", "symfony/expression-language": "<6.4", "symfony/http-kernel": "<6.4", "symfony/intl": "<6.4", From f15ea4292ecf753e9eb7eeb3b6bdb60e889a7d21 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 22 Oct 2023 12:36:09 +0200 Subject: [PATCH 012/149] fix tests on AppVeyor --- Tests/Constraints/FileTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Constraints/FileTest.php b/Tests/Constraints/FileTest.php index 5b70dc1d8..b05a3ec86 100644 --- a/Tests/Constraints/FileTest.php +++ b/Tests/Constraints/FileTest.php @@ -103,8 +103,8 @@ public static function provideValidSizes() ['1GI', 1073741824, true], ['2g', 2000000000, false], ['2G', 2000000000, false], - ['4g', 4 === \PHP_INT_SIZE ? 4000000000.0 : 4000000000, false], - ['4G', 4 === \PHP_INT_SIZE ? 4000000000.0 : 4000000000, false], + ['4g', 4 === \PHP_INT_SIZE ? '4000000000' : 4000000000, false], + ['4G', 4 === \PHP_INT_SIZE ? '4000000000' : 4000000000, false], ]; } From d5f70223274865c3c118243844d24a500bf75acd Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 14 Dec 2023 11:03:37 +0100 Subject: [PATCH 013/149] Set `strict` parameter of `in_array` to true where possible --- Constraint.php | 2 +- Constraints/CssColor.php | 2 +- Constraints/Length.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Constraint.php b/Constraint.php index b6a47643b..c8ec4041e 100644 --- a/Constraint.php +++ b/Constraint.php @@ -228,7 +228,7 @@ public function addImplicitGroupName(string $group): void throw new \LogicException(sprintf('"%s::$groups" is set to null. Did you forget to call "%s::__construct()"?', static::class, self::class)); } - if (\in_array(self::DEFAULT_GROUP, $this->groups) && !\in_array($group, $this->groups)) { + if (\in_array(self::DEFAULT_GROUP, $this->groups) && !\in_array($group, $this->groups, true)) { $this->groups[] = $group; } } diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 73b7c0543..641a2aa29 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -77,7 +77,7 @@ public function __construct(array|string $formats = [], string $message = null, $options['value'] = $formats; } elseif (\is_string($formats)) { - if (!\in_array($formats, self::$validationModes)) { + if (!\in_array($formats, self::$validationModes, true)) { throw new InvalidArgumentException(sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString)); } diff --git a/Constraints/Length.php b/Constraints/Length.php index 33d62fe94..740bfe27a 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -107,7 +107,7 @@ public function __construct( throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } - if (!\in_array($this->countUnit, self::VALID_COUNT_UNITS)) { + if (!\in_array($this->countUnit, self::VALID_COUNT_UNITS, true)) { throw new InvalidArgumentException(sprintf('The "countUnit" option must be one of the "%s"::COUNT_* constants ("%s" given).', __CLASS__, $this->countUnit)); } } From 448bcc8bf817fde62c408d195741622a6d626a74 Mon Sep 17 00:00:00 2001 From: Massimiliano Arione Date: Sun, 17 Dec 2023 21:09:20 +0100 Subject: [PATCH 014/149] [Validator] update Italian translation --- Resources/translations/validators.it.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/translations/validators.it.xlf b/Resources/translations/validators.it.xlf index d9d9d0661..4781b986d 100644 --- a/Resources/translations/validators.it.xlf +++ b/Resources/translations/validators.it.xlf @@ -426,6 +426,10 @@ Using hidden overlay characters is not allowed. Non è consentito utilizzare caratteri sovrapposti nascosti. + + The extension of the file is invalid ({{ extension }}). Allowed extensions are {{ extensions }}. + L'estensione del file non è valida ({{ extension }}). Le estensioni consentite sono {{ extensions }}. + From 898f88c644c1cc2a0a5d48d81c9a6dfe2a1dab3e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 18 Dec 2023 08:46:12 +0100 Subject: [PATCH 015/149] Code updates --- Constraints/UniqueValidator.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Constraints/UniqueValidator.php b/Constraints/UniqueValidator.php index f4e4012c6..b3782aac0 100644 --- a/Constraints/UniqueValidator.php +++ b/Constraints/UniqueValidator.php @@ -60,11 +60,7 @@ public function validate(mixed $value, Constraint $constraint): void private function getNormalizer(Unique $unique): callable { - if (null === $unique->normalizer) { - return static fn ($value) => $value; - } - - return $unique->normalizer; + return $unique->normalizer ?? static fn ($value) => $value; } private function reduceElementKeys(array $fields, array $element): array From e6ede6bb057507a80c06d7bd202cb10c64e9a1e1 Mon Sep 17 00:00:00 2001 From: Florian Hermann Date: Fri, 8 Dec 2023 21:21:17 +0100 Subject: [PATCH 016/149] [Validator] Add `list` and `associative_array` types to `Type` constraint --- CHANGELOG.md | 5 +++++ Constraints/TypeValidator.php | 4 ++++ Tests/Constraints/TypeValidatorTest.php | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73c4f2771..c2c41d6da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.1 +--- + + * Add `list` and `associative_array` types to `Type` constraint + 7.0 --- diff --git a/Constraints/TypeValidator.php b/Constraints/TypeValidator.php index 026db36eb..94c0c8639 100644 --- a/Constraints/TypeValidator.php +++ b/Constraints/TypeValidator.php @@ -36,6 +36,8 @@ class TypeValidator extends ConstraintValidator 'string' => 'is_string', 'scalar' => 'is_scalar', 'array' => 'is_array', + 'list' => 'is_array && array_is_list', + 'associative_array' => 'is_array && !array_is_list', 'iterable' => 'is_iterable', 'countable' => 'is_countable', 'callable' => 'is_callable', @@ -73,6 +75,8 @@ public function validate(mixed $value, Constraint $constraint): void 'finite-float' => \is_float($value) && is_finite($value), 'finite-number' => \is_int($value) || \is_float($value) && is_finite($value), 'number' => \is_int($value) || \is_float($value) && !is_nan($value), + 'list' => \is_array($value) && array_is_list($value), + 'associative_array' => \is_array($value) && !array_is_list($value), default => self::VALIDATION_FUNCTIONS[$type]($value), }) { return; diff --git a/Tests/Constraints/TypeValidatorTest.php b/Tests/Constraints/TypeValidatorTest.php index 8b4fe2531..99714407f 100644 --- a/Tests/Constraints/TypeValidatorTest.php +++ b/Tests/Constraints/TypeValidatorTest.php @@ -97,6 +97,10 @@ public static function getValidValues() [1.5, 'finite-number'], ['12345', 'string'], [[], 'array'], + [[], 'list'], + [[1, 2, 3], 'list'], + [['abc' => 1], 'associative_array'], + [[1 => 1], 'associative_array'], [$object, 'object'], [$object, 'stdClass'], [$file, 'resource'], @@ -166,6 +170,12 @@ public static function getInvalidValues() [$file, 'float', 'resource'], [$file, 'string', 'resource'], [$file, 'object', 'resource'], + [[1 => 1], 'list', 'array'], + [['abc' => 1], 'list', 'array'], + ['abcd1', 'list', '"abcd1"'], + [[], 'associative_array', 'array'], + [[1, 2, 3], 'associative_array', 'array'], + ['abcd1', 'associative_array', '"abcd1"'], ['12a34', 'digit', '"12a34"'], ['1a#23', 'alnum', '"1a#23"'], ['abcd1', 'alpha', '"abcd1"'], From 1c54a8685da732379d7af05f489210889db97145 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 1 Nov 2023 09:14:07 +0100 Subject: [PATCH 017/149] [Tests] Streamline --- .../AbstractComparisonValidatorTestCase.php | 6 ++---- Tests/Constraints/CssColorValidatorTest.php | 15 ++++++++++++-- Tests/Constraints/EmailValidatorTest.php | 5 +++-- Tests/Constraints/FileTest.php | 4 +++- Tests/Constraints/FileValidatorTestCase.php | 3 ++- ...idatorWithPositiveOrZeroConstraintTest.php | 6 +----- ...hanValidatorWithPositiveConstraintTest.php | 4 ---- Tests/Constraints/LengthValidatorTest.php | 20 +++++++++---------- ...idatorWithNegativeOrZeroConstraintTest.php | 4 ---- ...hanValidatorWithNegativeConstraintTest.php | 4 ---- Tests/Constraints/LocaleValidatorTest.php | 2 +- Tests/Constraints/UuidValidatorTest.php | 3 ++- Tests/Constraints/WhenTest.php | 4 +--- ...ontainerConstraintValidatorFactoryTest.php | 4 +++- .../AddConstraintValidatorsPassTest.php | 6 ++++-- Tests/Mapping/ClassMetadataTest.php | 8 ++++++-- .../Factory/BlackHoleMetadataFactoryTest.php | 7 ++----- .../LazyLoadingMetadataFactoryTest.php | 7 +++++-- Tests/Mapping/Loader/YamlFileLoaderTest.php | 3 ++- Tests/Validator/RecursiveValidatorTest.php | 13 ++++++------ 20 files changed, 67 insertions(+), 61 deletions(-) diff --git a/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/Tests/Constraints/AbstractComparisonValidatorTestCase.php index 521d5abd8..0b3a3897d 100644 --- a/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -231,11 +231,9 @@ public static function throwsOnInvalidStringDatesProvider(): array 'value' => 'foo', ]); - $constraintClass = $constraint::class; - return [ - [$constraint, sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraintClass), new \DateTimeImmutable()], - [$constraint, sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraintClass), new \DateTime()], + [$constraint, sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraint::class), new \DateTimeImmutable()], + [$constraint, sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraint::class), new \DateTime()], ]; } diff --git a/Tests/Constraints/CssColorValidatorTest.php b/Tests/Constraints/CssColorValidatorTest.php index 5c7904a80..081d41a57 100644 --- a/Tests/Constraints/CssColorValidatorTest.php +++ b/Tests/Constraints/CssColorValidatorTest.php @@ -369,7 +369,12 @@ public function testInvalidRGBA($cssColor) public static function getInvalidRGBA(): array { - return [['rgba(999,999,999,999)'], ['rgba(-99,-99,-99,-99)'], ['rgba(a,b,c,d)'], ['rgba(99 99, 9 99, 99 9, . 9)']]; + return [ + ['rgba(999,999,999,999)'], + ['rgba(-99,-99,-99,-99)'], + ['rgba(a,b,c,d)'], + ['rgba(99 99, 9 99, 99 9, . 9)'], + ]; } /** @@ -415,7 +420,13 @@ public function testInvalidHSLA($cssColor) public function getInvalidHSLA(): array { - return [['hsla(1000, 1000%, 20000%, 999)'], ['hsla(-100, -10%, -2%, 999)'], ['hsla(a, b, c, d)'], ['hsla(a, b%, c%, d)'], ['hsla( 9 99% , 99 9% , 9 %']]; + return [ + ['hsla(1000, 1000%, 20000%, 999)'], + ['hsla(-100, -10%, -2%, 999)'], + ['hsla(a, b, c, d)'], + ['hsla(a, b%, c%, d)'], + ['hsla( 9 99% , 99 9% , 9 %'], + ]; } /** diff --git a/Tests/Constraints/EmailValidatorTest.php b/Tests/Constraints/EmailValidatorTest.php index d894236e1..ab424a82d 100644 --- a/Tests/Constraints/EmailValidatorTest.php +++ b/Tests/Constraints/EmailValidatorTest.php @@ -245,11 +245,12 @@ public function testModeHtml5AllowNoTld() public function testUnknownModesOnValidateTriggerException() { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Email::$mode" parameter value is not valid.'); $constraint = new Email(); $constraint->mode = 'Unknown Mode'; + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Email::$mode" parameter value is not valid.'); + $this->validator->validate('example@example..com', $constraint); } diff --git a/Tests/Constraints/FileTest.php b/Tests/Constraints/FileTest.php index b05a3ec86..e8c27b4b1 100644 --- a/Tests/Constraints/FileTest.php +++ b/Tests/Constraints/FileTest.php @@ -57,8 +57,10 @@ public function testMaxSizeCanBeSetAfterInitialization($maxSize, $bytes, $binary */ public function testInvalidValueForMaxSizeThrowsExceptionAfterInitialization($maxSize) { - $this->expectException(ConstraintDefinitionException::class); $file = new File(['maxSize' => 1000]); + + $this->expectException(ConstraintDefinitionException::class); + $file->maxSize = $maxSize; } diff --git a/Tests/Constraints/FileValidatorTestCase.php b/Tests/Constraints/FileValidatorTestCase.php index 960a8f3b6..b5d05801e 100644 --- a/Tests/Constraints/FileValidatorTestCase.php +++ b/Tests/Constraints/FileValidatorTestCase.php @@ -519,10 +519,11 @@ public static function uploadedFileErrorProvider() public function testNegativeMaxSize() { + $file = new File(); + $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('"-1" is not a valid maximum size.'); - $file = new File(); $file->maxSize = -1; } diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php index 34e546029..11b092c6d 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php @@ -69,15 +69,11 @@ public function testThrowsConstraintExceptionIfValue() */ public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires either the "value" or "propertyPath" option to be set.'); - $this->markTestSkipped('Value option always set for PositiveOrZero constraint'); + $this->markTestSkipped('Value option always set for PositiveOrZero constraint'); } public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires only one of the "value" or "propertyPath" options to be set, not both.'); $this->markTestSkipped('Value option is set for PositiveOrZero constraint automatically'); } diff --git a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php index 5ce59d129..18a503bf2 100644 --- a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php +++ b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php @@ -67,15 +67,11 @@ public function testThrowsConstraintExceptionIfValue() */ public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires either the "value" or "propertyPath" option to be set.'); $this->markTestSkipped('Value option always set for Positive constraint.'); } public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires only one of the "value" or "propertyPath" options to be set, not both.'); $this->markTestSkipped('Value option is set for Positive constraint automatically'); } diff --git a/Tests/Constraints/LengthValidatorTest.php b/Tests/Constraints/LengthValidatorTest.php index 000b9d900..0afc9e6e1 100644 --- a/Tests/Constraints/LengthValidatorTest.php +++ b/Tests/Constraints/LengthValidatorTest.php @@ -116,7 +116,7 @@ public static function getThreeCharactersWithWhitespaces() /** * @dataProvider getFiveOrMoreCharacters */ - public function testValidValuesMin($value) + public function testValidValuesMin(int|string $value) { $constraint = new Length(['min' => 5]); $this->validator->validate($value, $constraint); @@ -127,7 +127,7 @@ public function testValidValuesMin($value) /** * @dataProvider getThreeOrLessCharacters */ - public function testValidValuesMax($value) + public function testValidValuesMax(int|string $value) { $constraint = new Length(['max' => 3]); $this->validator->validate($value, $constraint); @@ -138,7 +138,7 @@ public function testValidValuesMax($value) /** * @dataProvider getFourCharacters */ - public function testValidValuesExact($value) + public function testValidValuesExact(int|string $value) { $constraint = new Length(4); $this->validator->validate($value, $constraint); @@ -184,7 +184,7 @@ public function testValidBytesValues() /** * @dataProvider getThreeOrLessCharacters */ - public function testInvalidValuesMin($value, $valueLength) + public function testInvalidValuesMin(int|string $value, int $valueLength) { $constraint = new Length([ 'min' => 4, @@ -206,7 +206,7 @@ public function testInvalidValuesMin($value, $valueLength) /** * @dataProvider getThreeOrLessCharacters */ - public function testInvalidValuesMinNamed($value, $valueLength) + public function testInvalidValuesMinNamed(int|string $value, int $valueLength) { $constraint = new Length(min: 4, minMessage: 'myMessage'); @@ -225,7 +225,7 @@ public function testInvalidValuesMinNamed($value, $valueLength) /** * @dataProvider getFiveOrMoreCharacters */ - public function testInvalidValuesMax($value, $valueLength) + public function testInvalidValuesMax(int|string $value, int $valueLength) { $constraint = new Length([ 'max' => 4, @@ -247,7 +247,7 @@ public function testInvalidValuesMax($value, $valueLength) /** * @dataProvider getFiveOrMoreCharacters */ - public function testInvalidValuesMaxNamed($value, $valueLength) + public function testInvalidValuesMaxNamed(int|string $value, int $valueLength) { $constraint = new Length(max: 4, maxMessage: 'myMessage'); @@ -266,7 +266,7 @@ public function testInvalidValuesMaxNamed($value, $valueLength) /** * @dataProvider getThreeOrLessCharacters */ - public function testInvalidValuesExactLessThanFour($value, $valueLength) + public function testInvalidValuesExactLessThanFour(int|string $value, int $valueLength) { $constraint = new Length([ 'min' => 4, @@ -289,7 +289,7 @@ public function testInvalidValuesExactLessThanFour($value, $valueLength) /** * @dataProvider getThreeOrLessCharacters */ - public function testInvalidValuesExactLessThanFourNamed($value, $valueLength) + public function testInvalidValuesExactLessThanFourNamed(int|string $value, int $valueLength) { $constraint = new Length(exactly: 4, exactMessage: 'myMessage'); @@ -308,7 +308,7 @@ public function testInvalidValuesExactLessThanFourNamed($value, $valueLength) /** * @dataProvider getFiveOrMoreCharacters */ - public function testInvalidValuesExactMoreThanFour($value, $valueLength) + public function testInvalidValuesExactMoreThanFour(int|string $value, int $valueLength) { $constraint = new Length([ 'min' => 4, diff --git a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php index f75364e1d..946bf93c7 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php @@ -67,15 +67,11 @@ public function testThrowsConstraintExceptionIfValue() */ public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires either the "value" or "propertyPath" option to be set.'); $this->markTestSkipped('Value option always set for NegativeOrZero constraint'); } public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires only one of the "value" or "propertyPath" options to be set, not both.'); $this->markTestSkipped('Value option is set for NegativeOrZero constraint automatically'); } diff --git a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php index 569e662b2..35fac73ec 100644 --- a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php +++ b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php @@ -67,15 +67,11 @@ public function testThrowsConstraintExceptionIfValue() */ public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires either the "value" or "propertyPath" option to be set.'); $this->markTestSkipped('Value option always set for Negative constraint'); } public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('requires only one of the "value" or "propertyPath" options to be set, not both.'); $this->markTestSkipped('Value option is set for Negative constraint automatically'); } diff --git a/Tests/Constraints/LocaleValidatorTest.php b/Tests/Constraints/LocaleValidatorTest.php index 4eec91c63..1626eecef 100644 --- a/Tests/Constraints/LocaleValidatorTest.php +++ b/Tests/Constraints/LocaleValidatorTest.php @@ -151,7 +151,7 @@ public function testInvalidLocaleWithoutCanonicalizationNamed() ->assertRaised(); } - public static function getUncanonicalizedLocales(): iterable + public static function getUncanonicalizedLocales(): array { return [ ['en-US'], diff --git a/Tests/Constraints/UuidValidatorTest.php b/Tests/Constraints/UuidValidatorTest.php index 7c2e587cf..b5084b6d7 100644 --- a/Tests/Constraints/UuidValidatorTest.php +++ b/Tests/Constraints/UuidValidatorTest.php @@ -44,9 +44,10 @@ public function testEmptyStringIsValid() public function testExpectsUuidConstraintCompatibleType() { - $this->expectException(UnexpectedTypeException::class); $constraint = $this->getMockForAbstractClass(Constraint::class); + $this->expectException(UnexpectedTypeException::class); + $this->validator->validate('216fff40-98d9-11e3-a5e2-0800200c9a66', $constraint); } diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 12d2bd146..7cfe13f2f 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -36,9 +36,7 @@ public function testNonConstraintsAreRejected() { $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('The value "foo" is not an instance of Constraint in constraint "Symfony\Component\Validator\Constraints\When"'); - new When('true', [ - 'foo', - ]); + new When('true', ['foo']); } public function testAttributes() diff --git a/Tests/ContainerConstraintValidatorFactoryTest.php b/Tests/ContainerConstraintValidatorFactoryTest.php index 63b7f6f96..980a70e40 100644 --- a/Tests/ContainerConstraintValidatorFactoryTest.php +++ b/Tests/ContainerConstraintValidatorFactoryTest.php @@ -49,7 +49,6 @@ public function testGetInstanceReturnsService() public function testGetInstanceInvalidValidatorClass() { - $this->expectException(ValidatorException::class); $constraint = $this->createMock(Constraint::class); $constraint ->expects($this->once()) @@ -57,6 +56,9 @@ public function testGetInstanceInvalidValidatorClass() ->willReturn('Fully\\Qualified\\ConstraintValidator\\Class\\Name'); $factory = new ContainerConstraintValidatorFactory(new Container()); + + $this->expectException(ValidatorException::class); + $factory->getInstance($constraint); } } diff --git a/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php b/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php index 052c88f85..1b85c7e2a 100644 --- a/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php +++ b/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php @@ -47,8 +47,6 @@ public function testThatConstraintValidatorServicesAreProcessed() public function testAbstractConstraintValidator() { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The service "my_abstract_constraint_validator" tagged "validator.constraint_validator" must not be abstract.'); $container = new ContainerBuilder(); $container->register('validator.validator_factory') ->addArgument([]); @@ -58,6 +56,10 @@ public function testAbstractConstraintValidator() ->addTag('validator.constraint_validator'); $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The service "my_abstract_constraint_validator" tagged "validator.constraint_validator" must not be abstract.'); + $addConstraintValidatorsPass->process($container); } diff --git a/Tests/Mapping/ClassMetadataTest.php b/Tests/Mapping/ClassMetadataTest.php index 815f4a1f5..d38ecf860 100644 --- a/Tests/Mapping/ClassMetadataTest.php +++ b/Tests/Mapping/ClassMetadataTest.php @@ -279,17 +279,21 @@ public function testGroupSequencesFailIfContainingDefault() public function testGroupSequenceFailsIfGroupSequenceProviderIsSet() { - $this->expectException(GroupDefinitionException::class); $metadata = new ClassMetadata(self::PROVIDERCLASS); $metadata->setGroupSequenceProvider(true); + + $this->expectException(GroupDefinitionException::class); + $metadata->setGroupSequence(['GroupSequenceProviderEntity', 'Foo']); } public function testGroupSequenceProviderFailsIfGroupSequenceIsSet() { - $this->expectException(GroupDefinitionException::class); $metadata = new ClassMetadata(self::PROVIDERCLASS); $metadata->setGroupSequence(['GroupSequenceProviderEntity', 'Foo']); + + $this->expectException(GroupDefinitionException::class); + $metadata->setGroupSequenceProvider(true); } diff --git a/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php b/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php index 549bc518b..1fff11301 100644 --- a/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php +++ b/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php @@ -20,14 +20,11 @@ class BlackHoleMetadataFactoryTest extends TestCase public function testGetMetadataForThrowsALogicException() { $this->expectException(LogicException::class); - $metadataFactory = new BlackHoleMetadataFactory(); - $metadataFactory->getMetadataFor('foo'); + (new BlackHoleMetadataFactory())->getMetadataFor('foo'); } public function testHasMetadataForReturnsFalse() { - $metadataFactory = new BlackHoleMetadataFactory(); - - $this->assertFalse($metadataFactory->hasMetadataFor('foo')); + $this->assertFalse((new BlackHoleMetadataFactory())->hasMetadataFor('foo')); } } diff --git a/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index 3d10506ae..d2250114f 100644 --- a/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -109,14 +109,17 @@ public function testCachedMetadata() public function testNonClassNameStringValues() { - $this->expectException(NoSuchMetadataException::class); $testedValue = 'error@example.com'; $loader = $this->createMock(LoaderInterface::class); $cache = $this->createMock(CacheItemPoolInterface::class); - $factory = new LazyLoadingMetadataFactory($loader, $cache); $cache ->expects($this->never()) ->method('getItem'); + + $factory = new LazyLoadingMetadataFactory($loader, $cache); + + $this->expectException(NoSuchMetadataException::class); + $factory->getMetadataFor($testedValue); } diff --git a/Tests/Mapping/Loader/YamlFileLoaderTest.php b/Tests/Mapping/Loader/YamlFileLoaderTest.php index a5c983939..60493787e 100644 --- a/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -48,10 +48,11 @@ public function testLoadClassMetadataReturnsFalseIfEmpty() */ public function testInvalidYamlFiles($path) { - $this->expectException(\InvalidArgumentException::class); $loader = new YamlFileLoader(__DIR__.'/'.$path); $metadata = new ClassMetadata(Entity::class); + $this->expectException(\InvalidArgumentException::class); + $loader->loadClassMetadata($metadata); } diff --git a/Tests/Validator/RecursiveValidatorTest.php b/Tests/Validator/RecursiveValidatorTest.php index cffbaa5fb..ee183a1bf 100644 --- a/Tests/Validator/RecursiveValidatorTest.php +++ b/Tests/Validator/RecursiveValidatorTest.php @@ -531,12 +531,13 @@ public function testsIgnoreNullReference() public function testFailOnScalarReferences() { - $this->expectException(NoSuchMetadataException::class); $entity = new Entity(); $entity->reference = 'string'; $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->expectException(NoSuchMetadataException::class); + $this->validate($entity); } @@ -786,7 +787,6 @@ public function testDisableTraversableTraversal() public function testMetadataMustExistIfTraversalIsDisabled() { - $this->expectException(NoSuchMetadataException::class); $entity = new Entity(); $entity->reference = new \ArrayIterator(); @@ -794,6 +794,8 @@ public function testMetadataMustExistIfTraversalIsDisabled() 'traverse' => false, ])); + $this->expectException(NoSuchMetadataException::class); + $this->validate($entity); } @@ -1670,12 +1672,11 @@ public function testTraversalDisabledOnClass() public function testExpectTraversableIfTraversalEnabledOnClass() { - $this->expectException(ConstraintDefinitionException::class); - $entity = new Entity(); - $this->metadata->addConstraint(new Traverse(true)); - $this->validator->validate($entity); + $this->expectException(ConstraintDefinitionException::class); + + $this->validator->validate(new Entity()); } public function testReferenceTraversalDisabledOnClass() From 75783c5626d6a454353c43324acfed3e63c82dfd Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 20 Dec 2023 10:38:42 +0100 Subject: [PATCH 018/149] [Validator] Add the `Charset` constraint --- CHANGELOG.md | 1 + Constraints/Charset.php | 43 +++++++++++ Constraints/CharsetValidator.php | 46 ++++++++++++ Tests/Constraints/CharsetTest.php | 65 ++++++++++++++++ Tests/Constraints/CharsetValidatorTest.php | 86 ++++++++++++++++++++++ 5 files changed, 241 insertions(+) create mode 100644 Constraints/Charset.php create mode 100644 Constraints/CharsetValidator.php create mode 100644 Tests/Constraints/CharsetTest.php create mode 100644 Tests/Constraints/CharsetValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c41d6da..6e65a1355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add `list` and `associative_array` types to `Type` constraint + * Add the `Charset` constraint 7.0 --- diff --git a/Constraints/Charset.php b/Constraints/Charset.php new file mode 100644 index 000000000..a864a440f --- /dev/null +++ b/Constraints/Charset.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; + +/** + * @author Alexandre Daubois + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Charset extends Constraint +{ + public const BAD_ENCODING_ERROR = '94c5e58b-f892-4e25-8fd6-9d89c80bfe81'; + + protected const ERROR_NAMES = [ + self::BAD_ENCODING_ERROR => 'BAD_ENCODING_ERROR', + ]; + + public array|string $encodings = []; + public string $message = 'The detected encoding "{{ detected }}" does not match one of the accepted encoding: "{{ encodings }}".'; + + public function __construct(array|string $encodings = null, string $message = null, array $groups = null, mixed $payload = null, array $options = null) + { + parent::__construct($options, $groups, $payload); + + $this->message = $message ?? $this->message; + $this->encodings = (array) ($encodings ?? $this->encodings); + + if ([] === $this->encodings) { + throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires at least one encoding.', static::class)); + } + } +} diff --git a/Constraints/CharsetValidator.php b/Constraints/CharsetValidator.php new file mode 100644 index 000000000..2a4ca66a4 --- /dev/null +++ b/Constraints/CharsetValidator.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; + +/** + * @author Alexandre Daubois + */ +final class CharsetValidator extends ConstraintValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof Charset) { + throw new UnexpectedTypeException($constraint, Charset::class); + } + + if (null === $value) { + return; + } + + if (!\is_string($value)) { + throw new UnexpectedValueException($value, 'string'); + } + + if (!\in_array($detected = mb_detect_encoding($value, $constraint->encodings, true), $constraint->encodings, true)) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ detected }}', $detected) + ->setParameter('{{ encodings }}', implode('", "', $constraint->encodings)) + ->setCode(Charset::BAD_ENCODING_ERROR) + ->addViolation(); + } + } +} diff --git a/Tests/Constraints/CharsetTest.php b/Tests/Constraints/CharsetTest.php new file mode 100644 index 000000000..893066645 --- /dev/null +++ b/Tests/Constraints/CharsetTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Charset; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; + +class CharsetTest extends TestCase +{ + public function testSingleEncodingCanBeSet() + { + $encoding = new Charset('UTF-8'); + + $this->assertSame(['UTF-8'], $encoding->encodings); + } + + public function testMultipleEncodingCanBeSet() + { + $encoding = new Charset(['ASCII', 'UTF-8']); + + $this->assertSame(['ASCII', 'UTF-8'], $encoding->encodings); + } + + public function testThrowsOnNoCharset() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Charset" constraint requires at least one encoding.'); + + new Charset(); + } + + public function testAttributes() + { + $metadata = new ClassMetadata(CharsetDummy::class); + $loader = new AttributeLoader(); + $this->assertTrue($loader->loadClassMetadata($metadata)); + + [$aConstraint] = $metadata->properties['a']->getConstraints(); + $this->assertSame(['UTF-8'], $aConstraint->encodings); + + [$bConstraint] = $metadata->properties['b']->getConstraints(); + $this->assertSame(['ASCII', 'UTF-8'], $bConstraint->encodings); + } +} + +class CharsetDummy +{ + #[Charset('UTF-8')] + private string $a; + + #[Charset(['ASCII', 'UTF-8'])] + private string $b; +} diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php new file mode 100644 index 000000000..20a3fe884 --- /dev/null +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\Charset; +use Symfony\Component\Validator\Constraints\CharsetValidator; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class CharsetValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): CharsetValidator + { + return new CharsetValidator(); + } + + /** + * @dataProvider provideValidValues + */ + public function testEncodingIsValid(string $value, array $encodings) + { + $this->validator->validate($value, new Charset(encodings: $encodings)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider provideInvalidValues + */ + public function testInvalidValues(string $value, array $encodings) + { + $this->validator->validate($value, new Charset(encodings: $encodings)); + + $this->buildViolation('The detected encoding "{{ detected }}" does not match one of the accepted encoding: "{{ encodings }}".') + ->setParameter('{{ detected }}', mb_detect_encoding($value, $encodings, true)) + ->setParameter('{{ encodings }}', implode(', ', $encodings)) + ->setCode(Charset::BAD_ENCODING_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider provideInvalidTypes + */ + public function testNonStringValues(mixed $value) + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessageMatches('/Expected argument of type "string", ".*" given/'); + + $this->validator->validate($value, new Charset(encodings: ['UTF-8'])); + } + + public static function provideValidValues() + { + yield ['my ascii string', ['ASCII']]; + yield ['my ascii string', ['UTF-8']]; + yield ['my ascii string', ['ASCII', 'UTF-8']]; + yield ['my ûtf 8', ['ASCII', 'UTF-8']]; + yield ['my ûtf 8', ['UTF-8']]; + yield ['ώ', ['UTF-16']]; + } + + public static function provideInvalidValues() + { + yield ['my non-Äscîi string', ['ASCII']]; + yield ['😊', ['7bit']]; + } + + public static function provideInvalidTypes() + { + yield [true]; + yield [false]; + yield [1]; + yield [1.1]; + yield [[]]; + yield [new \stdClass()]; + } +} From 957ba7e8cdef1232a5e1c7186ccfed69c6aeb588 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 27 Dec 2023 14:01:16 +0100 Subject: [PATCH 019/149] [Validator] Fix `Charset` validator test data --- Tests/Constraints/CharsetValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 20a3fe884..d3a237fdb 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -65,7 +65,7 @@ public static function provideValidValues() yield ['my ascii string', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['UTF-8']]; - yield ['ώ', ['UTF-16']]; + yield ['string', ['ISO-8859-1']]; } public static function provideInvalidValues() From da52a809b25db838b8b45bfeb0af56b76ac0c70f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 28 Dec 2023 11:05:25 +0100 Subject: [PATCH 020/149] [Validator] Update `Charset` constraint message --- Constraints/Charset.php | 2 +- Tests/Constraints/CharsetValidatorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Constraints/Charset.php b/Constraints/Charset.php index a864a440f..29da9a076 100644 --- a/Constraints/Charset.php +++ b/Constraints/Charset.php @@ -27,7 +27,7 @@ final class Charset extends Constraint ]; public array|string $encodings = []; - public string $message = 'The detected encoding "{{ detected }}" does not match one of the accepted encoding: "{{ encodings }}".'; + public string $message = 'The detected character encoding "{{ detected }}" is invalid. Allowed encodings are "{{ encodings }}".'; public function __construct(array|string $encodings = null, string $message = null, array $groups = null, mixed $payload = null, array $options = null) { diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 20a3fe884..0aa814a8d 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -40,7 +40,7 @@ public function testInvalidValues(string $value, array $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); - $this->buildViolation('The detected encoding "{{ detected }}" does not match one of the accepted encoding: "{{ encodings }}".') + $this->buildViolation('The detected character encoding "{{ detected }}" is invalid. Allowed encodings are "{{ encodings }}".') ->setParameter('{{ detected }}', mb_detect_encoding($value, $encodings, true)) ->setParameter('{{ encodings }}', implode(', ', $encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) From d9dc3952b352ae3f8365411622b219a92380a195 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 28 Dec 2023 13:41:08 +0100 Subject: [PATCH 021/149] [Validator] Improve `Charset` constraint message --- Constraints/Charset.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/Charset.php b/Constraints/Charset.php index 29da9a076..37346b925 100644 --- a/Constraints/Charset.php +++ b/Constraints/Charset.php @@ -27,7 +27,7 @@ final class Charset extends Constraint ]; public array|string $encodings = []; - public string $message = 'The detected character encoding "{{ detected }}" is invalid. Allowed encodings are "{{ encodings }}".'; + public string $message = 'The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.'; public function __construct(array|string $encodings = null, string $message = null, array $groups = null, mixed $payload = null, array $options = null) { From 5fcf97d36cbc0ebd796364fcb497bf77a647a661 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 27 Dec 2023 23:59:38 +0100 Subject: [PATCH 022/149] support Stringable instances in CharsetValidator --- Constraints/CharsetValidator.php | 2 +- Tests/Constraints/CharsetValidatorTest.php | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Constraints/CharsetValidator.php b/Constraints/CharsetValidator.php index 2a4ca66a4..c682d67c3 100644 --- a/Constraints/CharsetValidator.php +++ b/Constraints/CharsetValidator.php @@ -31,7 +31,7 @@ public function validate(mixed $value, Constraint $constraint): void return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 1c6c4037a..f13bab003 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -26,7 +26,7 @@ protected function createValidator(): CharsetValidator /** * @dataProvider provideValidValues */ - public function testEncodingIsValid(string $value, array $encodings) + public function testEncodingIsValid(string|\Stringable $value, array $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); @@ -66,6 +66,12 @@ public static function provideValidValues() yield ['my ûtf 8', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['UTF-8']]; yield ['string', ['ISO-8859-1']]; + yield [new class() implements \Stringable { + public function __toString(): string + { + return 'my ûtf 8'; + } + }, ['UTF-8']]; } public static function provideInvalidValues() From 88a4268962d649c94efdb8d7bbd85fa992acf74c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 28 Dec 2023 19:10:09 +0100 Subject: [PATCH 023/149] fix merge --- Tests/Constraints/CharsetValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index f13bab003..9044e1c12 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -40,7 +40,7 @@ public function testInvalidValues(string $value, array $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); - $this->buildViolation('The detected character encoding "{{ detected }}" is invalid. Allowed encodings are "{{ encodings }}".') + $this->buildViolation('The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.') ->setParameter('{{ detected }}', mb_detect_encoding($value, $encodings, true)) ->setParameter('{{ encodings }}', implode(', ', $encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) From 43cd873b8eef0e0f3e500b22dc9958cce6be17b3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 29 Dec 2023 14:35:52 +0100 Subject: [PATCH 024/149] [Validator] Fix listing encodings in CharsetValidator --- Constraints/CharsetValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/CharsetValidator.php b/Constraints/CharsetValidator.php index c682d67c3..fd0adccfe 100644 --- a/Constraints/CharsetValidator.php +++ b/Constraints/CharsetValidator.php @@ -38,7 +38,7 @@ public function validate(mixed $value, Constraint $constraint): void if (!\in_array($detected = mb_detect_encoding($value, $constraint->encodings, true), $constraint->encodings, true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ detected }}', $detected) - ->setParameter('{{ encodings }}', implode('", "', $constraint->encodings)) + ->setParameter('{{ encodings }}', implode(', ', $constraint->encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) ->addViolation(); } From 2be2fcb62c0a2458691938bc2ebf27245347919c Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 27 Dec 2023 13:57:19 +0100 Subject: [PATCH 025/149] DX: re-apply CS --- Constraints/GroupSequenceProvider.php | 1 - Tests/Constraints/ImageValidatorTest.php | 1 - Tests/Constraints/IpValidatorTest.php | 2 +- Tests/Constraints/RangeTest.php | 6 +++--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Constraints/GroupSequenceProvider.php b/Constraints/GroupSequenceProvider.php index f069f9e4a..b72dd0c4c 100644 --- a/Constraints/GroupSequenceProvider.php +++ b/Constraints/GroupSequenceProvider.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Validator\Constraints; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; use Symfony\Component\Validator\Attribute\HasNamedArguments; /** diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index 180b00cd3..6523173a1 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraints\Image; use Symfony\Component\Validator\Constraints\ImageValidator; -use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; /** diff --git a/Tests/Constraints/IpValidatorTest.php b/Tests/Constraints/IpValidatorTest.php index 2a0bdcf70..82068184d 100644 --- a/Tests/Constraints/IpValidatorTest.php +++ b/Tests/Constraints/IpValidatorTest.php @@ -95,7 +95,7 @@ public function testValidIpV6WithWhitespacesNamed() { $this->validator->validate( "\n\t2001:0db8:85a3:0000:0000:8a2e:0370:7334\r\n", - new Ip(version: \Symfony\Component\Validator\Constraints\Ip::V6, normalizer: 'trim') + new Ip(version: Ip::V6, normalizer: 'trim') ); $this->assertNoViolation(); diff --git a/Tests/Constraints/RangeTest.php b/Tests/Constraints/RangeTest.php index 132be131d..f51898299 100644 --- a/Tests/Constraints/RangeTest.php +++ b/Tests/Constraints/RangeTest.php @@ -30,7 +30,7 @@ public function testThrowsConstraintExceptionIfBothMinLimitAndPropertyPath() public function testThrowsConstraintExceptionIfBothMinLimitAndPropertyPathNamed() { - $this->expectException(\Symfony\Component\Validator\Exception\ConstraintDefinitionException::class); + $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('requires only one of the "min" or "minPropertyPath" options to be set, not both.'); new Range(min: 'min', minPropertyPath: 'minPropertyPath'); } @@ -47,7 +47,7 @@ public function testThrowsConstraintExceptionIfBothMaxLimitAndPropertyPath() public function testThrowsConstraintExceptionIfBothMaxLimitAndPropertyPathNamed() { - $this->expectException(\Symfony\Component\Validator\Exception\ConstraintDefinitionException::class); + $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('requires only one of the "max" or "maxPropertyPath" options to be set, not both.'); new Range(max: 'max', maxPropertyPath: 'maxPropertyPath'); } @@ -67,7 +67,7 @@ public function testThrowsNoDefaultOptionConfiguredException() public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMessageOrMaxMessage() { - $this->expectException(\Symfony\Component\Validator\Exception\ConstraintDefinitionException::class); + $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('can not use "minMessage" and "maxMessage" when the "min" and "max" options are both set. Use "notInRangeMessage" instead.'); new Range(min: 'min', max: 'max', minMessage: 'minMessage', maxMessage: 'maxMessage'); } From 81efa071b3c3644eb340b67948a9a87dabbf5987 Mon Sep 17 00:00:00 2001 From: Ninos Date: Fri, 6 Oct 2023 07:47:30 +0200 Subject: [PATCH 026/149] [Validator] Add `MacAddress` constraint for validating MAC address --- CHANGELOG.md | 1 + Constraints/MacAddress.php | 52 ++++++++ Constraints/MacAddressValidator.php | 53 ++++++++ Resources/translations/validators.de.xlf | 4 + Resources/translations/validators.en.xlf | 4 + Tests/Constraints/MacAddressTest.php | 70 ++++++++++ Tests/Constraints/MacAddressValidatorTest.php | 125 ++++++++++++++++++ 7 files changed, 309 insertions(+) create mode 100644 Constraints/MacAddress.php create mode 100644 Constraints/MacAddressValidator.php create mode 100644 Tests/Constraints/MacAddressTest.php create mode 100644 Tests/Constraints/MacAddressValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e65a1355..cf1358e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Add `MacAddress` constraint * Add `list` and `associative_array` types to `Type` constraint * Add the `Charset` constraint diff --git a/Constraints/MacAddress.php b/Constraints/MacAddress.php new file mode 100644 index 000000000..fda431d79 --- /dev/null +++ b/Constraints/MacAddress.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\InvalidArgumentException; + +/** + * Validates that a value is a valid MAC address. + * + * @author Ninos Ego + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class MacAddress extends Constraint +{ + public const INVALID_MAC_ERROR = 'a183fbff-6968-43b4-82a2-cc5cf7150036'; + + protected const ERROR_NAMES = [ + self::INVALID_MAC_ERROR => 'INVALID_MAC_ERROR', + ]; + + public string $message = 'This is not a valid MAC address.'; + + /** @var callable|null */ + public $normalizer; + + public function __construct( + array $options = null, + string $message = null, + callable $normalizer = null, + array $groups = null, + mixed $payload = null, + ) { + parent::__construct($options, $groups, $payload); + + $this->message = $message ?? $this->message; + $this->normalizer = $normalizer ?? $this->normalizer; + + if (null !== $this->normalizer && !\is_callable($this->normalizer)) { + throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + } + } +} diff --git a/Constraints/MacAddressValidator.php b/Constraints/MacAddressValidator.php new file mode 100644 index 000000000..a93f90b4d --- /dev/null +++ b/Constraints/MacAddressValidator.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; + +/** + * Validates whether a value is a valid MAC address. + * + * @author Ninos Ego + */ +class MacAddressValidator extends ConstraintValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof MacAddress) { + throw new UnexpectedTypeException($constraint, MacAddress::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!\is_scalar($value) && !$value instanceof \Stringable) { + throw new UnexpectedValueException($value, 'string'); + } + + $value = (string) $value; + + if (null !== $constraint->normalizer) { + $value = ($constraint->normalizer)($value); + } + + if (!filter_var($value, \FILTER_VALIDATE_MAC)) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->addViolation(); + } + } +} diff --git a/Resources/translations/validators.de.xlf b/Resources/translations/validators.de.xlf index 50b8874d5..f94bb11ee 100644 --- a/Resources/translations/validators.de.xlf +++ b/Resources/translations/validators.de.xlf @@ -434,6 +434,10 @@ The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}. Der erkannte Zeichensatz ist nicht gültig ({{ detected }}). Gültige Zeichensätze sind "{{ encodings }}". + + This is not a valid MAC address. + Dies ist keine gültige MAC-Adresse. + diff --git a/Resources/translations/validators.en.xlf b/Resources/translations/validators.en.xlf index 6a49fb39f..0fe425b20 100644 --- a/Resources/translations/validators.en.xlf +++ b/Resources/translations/validators.en.xlf @@ -434,6 +434,10 @@ The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}. The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}. + + This is not a valid MAC address. + This is not a valid MAC address. + diff --git a/Tests/Constraints/MacAddressTest.php b/Tests/Constraints/MacAddressTest.php new file mode 100644 index 000000000..d39180bc0 --- /dev/null +++ b/Tests/Constraints/MacAddressTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\MacAddress; +use Symfony\Component\Validator\Exception\InvalidArgumentException; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; + +/** + * @author Ninos Ego + */ +class MacAddressTest extends TestCase +{ + public function testNormalizerCanBeSet() + { + $mac = new MacAddress(['normalizer' => 'trim']); + + $this->assertEquals('trim', $mac->normalizer); + } + + public function testInvalidNormalizerThrowsException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The "normalizer" option must be a valid callable ("string" given).'); + new MacAddress(['normalizer' => 'Unknown Callable']); + } + + public function testInvalidNormalizerObjectThrowsException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The "normalizer" option must be a valid callable ("stdClass" given).'); + new MacAddress(['normalizer' => new \stdClass()]); + } + + public function testAttributes() + { + $metadata = new ClassMetadata(MacAddressDummy::class); + $loader = new AttributeLoader(); + self::assertTrue($loader->loadClassMetadata($metadata)); + + [$aConstraint] = $metadata->properties['a']->getConstraints(); + self::assertSame('myMessage', $aConstraint->message); + self::assertSame('trim', $aConstraint->normalizer); + self::assertSame(['Default', 'MacAddressDummy'], $aConstraint->groups); + + [$bConstraint] = $metadata->properties['b']->getConstraints(); + self::assertSame(['my_group'], $bConstraint->groups); + self::assertSame('some attached data', $bConstraint->payload); + } +} + +class MacAddressDummy +{ + #[MacAddress(message: 'myMessage', normalizer: 'trim')] + private $a; + + #[MacAddress(groups: ['my_group'], payload: 'some attached data')] + private $b; +} diff --git a/Tests/Constraints/MacAddressValidatorTest.php b/Tests/Constraints/MacAddressValidatorTest.php new file mode 100644 index 000000000..0c56bcd23 --- /dev/null +++ b/Tests/Constraints/MacAddressValidatorTest.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\MacAddress; +use Symfony\Component\Validator\Constraints\MacAddressValidator; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +/** + * @author Ninos Ego + */ +class MacAddressValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): MacAddressValidator + { + return new MacAddressValidator(); + } + + public function testNullIsValid() + { + $this->validator->validate(null, new MacAddress()); + + $this->assertNoViolation(); + } + + public function testEmptyStringIsValid() + { + $this->validator->validate('', new MacAddress()); + + $this->assertNoViolation(); + } + + public function testExpectsStringCompatibleType() + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate(new \stdClass(), new MacAddress()); + } + + /** + * @dataProvider getValidMacs + */ + public function testValidMac($mac) + { + $this->validator->validate($mac, new MacAddress()); + + $this->assertNoViolation(); + } + + public static function getValidMacs(): array + { + return [ + ['00:00:00:00:00:00'], + ['00-00-00-00-00-00'], + ['ff:ff:ff:ff:ff:ff'], + ['ff-ff-ff-ff-ff-ff'], + ['FF:FF:FF:FF:FF:FF'], + ['FF-FF-FF-FF-FF-FF'], + ]; + } + + /** + * @dataProvider getValidMacsWithWhitespaces + */ + public function testValidMacsWithWhitespaces($mac) + { + $this->validator->validate($mac, new MacAddress([ + 'normalizer' => 'trim', + ])); + + $this->assertNoViolation(); + } + + public static function getValidMacsWithWhitespaces(): array + { + return [ + ["\x2000:00:00:00:00:00"], + ["\x09\x0900-00-00-00-00-00"], + ["ff:ff:ff:ff:ff:ff\x0A"], + ["ff-ff-ff-ff-ff-ff\x0D\x0D"], + ["\x00FF:FF:FF:FF:FF:FF\x00"], + ["\x0B\x0BFF-FF-FF-FF-FF-FF\x0B\x0B"], + ]; + } + + /** + * @dataProvider getInvalidMacs + */ + public function testInvalidMacs($mac) + { + $constraint = new MacAddress([ + 'message' => 'myMessage', + ]); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + public static function getInvalidMacs(): array + { + return [ + ['0'], + ['00:00'], + ['00:00:00'], + ['00:00:00:00'], + ['00:00:00:00:00'], + ['00:00:00:00:00:000'], + ['-00:00:00:00:00:00'], + ['foobar'], + ]; + } +} From 28037377a2275aec7cd5e1ac3de2c77cd40ccf97 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 2 Jan 2024 14:57:47 +0100 Subject: [PATCH 027/149] support Stringable values in all constraints --- CHANGELOG.md | 1 + Constraints/CidrValidator.php | 2 +- Constraints/CssColorValidator.php | 4 +-- Constraints/ExpressionSyntaxValidator.php | 2 +- Constraints/PasswordStrengthValidator.php | 4 +-- Tests/Constraints/CharsetValidatorTest.php | 8 ++---- Tests/Constraints/CidrValidatorTest.php | 7 ++++-- Tests/Constraints/CssColorValidatorTest.php | 4 ++- .../ExpressionSyntaxValidatorTest.php | 25 +++++++++++++++++++ .../Constraints/Fixtures/StringableValue.php | 24 ++++++++++++++++++ .../PasswordStrengthValidatorTest.php | 4 ++- 11 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 Tests/Constraints/Fixtures/StringableValue.php diff --git a/CHANGELOG.md b/CHANGELOG.md index cf1358e94..1a0a50b42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Add support for `Stringable` values when using the `Cidr`, `CssColor`, `ExpressionSyntax` and `PasswordStrength` constraints * Add `MacAddress` constraint * Add `list` and `associative_array` types to `Type` constraint * Add the `Charset` constraint diff --git a/Constraints/CidrValidator.php b/Constraints/CidrValidator.php index c90ebcfae..38168ebb4 100644 --- a/Constraints/CidrValidator.php +++ b/Constraints/CidrValidator.php @@ -28,7 +28,7 @@ public function validate($value, Constraint $constraint): void return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } diff --git a/Constraints/CssColorValidator.php b/Constraints/CssColorValidator.php index 9e8b1b55c..d55191b41 100644 --- a/Constraints/CssColorValidator.php +++ b/Constraints/CssColorValidator.php @@ -62,7 +62,7 @@ public function validate($value, Constraint $constraint): void return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } @@ -76,7 +76,7 @@ public function validate($value, Constraint $constraint): void } $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue((string) $value)) ->setCode(CssColor::INVALID_FORMAT_ERROR) ->addViolation(); } diff --git a/Constraints/ExpressionSyntaxValidator.php b/Constraints/ExpressionSyntaxValidator.php index 4b20de302..c0c758d6a 100644 --- a/Constraints/ExpressionSyntaxValidator.php +++ b/Constraints/ExpressionSyntaxValidator.php @@ -40,7 +40,7 @@ public function validate(mixed $expression, Constraint $constraint): void return; } - if (!\is_string($expression)) { + if (!\is_string($expression) && !$expression instanceof \Stringable) { throw new UnexpectedValueException($expression, 'string'); } diff --git a/Constraints/PasswordStrengthValidator.php b/Constraints/PasswordStrengthValidator.php index c3d2b7d76..72227b85a 100644 --- a/Constraints/PasswordStrengthValidator.php +++ b/Constraints/PasswordStrengthValidator.php @@ -36,11 +36,11 @@ public function validate(#[\SensitiveParameter] mixed $value, Constraint $constr return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } $passwordStrengthEstimator = $this->passwordStrengthEstimator ?? self::estimateStrength(...); - $strength = $passwordStrengthEstimator($value); + $strength = $passwordStrengthEstimator((string) $value); if ($strength < $constraint->minScore) { $this->context->buildViolation($constraint->message) diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 9044e1c12..4d33080d0 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\CharsetValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class CharsetValidatorTest extends ConstraintValidatorTestCase { @@ -66,12 +67,7 @@ public static function provideValidValues() yield ['my ûtf 8', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['UTF-8']]; yield ['string', ['ISO-8859-1']]; - yield [new class() implements \Stringable { - public function __toString(): string - { - return 'my ûtf 8'; - } - }, ['UTF-8']]; + yield [new StringableValue('my ûtf 8'), ['UTF-8']]; } public static function provideInvalidValues() diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 7c5745ee6..59caf3872 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class CidrValidatorTest extends ConstraintValidatorTestCase { @@ -83,7 +84,7 @@ public function testInvalidIpValue(string $cidr) /** * @dataProvider getValid */ - public function testValidCidr(string $cidr, string $version) + public function testValidCidr(string|\Stringable $cidr, string $version) { $this->validator->validate($cidr, new Cidr(['version' => $version])); @@ -93,7 +94,7 @@ public function testValidCidr(string $cidr, string $version) /** * @dataProvider getWithInvalidMasksAndIps */ - public function testInvalidIpAddressAndNetmask(string $cidr) + public function testInvalidIpAddressAndNetmask(string|\Stringable $cidr) { $this->validator->validate($cidr, new Cidr()); $this @@ -195,6 +196,7 @@ public static function getValid(): array ['::255.255.255.255/32', Ip::V6], ['::123.45.67.178/120', Ip::V6], ['::123.45.67.178/120', Ip::ALL], + [new StringableValue('::123.45.67.178/120'), Ip::ALL], ]; } @@ -233,6 +235,7 @@ public static function getWithInvalidMasksAndIps(): array ['::0.0.0/a/'], ['::256.0.0.0/-1aa'], ['::0.256.0.0/1b'], + [new StringableValue('::0.256.0.0/1b')], ]; } diff --git a/Tests/Constraints/CssColorValidatorTest.php b/Tests/Constraints/CssColorValidatorTest.php index 081d41a57..1f509130e 100644 --- a/Tests/Constraints/CssColorValidatorTest.php +++ b/Tests/Constraints/CssColorValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\CssColorValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; final class CssColorValidatorTest extends ConstraintValidatorTestCase { @@ -67,6 +68,7 @@ public static function getValidAnyColor(): array ['rgba(255, 255, 255, 0.3)'], ['hsl(0, 0%, 20%)'], ['hsla(0, 0%, 20%, 0.4)'], + [new StringableValue('hsla(0, 0%, 20%, 0.4)')], ]; } @@ -323,7 +325,7 @@ public function testInvalidNamedColors($cssColor) public static function getInvalidNamedColors(): array { - return [['fabpot'], ['ngrekas'], ['symfony'], ['FABPOT'], ['NGREKAS'], ['SYMFONY']]; + return [['fabpot'], ['ngrekas'], ['symfony'], ['FABPOT'], ['NGREKAS'], ['SYMFONY'], [new StringableValue('SYMFONY')]]; } /** diff --git a/Tests/Constraints/ExpressionSyntaxValidatorTest.php b/Tests/Constraints/ExpressionSyntaxValidatorTest.php index de316f47e..d7e62cbd2 100644 --- a/Tests/Constraints/ExpressionSyntaxValidatorTest.php +++ b/Tests/Constraints/ExpressionSyntaxValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\ExpressionSyntax; use Symfony\Component\Validator\Constraints\ExpressionSyntaxValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class ExpressionSyntaxValidatorTest extends ConstraintValidatorTestCase { @@ -47,6 +48,16 @@ public function testExpressionValid() $this->assertNoViolation(); } + public function testStringableExpressionValid() + { + $this->validator->validate(new StringableValue('1 + 1'), new ExpressionSyntax([ + 'message' => 'myMessage', + 'allowedVariables' => [], + ])); + + $this->assertNoViolation(); + } + public function testExpressionWithoutNames() { $this->validator->validate('1 + 1', new ExpressionSyntax([ @@ -79,4 +90,18 @@ public function testExpressionIsNotValid() ->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR) ->assertRaised(); } + + public function testStringableExpressionIsNotValid() + { + $this->validator->validate(new StringableValue('a + 1'), new ExpressionSyntax([ + 'message' => 'myMessage', + 'allowedVariables' => [], + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."') + ->setInvalidValue('a + 1') + ->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR) + ->assertRaised(); + } } diff --git a/Tests/Constraints/Fixtures/StringableValue.php b/Tests/Constraints/Fixtures/StringableValue.php new file mode 100644 index 000000000..0cd2f4ddb --- /dev/null +++ b/Tests/Constraints/Fixtures/StringableValue.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; + +class StringableValue implements \Stringable +{ + public function __construct(private string $value) + { + } + + public function __toString(): string + { + return $this->value; + } +} diff --git a/Tests/Constraints/PasswordStrengthValidatorTest.php b/Tests/Constraints/PasswordStrengthValidatorTest.php index 21dabcad7..e279843f3 100644 --- a/Tests/Constraints/PasswordStrengthValidatorTest.php +++ b/Tests/Constraints/PasswordStrengthValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraints\PasswordStrength; use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class PasswordStrengthValidatorTest extends ConstraintValidatorTestCase { @@ -25,7 +26,7 @@ protected function createValidator(): PasswordStrengthValidator /** * @dataProvider getValidValues */ - public function testValidValues(string $value, int $expectedStrength) + public function testValidValues(string|\Stringable $value, int $expectedStrength) { $this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength)); @@ -48,6 +49,7 @@ public static function getValidValues(): iterable yield ['Reasonable-pwd', PasswordStrength::STRENGTH_MEDIUM]; yield ['This 1s a very g00d Pa55word! ;-)', PasswordStrength::STRENGTH_VERY_STRONG]; yield ['pudding-smack-👌🏼-fox-😎', PasswordStrength::STRENGTH_VERY_STRONG]; + yield [new StringableValue('How-is-this'), PasswordStrength::STRENGTH_WEAK]; } /** From 835cc834cf323f0a13062065fe4d5cae4f6257d7 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Tue, 2 Jan 2024 15:49:33 +0100 Subject: [PATCH 028/149] CS: trailing commas --- Constraints/Cidr.php | 2 +- Constraints/Count.php | 2 +- Constraints/Country.php | 2 +- Constraints/Email.php | 2 +- Constraints/Hostname.php | 2 +- Constraints/Image.php | 2 +- Constraints/Ip.php | 2 +- Constraints/Isbn.php | 2 +- Constraints/Issn.php | 2 +- Constraints/Language.php | 2 +- Constraints/Length.php | 2 +- Constraints/Locale.php | 2 +- Constraints/Luhn.php | 2 +- Constraints/NoSuspiciousCharacters.php | 2 +- Constraints/NotCompromisedPassword.php | 2 +- Constraints/Range.php | 2 +- Constraints/Regex.php | 2 +- Constraints/Timezone.php | 2 +- Constraints/Ulid.php | 2 +- Constraints/Url.php | 2 +- Constraints/Uuid.php | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index b62e6c122..b3a892803 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -50,7 +50,7 @@ public function __construct( int $netmaskMax = null, string $message = null, array $groups = null, - $payload = null + $payload = null, ) { $this->version = $version ?? $options['version'] ?? $this->version; diff --git a/Constraints/Count.php b/Constraints/Count.php index f18c99c84..0153a2611 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -51,7 +51,7 @@ public function __construct( string $divisibleByMessage = null, array $groups = null, mixed $payload = null, - array $options = [] + array $options = [], ) { if (\is_array($exactly)) { $options = array_merge($exactly, $options); diff --git a/Constraints/Country.php b/Constraints/Country.php index f3dfeaa9a..b8c9bb868 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -35,7 +35,7 @@ public function __construct( string $message = null, bool $alpha3 = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { if (!class_exists(Countries::class)) { throw new LogicException('The Intl component is required to use the Country constraint. Try running "composer require symfony/intl".'); diff --git a/Constraints/Email.php b/Constraints/Email.php index 6da89ebef..5bc838a68 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -49,7 +49,7 @@ public function __construct( string $mode = null, callable $normalizer = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { if (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::VALIDATION_MODES, true)) { throw new InvalidArgumentException('The "mode" parameter value is not valid.'); diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index 28f2a9b2d..1536bf6f8 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -33,7 +33,7 @@ public function __construct( string $message = null, bool $requireTld = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Image.php b/Constraints/Image.php index 1b37a54d5..95f553c3b 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -131,7 +131,7 @@ public function __construct( string $allowPortraitMessage = null, string $corruptedMessage = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct( $options, diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 35e47deea..57167679f 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -78,7 +78,7 @@ public function __construct( string $message = null, callable $normalizer = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index 9b7c42ab8..d3a35fa88 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -52,7 +52,7 @@ public function __construct( string $bothIsbnMessage = null, array $groups = null, mixed $payload = null, - array $options = [] + array $options = [], ) { if (\is_array($type)) { $options = array_merge($type, $options); diff --git a/Constraints/Issn.php b/Constraints/Issn.php index d03b835c6..314a75731 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -46,7 +46,7 @@ public function __construct( bool $caseSensitive = null, bool $requireHyphen = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Language.php b/Constraints/Language.php index fe38adcf8..ea7efb378 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -35,7 +35,7 @@ public function __construct( string $message = null, bool $alpha3 = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { if (!class_exists(Languages::class)) { throw new LogicException('The Intl component is required to use the Language constraint. Try running "composer require symfony/intl".'); diff --git a/Constraints/Length.php b/Constraints/Length.php index 740bfe27a..6653ff407 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -71,7 +71,7 @@ public function __construct( string $charsetMessage = null, array $groups = null, mixed $payload = null, - array $options = [] + array $options = [], ) { if (\is_array($exactly)) { $options = array_merge($exactly, $options); diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 964c81da7..a6891bc67 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -35,7 +35,7 @@ public function __construct( string $message = null, bool $canonicalize = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { if (!class_exists(Locales::class)) { throw new LogicException('The Intl component is required to use the Locale constraint. Try running "composer require symfony/intl".'); diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index f370067bd..97acd3f92 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -37,7 +37,7 @@ public function __construct( array $options = null, string $message = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/NoSuspiciousCharacters.php b/Constraints/NoSuspiciousCharacters.php index 479a78640..f8a19503f 100644 --- a/Constraints/NoSuspiciousCharacters.php +++ b/Constraints/NoSuspiciousCharacters.php @@ -91,7 +91,7 @@ public function __construct( int $restrictionLevel = null, array $locales = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { if (!class_exists(\Spoofchecker::class)) { throw new LogicException('The intl extension is required to use the NoSuspiciousCharacters constraint.'); diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index 2c3924e87..03cab4e26 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -37,7 +37,7 @@ public function __construct( int $threshold = null, bool $skipOnError = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Range.php b/Constraints/Range.php index 8d8d1dfa8..d26e61457 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -57,7 +57,7 @@ public function __construct( mixed $max = null, string $maxPropertyPath = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 53a525fa3..827da933d 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -41,7 +41,7 @@ public function __construct( callable $normalizer = null, array $groups = null, mixed $payload = null, - array $options = [] + array $options = [], ) { if (\is_array($pattern)) { $options = array_merge($pattern, $options); diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index 5fc5701cf..561fb720d 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -45,7 +45,7 @@ public function __construct( bool $intlCompatible = null, array $groups = null, mixed $payload = null, - array $options = [] + array $options = [], ) { if (\is_array($zone)) { $options = array_merge($zone, $options); diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index bbee1417b..1ad1eba0f 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -37,7 +37,7 @@ public function __construct( array $options = null, string $message = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Url.php b/Constraints/Url.php index 65cbffc0a..7873bcc74 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -39,7 +39,7 @@ public function __construct( bool $relativeProtocol = null, callable $normalizer = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index f497b00dc..b1ed6e1eb 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -99,7 +99,7 @@ public function __construct( bool $strict = null, callable $normalizer = null, array $groups = null, - mixed $payload = null + mixed $payload = null, ) { parent::__construct($options, $groups, $payload); From 8fb38a8e174096d35957531f0ce16394275a169e Mon Sep 17 00:00:00 2001 From: Nicolas Rigaud Date: Wed, 11 Oct 2023 18:07:14 +0200 Subject: [PATCH 029/149] [Validator] Add PHPDoc to validator constraints --- Attribute/HasNamedArguments.php | 5 ++++ Constraints/All.php | 7 ++++++ Constraints/AtLeastOneOf.php | 9 ++++++++ Constraints/Bic.php | 10 ++++++++ Constraints/Blank.php | 6 +++++ Constraints/Callback.php | 6 +++++ Constraints/CardScheme.php | 7 +++++- Constraints/Cascade.php | 6 +++++ Constraints/Choice.php | 12 ++++++++++ Constraints/Cidr.php | 9 ++++++++ Constraints/Collection.php | 10 +++++++- Constraints/Count.php | 10 ++++++++ Constraints/Country.php | 11 +++++++++ Constraints/CssColor.php | 6 ++++- Constraints/Currency.php | 8 +++++++ Constraints/Date.php | 8 +++++++ Constraints/DateTime.php | 9 ++++++++ Constraints/DisableAutoMapping.php | 3 +++ Constraints/DivisibleBy.php | 2 ++ Constraints/Email.php | 7 ++++++ Constraints/EnableAutoMapping.php | 3 +++ Constraints/EqualTo.php | 2 ++ Constraints/Expression.php | 11 +++++++++ Constraints/ExpressionSyntax.php | 8 +++++++ Constraints/File.php | 23 +++++++++++++++++- Constraints/GreaterThan.php | 2 ++ Constraints/GreaterThanOrEqual.php | 2 ++ Constraints/Hostname.php | 7 ++++++ Constraints/Iban.php | 8 +++++++ Constraints/IdenticalTo.php | 2 ++ Constraints/Image.php | 32 ++++++++++++++++++++++++++ Constraints/Ip.php | 5 ++++ Constraints/IsFalse.php | 6 +++++ Constraints/IsNull.php | 6 +++++ Constraints/IsTrue.php | 6 +++++ Constraints/Isbn.php | 10 ++++++++ Constraints/Isin.php | 8 +++++++ Constraints/Issn.php | 10 ++++++++ Constraints/Json.php | 6 +++++ Constraints/Language.php | 9 ++++++++ Constraints/Length.php | 12 +++++++++- Constraints/LessThan.php | 2 ++ Constraints/LessThanOrEqual.php | 2 ++ Constraints/Locale.php | 9 ++++++++ Constraints/Luhn.php | 8 ++++++- Constraints/Negative.php | 2 ++ Constraints/NegativeOrZero.php | 2 ++ Constraints/NoSuspiciousCharacters.php | 12 ++++++++-- Constraints/NotBlank.php | 7 ++++++ Constraints/NotCompromisedPassword.php | 6 +++++ Constraints/NotEqualTo.php | 2 ++ Constraints/NotIdenticalTo.php | 2 ++ Constraints/NotNull.php | 6 +++++ Constraints/PasswordStrength.php | 7 ++++++ Constraints/Positive.php | 2 ++ Constraints/PositiveOrZero.php | 2 ++ Constraints/Range.php | 12 ++++++++++ Constraints/Regex.php | 9 ++++++++ Constraints/Sequentially.php | 4 ++++ Constraints/Time.php | 7 ++++++ Constraints/Timezone.php | 11 +++++++++ Constraints/Traverse.php | 5 ++++ Constraints/Type.php | 7 ++++++ Constraints/Ulid.php | 8 +++++++ Constraints/Unique.php | 6 ++++- Constraints/Url.php | 8 +++++++ Constraints/Uuid.php | 10 +++++++- Constraints/Valid.php | 7 ++++++ Constraints/When.php | 12 ++++++++++ 69 files changed, 496 insertions(+), 10 deletions(-) diff --git a/Attribute/HasNamedArguments.php b/Attribute/HasNamedArguments.php index cc1662943..564d42282 100644 --- a/Attribute/HasNamedArguments.php +++ b/Attribute/HasNamedArguments.php @@ -11,6 +11,11 @@ namespace Symfony\Component\Validator\Attribute; +/** + * Hints the loader that some constraint options are required. + * + * @see https://symfony.com/doc/current/validation/custom_constraint.html + */ #[\Attribute(\Attribute::TARGET_METHOD)] final class HasNamedArguments { diff --git a/Constraints/All.php b/Constraints/All.php index 95f882601..33236dc11 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -14,6 +14,9 @@ use Symfony\Component\Validator\Constraint; /** + * When applied to an array (or Traversable object), this constraint allows you to apply + * a collection of constraints to each element of the array. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -21,6 +24,10 @@ class All extends Composite { public array|Constraint $constraints = []; + /** + * @param array|array|null $constraints + * @param string[]|null $groups + */ public function __construct(mixed $constraints = null, array $groups = null, mixed $payload = null) { parent::__construct($constraints ?? [], $groups, $payload); diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index 60f5e31a6..0b0f76dbe 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Checks that at least one of the given constraint is satisfied. + * * @author Przemysław Bogusz */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -30,6 +32,13 @@ class AtLeastOneOf extends Composite public string $messageCollection = 'Each element of this collection should satisfy its own set of constraints.'; public bool $includeInternalMessages = true; + /** + * @param array|array|null $constraints An array of validation constraints + * @param string[]|null $groups + * @param string|null $message Intro of the failure message that will be followed by the failed constraint(s) message(s) + * @param string|null $messageCollection Failure message for All and Collection inner constraints + * @param bool|null $includeInternalMessages Whether to include inner constraint messages (defaults to true) + */ public function __construct(mixed $constraints = null, array $groups = null, mixed $payload = null, string $message = null, string $messageCollection = null, bool $includeInternalMessages = null) { parent::__construct($constraints ?? [], $groups, $payload); diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 47f523f89..653caad0a 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -18,6 +18,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Ensures that the value is valid against the BIC format. + * + * @see https://en.wikipedia.org/wiki/ISO_9362 + * * @author Michael Hirschler */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -43,6 +47,12 @@ class Bic extends Constraint public ?string $iban = null; public ?string $ibanPropertyPath = null; + /** + * @param array|null $options + * @param string|null $iban An IBAN value to validate that its country code is the same as the BIC's one + * @param string|null $ibanPropertyPath Property path to the IBAN value when validating objects + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, string $iban = null, string $ibanPropertyPath = null, string $ibanMessage = null, array $groups = null, mixed $payload = null) { if (!class_exists(Countries::class)) { diff --git a/Constraints/Blank.php b/Constraints/Blank.php index 9f0b2be82..6f13155e2 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is blank, i.e. an empty string or null. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -27,6 +29,10 @@ class Blank extends Constraint public string $message = 'This value should be blank.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/Callback.php b/Constraints/Callback.php index c4bf70ea9..6d3912cc2 100644 --- a/Constraints/Callback.php +++ b/Constraints/Callback.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Defines custom validation rules through arbitrary callback methods. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -24,6 +26,10 @@ class Callback extends Constraint */ public $callback; + /** + * @param string|string[]|callable|array|null $callback The callback definition + * @param string[]|null $groups + */ public function __construct(array|string|callable $callback = null, array $groups = null, mixed $payload = null, array $options = []) { // Invocation through attributes with an array parameter only diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 3097ef8a6..19fdbe759 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -14,7 +14,7 @@ use Symfony\Component\Validator\Constraint; /** - * Metadata for the CardSchemeValidator. + * Validates a credit card number for a given credit card company. * * @author Tim Nagel * @author Bernhard Schussek @@ -46,6 +46,11 @@ class CardScheme extends Constraint public string $message = 'Unsupported card type or invalid card number.'; public array|string|null $schemes = null; + /** + * @param string|string[]|array|null $schemes Name(s) of the number scheme(s) used to validate the credit card number + * @param string[]|null $groups + * @param array $options + */ public function __construct(array|string|null $schemes, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { if (\is_array($schemes) && \is_string(key($schemes))) { diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index d353ebc81..3ef3b4bbc 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** + * Validates a whole class, including nested objects in properties. + * * @author Jules Pietri */ #[\Attribute(\Attribute::TARGET_CLASS)] @@ -22,6 +24,10 @@ class Cascade extends Constraint { public array $exclude = []; + /** + * @param string[]|string|array|null $exclude Properties excluded from validation + * @param array|null $options + */ public function __construct(array|string $exclude = null, array $options = null) { if (\is_array($exclude) && !array_is_list($exclude)) { diff --git a/Constraints/Choice.php b/Constraints/Choice.php index de1d87f5f..36f1595a8 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is one of a given set of valid choices. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -47,6 +49,16 @@ public function getDefaultOption(): ?string return 'choices'; } + /** + * @param array|null $choices An array of choices (required unless a callback is specified) + * @param callable|string|null $callback Callback method to use instead of the choice option to get the choices + * @param bool|null $multiple Whether to expect the value to be an array of valid choices (defaults to false) + * @param bool|null $strict This option defaults to true and should not be used + * @param int|null $min Minimum of valid choices if multiple values are expected + * @param int|null $max Maximum of valid choices if multiple values are expected + * @param string[]|null $groups + * @param bool|null $match Whether to validate the values are part of the choices or not (defaults to true) + */ public function __construct( string|array $options = [], array $choices = null, diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index b62e6c122..bdfaa0baa 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -17,6 +17,8 @@ /** * Validates that a value is a valid CIDR notation. * + * @see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing + * * @author Sorin Pop * @author Calin Bolea */ @@ -43,6 +45,13 @@ class Cidr extends Constraint public int $netmaskMin = 0; public int $netmaskMax; + /** + * @param array|null $options + * @param string|null $version The CIDR version to validate (4, 6 or all, defaults to all) + * @param int|null $netmaskMin The lowest valid for a valid netmask (defaults to 0) + * @param int|null $netmaskMax The biggest valid for a valid netmask (defaults to 32 for IPv4, 128 for IPv6) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $version = null, diff --git a/Constraints/Collection.php b/Constraints/Collection.php index 6f00751ef..956761282 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates a collection with constraints defined for specific keys. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -33,7 +35,13 @@ class Collection extends Composite public string $extraFieldsMessage = 'This field was not expected.'; public string $missingFieldsMessage = 'This field is missing.'; - public function __construct(array $fields = null, array $groups = null, mixed $payload = null, bool $allowExtraFields = null, bool $allowMissingFields = null, string $extraFieldsMessage = null, string $missingFieldsMessage = null) + /** + * @param array|array|null $fields An associative array defining keys in the collection and their constraints + * @param string[]|null $groups + * @param bool|null $allowExtraFields Whether to allow additional keys not declared in the configured fields (defaults to false) + * @param bool|null $allowMissingFields Whether to allow the collection to lack some fields declared in the configured fields (defaults to false) + */ + public function __construct(mixed $fields = null, array $groups = null, mixed $payload = null, bool $allowExtraFields = null, bool $allowMissingFields = null, string $extraFieldsMessage = null, string $missingFieldsMessage = null) { if (\is_array($fields) && (($firstField = reset($fields)) instanceof Constraint diff --git a/Constraints/Count.php b/Constraints/Count.php index f18c99c84..7cb4a555f 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\MissingOptionsException; /** + * Validates a collection's element count. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -40,6 +42,14 @@ class Count extends Constraint public ?int $max = null; public ?int $divisibleBy = null; + /** + * @param int|array|null $exactly The exact expected number of elements + * @param int|null $min Minimum expected number of elements + * @param int|null $max Maximum expected number of elements + * @param int|null $divisibleBy The number the collection count should be divisible by + * @param string[]|null $groups + * @param array $options + */ public function __construct( int|array $exactly = null, int $min = null, diff --git a/Constraints/Country.php b/Constraints/Country.php index f3dfeaa9a..0b6d96e39 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -16,6 +16,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates a value is a valid ISO 3166-1 alpha-2 country code. + * + * @see https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -30,6 +34,13 @@ class Country extends Constraint public string $message = 'This value is not a valid country.'; public bool $alpha3 = false; + /** + * @param array|null $options + * @param bool|null $alpha3 Whether to check for alpha-3 codes instead of alpha-2 (defaults to false) + * @param string[]|null $groups + * + * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3#Current_codes + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 641a2aa29..47b0e7224 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** + * Validates that a value is a valid CSS color. + * * @author Mathieu Santostefano */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -60,7 +62,9 @@ class CssColor extends Constraint public array|string $formats; /** - * @param array|string $formats The types of CSS colors allowed (e.g. hexadecimal only, RGB and HSL only, etc.). + * @param string[]|string|array $formats The types of CSS colors allowed ({@see https://symfony.com/doc/current/reference/constraints/CssColor.html#formats}) + * @param string[]|null $groups + * @param array|null $options */ public function __construct(array|string $formats = [], string $message = null, array $groups = null, $payload = null, array $options = null) { diff --git a/Constraints/Currency.php b/Constraints/Currency.php index f75412c72..ada418ab7 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -16,6 +16,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates that a value is a valid 3-letter ISO 4217 currency name. + * + * @see https://en.wikipedia.org/wiki/ISO_4217 + * * @author Miha Vrhovnik * @author Bernhard Schussek */ @@ -30,6 +34,10 @@ class Currency extends Constraint public string $message = 'This value is not a valid currency.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { if (!class_exists(Currencies::class)) { diff --git a/Constraints/Date.php b/Constraints/Date.php index b54f9b2ef..5d2be69ae 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid date, i.e. its string representation follows the Y-m-d format. + * + * @see https://www.php.net/manual/en/datetime.format.php + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -29,6 +33,10 @@ class Date extends Constraint public string $message = 'This value is not a valid date.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index 5f434346f..f35d6f2ff 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid "datetime" according to a given format. + * + * @see https://www.php.net/manual/en/datetime.format.php + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -32,6 +36,11 @@ class DateTime extends Constraint public string $format = 'Y-m-d H:i:s'; public string $message = 'This value is not a valid datetime.'; + /** + * @param string|array|null $format The datetime format to match (defaults to 'Y-m-d H:i:s') + * @param string[]|null $groups + * @param array $options + */ public function __construct(string|array $format = null, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { if (\is_array($format)) { diff --git a/Constraints/DisableAutoMapping.php b/Constraints/DisableAutoMapping.php index 68646f5c0..29cfd1923 100644 --- a/Constraints/DisableAutoMapping.php +++ b/Constraints/DisableAutoMapping.php @@ -25,6 +25,9 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)] class DisableAutoMapping extends Constraint { + /** + * @param array|null $options + */ public function __construct(array $options = null) { if (\is_array($options) && \array_key_exists('groups', $options)) { diff --git a/Constraints/DivisibleBy.php b/Constraints/DivisibleBy.php index f1490358d..e56ddfb7c 100644 --- a/Constraints/DivisibleBy.php +++ b/Constraints/DivisibleBy.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is divisible by another value. + * * @author Colin O'Dell */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Email.php b/Constraints/Email.php index 6da89ebef..ee1ae6ef7 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -17,6 +17,8 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates that a value is a valid email address. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -43,6 +45,11 @@ class Email extends Constraint /** @var callable|null */ public $normalizer; + /** + * @param array|null $options + * @param self::VALIDATION_MODE_*|null $mode The pattern used to validate the email address; pass null to use the default mode configured for the EmailValidator + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/EnableAutoMapping.php b/Constraints/EnableAutoMapping.php index fbedddd5d..43ef30e6c 100644 --- a/Constraints/EnableAutoMapping.php +++ b/Constraints/EnableAutoMapping.php @@ -25,6 +25,9 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)] class EnableAutoMapping extends Constraint { + /** + * @param array|null $options + */ public function __construct(array $options = null) { if (\is_array($options) && \array_key_exists('groups', $options)) { diff --git a/Constraints/EqualTo.php b/Constraints/EqualTo.php index 3a6f8f828..6ac5b22ca 100644 --- a/Constraints/EqualTo.php +++ b/Constraints/EqualTo.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is equal to another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Expression.php b/Constraints/Expression.php index a6bd83141..7d3511d9c 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -17,6 +17,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates a value using an expression from the Expression Language component. + * + * @see https://symfony.com/doc/current/components/expression_language.html + * * @author Fabien Potencier * @author Bernhard Schussek */ @@ -34,6 +38,13 @@ class Expression extends Constraint public array $values = []; public bool $negate = true; + /** + * @param string|ExpressionObject|array|null $expression The expression to evaluate + * @param array|null $values The values of the custom variables used in the expression (defaults to an empty array) + * @param string[]|null $groups + * @param array $options + * @param bool|null $negate Whether to fail is the expression evaluates to true (defaults to false) + */ public function __construct( string|ExpressionObject|array|null $expression, string $message = null, diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index b0255743e..082e41940 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is valid as an ExpressionLanguage expression. + * * @author Andrey Sevastianov */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -29,6 +31,12 @@ class ExpressionSyntax extends Constraint public ?string $service = null; public ?array $allowedVariables = null; + /** + * @param array|null $options + * @param string|null $service The service used to validate the constraint instead of the default one + * @param string[]|null $allowedVariables Restrict the available variables in the expression to these values (defaults to null that allows any variable) + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, string $service = null, array $allowedVariables = null, array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/File.php b/Constraints/File.php index 4ac288f5f..c6be69522 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -15,6 +15,12 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** + * Validates that a value is a valid "file". + * + * A file can be one of the following: + * - A string (or object with a __toString() method) path to an existing file; + * - A valid {@see \Symfony\Component\HttpFoundation\File\File File} object (including objects of {@see \Symfony\Component\HttpFoundation\File\UploadedFile UploadedFile} class). + * * @property int $maxSize * * @author Bernhard Schussek @@ -65,7 +71,22 @@ class File extends Constraint protected int|string|null $maxSize = null; /** - * @param array|string $extensions + * @param array|null $options + * @param int|string|null $maxSize The max size of the underlying file + * @param bool|null $binaryFormat Pass true to use binary-prefixed units (KiB, MiB, etc.) or false to use SI-prefixed units (kB, MB) in displayed messages. Pass null to guess the format from the maxSize option. (defaults to null) + * @param string[]|string|null $mimeTypes Acceptable media type(s). Prefer the extensions option that also enforce the file's extension consistency. + * @param int|null $filenameMaxLength Maximum length of the file name + * @param string|null $disallowEmptyMessage Enable empty upload validation with this message in case of error + * @param string|null $uploadIniSizeErrorMessage Message if the file size exceeds the max size configured in php.ini + * @param string|null $uploadFormSizeErrorMessage Message if the file size exceeds the max size configured in the HTML input field + * @param string|null $uploadPartialErrorMessage Message if the file is only partially uploaded + * @param string|null $uploadNoTmpDirErrorMessage Message if there is no upload_tmp_dir in php.ini + * @param string|null $uploadCantWriteErrorMessage Message if the uploaded file can not be stored in the temporary directory + * @param string|null $uploadErrorMessage Message if an unknown error occurred on upload + * @param string[]|null $groups + * @param array|string|null $extensions A list of valid extensions to check. Related media types are also enforced ({@see https://symfony.com/doc/current/reference/constraints/File.html#extensions}) + * + * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types */ public function __construct( array $options = null, diff --git a/Constraints/GreaterThan.php b/Constraints/GreaterThan.php index 99785aee0..7ce6cf04b 100644 --- a/Constraints/GreaterThan.php +++ b/Constraints/GreaterThan.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is greater than another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/GreaterThanOrEqual.php b/Constraints/GreaterThanOrEqual.php index 43c36e80e..103ca9f53 100644 --- a/Constraints/GreaterThanOrEqual.php +++ b/Constraints/GreaterThanOrEqual.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is greater than or equal to another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index 28f2a9b2d..7cd5b84a1 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid host name. + * * @author Dmitrii Poddubnyi */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -28,6 +30,11 @@ class Hostname extends Constraint public string $message = 'This value is not a valid hostname.'; public bool $requireTld = true; + /** + * @param array|null $options + * @param bool|null $requireTld Whether to require the hostname to include its top-level domain (defaults to true) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Iban.php b/Constraints/Iban.php index 601e2b01c..b652d2354 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid bank account number according to the IBAN format. + * + * @see https://en.wikipedia.org/wiki/International_Bank_Account_Number + * * @author Manuel Reinhard * @author Michael Schummel * @author Bernhard Schussek @@ -37,6 +41,10 @@ class Iban extends Constraint public string $message = 'This is not a valid International Bank Account Number (IBAN).'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/IdenticalTo.php b/Constraints/IdenticalTo.php index 6a8b7bdf2..e06cb5356 100644 --- a/Constraints/IdenticalTo.php +++ b/Constraints/IdenticalTo.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is identical to another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Image.php b/Constraints/Image.php index 1b37a54d5..efaf21258 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a file (or a path to a file) is a valid image. + * * @author Benjamin Dulau * @author Bernhard Schussek */ @@ -85,6 +87,36 @@ class Image extends File public string $allowPortraitMessage = 'The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.'; public string $corruptedMessage = 'The image file is corrupted.'; + /** + * @param array|null $options + * @param int|string|null $maxSize The max size of the underlying file + * @param bool|null $binaryFormat Pass true to use binary-prefixed units (KiB, MiB, etc.) or false to use SI-prefixed units (kB, MB) in displayed messages. Pass null to guess the format from the maxSize option. (defaults to null) + * @param string[]|null $mimeTypes Acceptable media types + * @param int|null $filenameMaxLength Maximum length of the file name + * @param string|null $disallowEmptyMessage Enable empty upload validation with this message in case of error + * @param string|null $uploadIniSizeErrorMessage Message if the file size exceeds the max size configured in php.ini + * @param string|null $uploadFormSizeErrorMessage Message if the file size exceeds the max size configured in the HTML input field + * @param string|null $uploadPartialErrorMessage Message if the file is only partially uploaded + * @param string|null $uploadNoTmpDirErrorMessage Message if there is no upload_tmp_dir in php.ini + * @param string|null $uploadCantWriteErrorMessage Message if the uploaded file can not be stored in the temporary directory + * @param string|null $uploadErrorMessage Message if an unknown error occurred on upload + * @param string[]|null $groups + * @param int|null $minWidth Minimum image width + * @param int|null $maxWidth Maximum image width + * @param int|null $maxHeight Maximum image height + * @param int|null $minHeight Minimum image weight + * @param int|float|null $maxRatio Maximum image ratio + * @param int|float|null $minRatio Minimum image ration + * @param int|float|null $minPixels Minimum amount of pixels + * @param int|float|null $maxPixels Maximum amount of pixels + * @param bool|null $allowSquare Whether to allow a square image (defaults to true) + * @param bool|null $allowLandscape Whether to allow a landscape image (defaults to true) + * @param bool|null $allowPortrait Whether to allow a portrait image (defaults to true) + * @param bool|null $detectCorrupted Whether to validate the image is not corrupted (defaults to false) + * @param string|null $sizeNotDetectedMessage Message if the system can not determine image size and there is a size constraint to validate + * + * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types + */ public function __construct( array $options = null, int|string $maxSize = null, diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 35e47deea..08051a50c 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -72,6 +72,11 @@ class Ip extends Constraint /** @var callable|null */ public $normalizer; + /** + * @param array|null $options + * @param self::V4*|self::V6*|self::ALL*|null $version The IP version to validate (defaults to {@see self::V4}) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $version = null, diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index 62dfc42e1..9a9d36ed0 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is false. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -27,6 +29,10 @@ class IsFalse extends Constraint public string $message = 'This value should be false.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index cbac402fa..d0de1a1d8 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is exactly equal to null. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -27,6 +29,10 @@ class IsNull extends Constraint public string $message = 'This value should be null.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index c574ffb37..2366f279a 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is true. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -27,6 +29,10 @@ class IsTrue extends Constraint public string $message = 'This value should be true.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index 9b7c42ab8..07f55f777 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid ISBN according to ISBN-10 or ISBN-13 formats. + * + * @see https://en.wikipedia.org/wiki/ISBN + * * @author The Whole Life To Learn * @author Manuel Reinhard * @author Bernhard Schussek @@ -44,6 +48,12 @@ class Isbn extends Constraint public ?string $type = null; public ?string $message = null; + /** + * @param self::ISBN_*|array|null $type The type of ISBN to validate (i.e. {@see Isbn::ISBN_10}, {@see Isbn::ISBN_13} or null to accept both, defaults to null) + * @param string|null $message If defined, this message has priority over the others + * @param string[]|null $groups + * @param array $options + */ public function __construct( string|array $type = null, string $message = null, diff --git a/Constraints/Isin.php b/Constraints/Isin.php index 496e3d903..dfe1ab65e 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid International Securities Identification Number (ISIN). + * + * @see https://en.wikipedia.org/wiki/International_Securities_Identification_Number + * * @author Laurent Masforné */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -34,6 +38,10 @@ class Isin extends Constraint public string $message = 'This value is not a valid International Securities Identification Number (ISIN).'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Issn.php b/Constraints/Issn.php index d03b835c6..63bcbe92e 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid International Standard Serial Number (ISSN). + * + * @see https://en.wikipedia.org/wiki/ISSN + * * @author Antonio J. García Lagar * @author Bernhard Schussek */ @@ -40,6 +44,12 @@ class Issn extends Constraint public bool $caseSensitive = false; public bool $requireHyphen = false; + /** + * @param array|null $options + * @param bool|null $caseSensitive Whether to allow the value to end with a lowercase character (defaults to false) + * @param bool|null $requireHyphen Whether to require a hyphenated ISSN value (defaults to false) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Json.php b/Constraints/Json.php index 6656e4a5b..c97a5ca81 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value has valid JSON syntax. + * * @author Imad ZAIRIG */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -27,6 +29,10 @@ class Json extends Constraint public string $message = 'This value should be valid JSON.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Language.php b/Constraints/Language.php index fe38adcf8..df8ed5a06 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -16,6 +16,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates that a value is a valid language Unicode language identifier. + * + * @see https://unicode.org/reports/tr35/#Unicode_language_identifier + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -30,6 +34,11 @@ class Language extends Constraint public string $message = 'This value is not a valid language.'; public bool $alpha3 = false; + /** + * @param array|null $options + * @param bool|null $alpha3 Pass true to validate the language with three-letter code (ISO 639-2 (2T)) or false with two-letter code (ISO 639-1) (defaults to false) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Length.php b/Constraints/Length.php index 740bfe27a..a19978f7b 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -16,6 +16,8 @@ use Symfony\Component\Validator\Exception\MissingOptionsException; /** + * Validates that a given string length is between some minimum and maximum value. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -56,7 +58,15 @@ class Length extends Constraint public string $countUnit = self::COUNT_CODEPOINTS; /** - * @param self::COUNT_*|null $countUnit + * @param int|array|null $exactly The exact expected length + * @param int|null $min The minimum expected length + * @param int|null $max The maximum expected length + * @param string|null $charset The charset to be used when computing value's length (defaults to UTF-8) + * @param callable|null $normalizer A callable to normalize value before it is validated + * @param self::COUNT_*|null $countUnit The character count unit for the length check (defaults to {@see Length::COUNT_CODEPOINTS}) + * @param string[]|null $groups + * @param array $options + * */ public function __construct( int|array $exactly = null, diff --git a/Constraints/LessThan.php b/Constraints/LessThan.php index da07673f6..d9be15958 100644 --- a/Constraints/LessThan.php +++ b/Constraints/LessThan.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is less than another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/LessThanOrEqual.php b/Constraints/LessThanOrEqual.php index 4d838830f..ddeaf2c38 100644 --- a/Constraints/LessThanOrEqual.php +++ b/Constraints/LessThanOrEqual.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is less than or equal to another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 964c81da7..6ae91122e 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -16,6 +16,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates that a value is a valid locale (e.g. fr, fr_FR, etc.). + * + * @see https://unicode-org.github.io/icu/userguide/locale/ + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -30,6 +34,11 @@ class Locale extends Constraint public string $message = 'This value is not a valid locale.'; public bool $canonicalize = true; + /** + * @param array|null $options + * @param bool|null $canonicalize Whether to canonicalize the value before validation (defaults to true) (see {@see https://www.php.net/manual/en/locale.canonicalize.php}) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index f370067bd..89f5a6356 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -14,7 +14,9 @@ use Symfony\Component\Validator\Constraint; /** - * Metadata for the LuhnValidator. + * Validates that a value (typically a credit card number) passes the Luhn algorithm. + * + * @see https://en.wikipedia.org/wiki/Luhn_algorithm * * @author Tim Nagel * @author Greg Knapp http://gregk.me/2011/php-implementation-of-bank-card-luhn-algorithm/ @@ -33,6 +35,10 @@ class Luhn extends Constraint public string $message = 'Invalid card number.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Negative.php b/Constraints/Negative.php index eeb336ac1..ff52073ea 100644 --- a/Constraints/Negative.php +++ b/Constraints/Negative.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is a negative number. + * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/NegativeOrZero.php b/Constraints/NegativeOrZero.php index 740e582ca..610af4954 100644 --- a/Constraints/NegativeOrZero.php +++ b/Constraints/NegativeOrZero.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is a negative number or equal to zero. + * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/NoSuspiciousCharacters.php b/Constraints/NoSuspiciousCharacters.php index 479a78640..eb775f665 100644 --- a/Constraints/NoSuspiciousCharacters.php +++ b/Constraints/NoSuspiciousCharacters.php @@ -15,6 +15,10 @@ use Symfony\Component\Validator\Exception\LogicException; /** + * Validates that the given string does not contain characters used in spoofing security attacks. + * + * @see https://www.php.net/manual/en/class.spoofchecker.php + * * @author Mathieu Lechat */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -78,8 +82,12 @@ class NoSuspiciousCharacters extends Constraint public ?array $locales = null; /** - * @param int-mask-of|null $checks - * @param self::RESTRICTION_LEVEL_*|null $restrictionLevel + * @param array|null $options + * @param int-mask-of|null $checks A bitmask of the checks to perform on the string (defaults to all checks) + * @param int-mask-of|null $restrictionLevel Configures the set of acceptable characters for the validated string through a specified "level" (defaults to + * {@see NoSuspiciousCharacters::RESTRICTION_LEVEL_MODERATE} on ICU >= 58, {@see NoSuspiciousCharacters::RESTRICTION_LEVEL_SINGLE_SCRIPT} otherwise) + * @param string[]|null $locales Restrict the string's characters to those normally used with these locales. Pass null to use the default locales configured for the NoSuspiciousCharactersValidator. (defaults to null) + * @param string[]|null $groups */ public function __construct( array $options = null, diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index fca14cf78..2fe2d5308 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** + * Validates that a value is not blank. + * * @author Bernhard Schussek * @author Kévin Dunglas */ @@ -32,6 +34,11 @@ class NotBlank extends Constraint /** @var callable|null */ public $normalizer; + /** + * @param array|null $options + * @param bool|null $allowNull Whether to allow null values (defaults to false) + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, bool $allowNull = null, callable $normalizer = null, array $groups = null, mixed $payload = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index 2c3924e87..f1a5590b6 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -31,6 +31,12 @@ class NotCompromisedPassword extends Constraint public int $threshold = 1; public bool $skipOnError = false; + /** + * @param array|null $options + * @param int|null $threshold The number of times the password should have been leaked to consider it is compromised (defaults to 1) + * @param bool|null $skipOnError Whether to ignore HTTP errors while requesting the API and thus consider the password valid (defaults to false) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/NotEqualTo.php b/Constraints/NotEqualTo.php index 65967433f..02d466566 100644 --- a/Constraints/NotEqualTo.php +++ b/Constraints/NotEqualTo.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is not equal to another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/NotIdenticalTo.php b/Constraints/NotIdenticalTo.php index c7500196e..3654bfb03 100644 --- a/Constraints/NotIdenticalTo.php +++ b/Constraints/NotIdenticalTo.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is not identical to another value. + * * @author Daniel Holmes * @author Bernhard Schussek */ diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 14f82be19..cd771a8d2 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is not strictly equal to null. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -27,6 +29,10 @@ class NotNull extends Constraint public string $message = 'This value should not be null.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct(array $options = null, string $message = null, array $groups = null, mixed $payload = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/PasswordStrength.php b/Constraints/PasswordStrength.php index cb5792800..662e89d17 100644 --- a/Constraints/PasswordStrength.php +++ b/Constraints/PasswordStrength.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** + * Validates that the given password has reached a minimum strength. + * * @author Florent Morselli */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -36,6 +38,11 @@ final class PasswordStrength extends Constraint public int $minScore; + /** + * @param array|null $options + * @param self::STRENGTH_*|null $minScore The minimum required strength of the password (defaults to {@see PasswordStrength::STRENGTH_MEDIUM}) + * @param string[]|null $groups + */ public function __construct(array $options = null, int $minScore = null, array $groups = null, mixed $payload = null, string $message = null) { $options['minScore'] ??= self::STRENGTH_MEDIUM; diff --git a/Constraints/Positive.php b/Constraints/Positive.php index 4f5e2da82..9a5aea43f 100644 --- a/Constraints/Positive.php +++ b/Constraints/Positive.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is a positive number. + * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/PositiveOrZero.php b/Constraints/PositiveOrZero.php index 2df48d329..f1f761bcf 100644 --- a/Constraints/PositiveOrZero.php +++ b/Constraints/PositiveOrZero.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Constraints; /** + * Validates that a value is a positive number or equal to zero. + * * @author Jan Schädlich */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] diff --git a/Constraints/Range.php b/Constraints/Range.php index 8d8d1dfa8..24996bf4c 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -18,6 +18,8 @@ use Symfony\Component\Validator\Exception\MissingOptionsException; /** + * Validates that a given number or DateTime object is between some minimum and maximum. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -45,6 +47,16 @@ class Range extends Constraint public mixed $max = null; public ?string $maxPropertyPath = null; + /** + * @param array|null $options + * @param string|null $invalidMessage The message if min and max values are numeric but the given value is not + * @param string|null $invalidDateTimeMessage The message if min and max values are PHP datetimes but the given value is not + * @param int|float|string|null $min The minimum value, either numeric or a datetime string representation + * @param string|null $minPropertyPath Property path to the min value + * @param int|float|string|null $max The maximum value, either numeric or a datetime string representation + * @param string|null $maxPropertyPath Property path to the max value + * @param string[]|null $groups + */ public function __construct( array $options = null, string $notInRangeMessage = null, diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 53a525fa3..3bd6d222f 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** + * Validates that a value matches a regular expression. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -33,6 +35,13 @@ class Regex extends Constraint /** @var callable|null */ public $normalizer; + /** + * @param string|array|null $pattern The regular expression to match + * @param string|null $htmlPattern The pattern to use in the HTML5 pattern attribute + * @param bool|null $match Whether to validate the value matches the configured pattern or not (defaults to false) + * @param string[]|null $groups + * @param array $options + */ public function __construct( string|array|null $pattern, string $message = null, diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index 4a57c98ab..1ddb8b356 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -24,6 +24,10 @@ class Sequentially extends Composite { public array|Constraint $constraints = []; + /** + * @param Constraint[]|array|null $constraints An array of validation constraints + * @param string[]|null $groups + */ public function __construct(mixed $constraints = null, array $groups = null, mixed $payload = null) { parent::__construct($constraints ?? [], $groups, $payload); diff --git a/Constraints/Time.php b/Constraints/Time.php index dc6f5dacc..0e9056ae7 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid time that follows the H:i:s format. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -30,6 +32,11 @@ class Time extends Constraint public bool $withSeconds = true; public string $message = 'This value is not a valid time.'; + /** + * @param array|null $options + * @param string[]|null $groups + * @param bool|null $withSeconds Whether to allow seconds in the given value (defaults to true) + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index 5fc5701cf..8e4927c9d 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** + * Validates that a value is a valid timezone identifier. + * * @author Javier Spagnoletti * @author Hugo Hamon */ @@ -38,6 +40,15 @@ class Timezone extends Constraint self::TIMEZONE_IDENTIFIER_INTL_ERROR => 'TIMEZONE_IDENTIFIER_INTL_ERROR', ]; + /** + * @param int|array|null $zone Restrict valid timezones to this geographical zone (defaults to {@see \DateTimeZone::ALL}) + * @param string|null $countryCode Restrict the valid timezones to this country if the zone option is {@see \DateTimeZone::PER_COUNTRY} + * @param bool|null $intlCompatible Whether to restrict valid timezones to ones available in PHP's intl (defaults to false) + * @param string[]|null $groups + * @param array $options + * + * @see \DateTimeZone + */ public function __construct( int|array $zone = null, string $message = null, diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index cac8f4b54..89c6e124e 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** + * Validates an object that needs to be traversed. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_CLASS)] @@ -22,6 +24,9 @@ class Traverse extends Constraint { public bool $traverse = true; + /** + * @param bool|array|null $traverse Whether to traverse the given object or not (defaults to true). Pass an associative array to configure the constraint's options (e.g. payload). + */ public function __construct(bool|array $traverse = null) { if (\is_array($traverse) && \array_key_exists('groups', $traverse)) { diff --git a/Constraints/Type.php b/Constraints/Type.php index de94a458b..ebedd39fa 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is of a specific data type. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -28,6 +30,11 @@ class Type extends Constraint public string $message = 'This value should be of type {{ type }}.'; public string|array|null $type = null; + /** + * @param string|string[]|array|null $type The type(s) to enforce on the value + * @param string[]|null $groups + * @param array $options + */ public function __construct(string|array|null $type, string $message = null, array $groups = null, mixed $payload = null, array $options = []) { if (\is_array($type) && \is_string(key($type))) { diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index bbee1417b..47bc8d5b7 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -14,6 +14,10 @@ use Symfony\Component\Validator\Constraint; /** + * Validates that a value is a valid Universally Unique Lexicographically Sortable Identifier (ULID). + * + * @see https://github.com/ulid/spec + * * @author Laurent Clouet */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -33,6 +37,10 @@ class Ulid extends Constraint public string $message = 'This is not a valid ULID.'; + /** + * @param array|null $options + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Unique.php b/Constraints/Unique.php index e80c2d3e8..b1d08a10d 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** + * Validates that all the elements of the given collection are unique. + * * @author Yevgeniy Zholkevskiy */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -33,7 +35,9 @@ class Unique extends Constraint public $normalizer; /** - * @param array|string $fields the combination of fields that must contain unique values or a set of options + * @param array|null $options + * @param string[]|null $groups + * @param string[]|string|null $fields Defines the key or keys in the collection that should be checked for uniqueness (defaults to null, which ensure uniqueness for all keys) */ public function __construct( array $options = null, diff --git a/Constraints/Url.php b/Constraints/Url.php index 65cbffc0a..ae67b0335 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** + * Validates that a value is a valid URL string. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -32,6 +34,12 @@ class Url extends Constraint /** @var callable|null */ public $normalizer; + /** + * @param array|null $options + * @param string[]|null $protocols The protocols considered to be valid for the URL (e.g. http, https, ftp, etc.) (defaults to ['http', 'https'] + * @param bool|null $relativeProtocol Whether to accept URL without the protocol (i.e. //example.com) (defaults to false) + * @param string[]|null $groups + */ public function __construct( array $options = null, string $message = null, diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index f497b00dc..6af08b5d8 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -15,6 +15,11 @@ use Symfony\Component\Validator\Exception\InvalidArgumentException; /** + * Validates that a value is a valid Universally unique identifier (UUID). + * + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier + * @see https://datatracker.ietf.org/doc/html/rfc4122 + * * @author Colin O'Dell * @author Bernhard Schussek */ @@ -90,7 +95,10 @@ class Uuid extends Constraint public $normalizer; /** - * @param int[]|int|null $versions + * @param array|null $options + * @param self::V*[]|self::V*|null $versions Specific UUID versions (defaults to {@see Uuid::ALL_VERSIONS}) + * @param bool|null $strict Whether to force the value to follow the RFC's input format rules; pass false to allow alternate formats (defaults to true) + * @param string[]|null $groups */ public function __construct( array $options = null, diff --git a/Constraints/Valid.php b/Constraints/Valid.php index 95422310a..91e59f560 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -14,6 +14,8 @@ use Symfony\Component\Validator\Constraint; /** + * Validates an object embedded in an object's property. + * * @author Bernhard Schussek */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -21,6 +23,11 @@ class Valid extends Constraint { public bool $traverse = true; + /** + * @param array|null $options + * @param string[]|null $groups + * @param bool|null $traverse Whether to validate {@see \Traversable} objects (defaults to true) + */ public function __construct(array $options = null, array $groups = null, $payload = null, bool $traverse = null) { parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/When.php b/Constraints/When.php index 807410d81..e5c5629c2 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -16,6 +16,11 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; +/** + * Conditionally apply validation constraints based on an expression using the ExpressionLanguage syntax. + * + * @see https://symfony.com/doc/current/components/expression_language.html + */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class When extends Composite { @@ -23,6 +28,13 @@ class When extends Composite public array|Constraint $constraints = []; public array $values = []; + /** + * @param string|Expression|array $expression The condition to evaluate, written with the ExpressionLanguage syntax + * @param Constraint[]|Constraint|null $constraints One or multiple constraints that are applied if the expression returns true + * @param array|null $values The values of the custom variables used in the expression (defaults to []) + * @param string[]|null $groups + * @param array $options + */ public function __construct(string|Expression|array $expression, array|Constraint $constraints = null, array $values = null, array $groups = null, $payload = null, array $options = []) { if (!class_exists(ExpressionLanguage::class)) { From 01a4fdf16f0304f1d7a53028bb43b8eb655f80e5 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 6 Jan 2024 13:29:55 +0100 Subject: [PATCH 030/149] use constructor property promotion in Charset and MacAddress constraints --- Constraints/Charset.php | 16 +++++++------- Constraints/CharsetValidator.php | 2 +- Constraints/MacAddress.php | 18 ++++------------ Tests/Constraints/CharsetTest.php | 4 ++-- Tests/Constraints/CharsetValidatorTest.php | 3 ++- Tests/Constraints/MacAddressTest.php | 21 +++---------------- Tests/Constraints/MacAddressValidatorTest.php | 8 ++----- 7 files changed, 21 insertions(+), 51 deletions(-) diff --git a/Constraints/Charset.php b/Constraints/Charset.php index 37346b925..23d493fb6 100644 --- a/Constraints/Charset.php +++ b/Constraints/Charset.php @@ -26,15 +26,13 @@ final class Charset extends Constraint self::BAD_ENCODING_ERROR => 'BAD_ENCODING_ERROR', ]; - public array|string $encodings = []; - public string $message = 'The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.'; - - public function __construct(array|string $encodings = null, string $message = null, array $groups = null, mixed $payload = null, array $options = null) - { - parent::__construct($options, $groups, $payload); - - $this->message = $message ?? $this->message; - $this->encodings = (array) ($encodings ?? $this->encodings); + public function __construct( + public array|string $encodings = [], + public string $message = 'The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.', + array $groups = null, + mixed $payload = null, + ) { + parent::__construct(null, $groups, $payload); if ([] === $this->encodings) { throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires at least one encoding.', static::class)); diff --git a/Constraints/CharsetValidator.php b/Constraints/CharsetValidator.php index fd0adccfe..3e5de79d2 100644 --- a/Constraints/CharsetValidator.php +++ b/Constraints/CharsetValidator.php @@ -35,7 +35,7 @@ public function validate(mixed $value, Constraint $constraint): void throw new UnexpectedValueException($value, 'string'); } - if (!\in_array($detected = mb_detect_encoding($value, $constraint->encodings, true), $constraint->encodings, true)) { + if (!\in_array($detected = mb_detect_encoding($value, $constraint->encodings, true), (array) $constraint->encodings, true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ detected }}', $detected) ->setParameter('{{ encodings }}', implode(', ', $constraint->encodings)) diff --git a/Constraints/MacAddress.php b/Constraints/MacAddress.php index fda431d79..474f1d96d 100644 --- a/Constraints/MacAddress.php +++ b/Constraints/MacAddress.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Exception\InvalidArgumentException; /** * Validates that a value is a valid MAC address. @@ -28,25 +27,16 @@ class MacAddress extends Constraint self::INVALID_MAC_ERROR => 'INVALID_MAC_ERROR', ]; - public string $message = 'This is not a valid MAC address.'; - - /** @var callable|null */ - public $normalizer; + public ?\Closure $normalizer; public function __construct( - array $options = null, - string $message = null, + public string $message = 'This value is not a valid MAC address.', callable $normalizer = null, array $groups = null, mixed $payload = null, ) { - parent::__construct($options, $groups, $payload); - - $this->message = $message ?? $this->message; - $this->normalizer = $normalizer ?? $this->normalizer; + parent::__construct(null, $groups, $payload); - if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); - } + $this->normalizer = null !== $normalizer ? $normalizer(...) : null; } } diff --git a/Tests/Constraints/CharsetTest.php b/Tests/Constraints/CharsetTest.php index 893066645..1b23a2ea7 100644 --- a/Tests/Constraints/CharsetTest.php +++ b/Tests/Constraints/CharsetTest.php @@ -23,7 +23,7 @@ public function testSingleEncodingCanBeSet() { $encoding = new Charset('UTF-8'); - $this->assertSame(['UTF-8'], $encoding->encodings); + $this->assertSame('UTF-8', $encoding->encodings); } public function testMultipleEncodingCanBeSet() @@ -48,7 +48,7 @@ public function testAttributes() $this->assertTrue($loader->loadClassMetadata($metadata)); [$aConstraint] = $metadata->properties['a']->getConstraints(); - $this->assertSame(['UTF-8'], $aConstraint->encodings); + $this->assertSame('UTF-8', $aConstraint->encodings); [$bConstraint] = $metadata->properties['b']->getConstraints(); $this->assertSame(['ASCII', 'UTF-8'], $bConstraint->encodings); diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 4d33080d0..76470b937 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -27,7 +27,7 @@ protected function createValidator(): CharsetValidator /** * @dataProvider provideValidValues */ - public function testEncodingIsValid(string|\Stringable $value, array $encodings) + public function testEncodingIsValid(string|\Stringable $value, array|string $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); @@ -63,6 +63,7 @@ public static function provideValidValues() { yield ['my ascii string', ['ASCII']]; yield ['my ascii string', ['UTF-8']]; + yield ['my ascii string', 'UTF-8']; yield ['my ascii string', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['UTF-8']]; diff --git a/Tests/Constraints/MacAddressTest.php b/Tests/Constraints/MacAddressTest.php index d39180bc0..0669f3ed8 100644 --- a/Tests/Constraints/MacAddressTest.php +++ b/Tests/Constraints/MacAddressTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\MacAddress; -use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; @@ -24,23 +23,9 @@ class MacAddressTest extends TestCase { public function testNormalizerCanBeSet() { - $mac = new MacAddress(['normalizer' => 'trim']); + $mac = new MacAddress(normalizer: 'trim'); - $this->assertEquals('trim', $mac->normalizer); - } - - public function testInvalidNormalizerThrowsException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('The "normalizer" option must be a valid callable ("string" given).'); - new MacAddress(['normalizer' => 'Unknown Callable']); - } - - public function testInvalidNormalizerObjectThrowsException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('The "normalizer" option must be a valid callable ("stdClass" given).'); - new MacAddress(['normalizer' => new \stdClass()]); + $this->assertEquals(trim(...), $mac->normalizer); } public function testAttributes() @@ -51,7 +36,7 @@ public function testAttributes() [$aConstraint] = $metadata->properties['a']->getConstraints(); self::assertSame('myMessage', $aConstraint->message); - self::assertSame('trim', $aConstraint->normalizer); + self::assertEquals(trim(...), $aConstraint->normalizer); self::assertSame(['Default', 'MacAddressDummy'], $aConstraint->groups); [$bConstraint] = $metadata->properties['b']->getConstraints(); diff --git a/Tests/Constraints/MacAddressValidatorTest.php b/Tests/Constraints/MacAddressValidatorTest.php index 0c56bcd23..2552d4bc9 100644 --- a/Tests/Constraints/MacAddressValidatorTest.php +++ b/Tests/Constraints/MacAddressValidatorTest.php @@ -73,9 +73,7 @@ public static function getValidMacs(): array */ public function testValidMacsWithWhitespaces($mac) { - $this->validator->validate($mac, new MacAddress([ - 'normalizer' => 'trim', - ])); + $this->validator->validate($mac, new MacAddress(normalizer: 'trim')); $this->assertNoViolation(); } @@ -97,9 +95,7 @@ public static function getValidMacsWithWhitespaces(): array */ public function testInvalidMacs($mac) { - $constraint = new MacAddress([ - 'message' => 'myMessage', - ]); + $constraint = new MacAddress('myMessage'); $this->validator->validate($mac, $constraint); From b865345f08bee9658277ebf1e65b75a621eae46a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 15 Jan 2024 09:20:33 +0100 Subject: [PATCH 031/149] [Validator] Fix charset encoding detection in `CharsetValidator` --- Constraints/CharsetValidator.php | 4 ++-- Tests/Constraints/CharsetValidatorTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Constraints/CharsetValidator.php b/Constraints/CharsetValidator.php index 3e5de79d2..5cde6ae90 100644 --- a/Constraints/CharsetValidator.php +++ b/Constraints/CharsetValidator.php @@ -35,9 +35,9 @@ public function validate(mixed $value, Constraint $constraint): void throw new UnexpectedValueException($value, 'string'); } - if (!\in_array($detected = mb_detect_encoding($value, $constraint->encodings, true), (array) $constraint->encodings, true)) { + if (!\in_array(mb_detect_encoding($value, $constraint->encodings, true), (array) $constraint->encodings, true)) { $this->context->buildViolation($constraint->message) - ->setParameter('{{ detected }}', $detected) + ->setParameter('{{ detected }}', mb_detect_encoding($value, strict: true)) ->setParameter('{{ encodings }}', implode(', ', $constraint->encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) ->addViolation(); diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 76470b937..88998dcdb 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -42,7 +42,7 @@ public function testInvalidValues(string $value, array $encodings) $this->validator->validate($value, new Charset(encodings: $encodings)); $this->buildViolation('The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.') - ->setParameter('{{ detected }}', mb_detect_encoding($value, $encodings, true)) + ->setParameter('{{ detected }}', 'UTF-8') ->setParameter('{{ encodings }}', implode(', ', $encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) ->assertRaised(); From 59e6cb9cad755c66cd7bab765384bb0de32db375 Mon Sep 17 00:00:00 2001 From: Ninos Date: Mon, 20 Nov 2023 18:15:41 +0100 Subject: [PATCH 032/149] [Validator] Add additional versions (`*_NO_PUBLIC`, `*_ONLY_PRIV` & `*_ONLY_RES`) in IP address & CIDR constraint --- CHANGELOG.md | 2 + Constraints/Cidr.php | 44 +++++++-- Constraints/CidrValidator.php | 28 ++++-- Constraints/Ip.php | 58 ++++++++--- Constraints/IpValidator.php | 58 ++++++++--- Tests/Constraints/CidrTest.php | 10 +- Tests/Constraints/CidrValidatorTest.php | 5 +- Tests/Constraints/IpValidatorTest.php | 123 +++++++++++++++++++++--- 8 files changed, 266 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0a50b42..72b08b710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG * Add support for `Stringable` values when using the `Cidr`, `CssColor`, `ExpressionSyntax` and `PasswordStrength` constraints * Add `MacAddress` constraint + * Add `*_NO_PUBLIC`, `*_ONLY_PRIVATE` and `*_ONLY_RESERVED` versions to `Ip` constraint + * Possibility to use all `Ip` constraint versions for `Cidr` constraint * Add `list` and `associative_array` types to `Type` constraint * Add the `Charset` constraint diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index b274f48b7..98617c557 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\InvalidArgumentException; /** * Validates that a value is a valid CIDR notation. @@ -21,6 +22,7 @@ * * @author Sorin Pop * @author Calin Bolea + * @author Ninos Ego */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Cidr extends Constraint @@ -34,9 +36,33 @@ class Cidr extends Constraint ]; private const NET_MAXES = [ - Ip::ALL => 128, Ip::V4 => 32, Ip::V6 => 128, + Ip::ALL => 128, + + Ip::V4_NO_PUBLIC => 32, + Ip::V6_NO_PUBLIC => 128, + Ip::ALL_NO_PUBLIC => 128, + + Ip::V4_NO_PRIVATE => 32, + Ip::V6_NO_PRIVATE => 128, + Ip::ALL_NO_PRIVATE => 128, + + Ip::V4_NO_RESERVED => 32, + Ip::V6_NO_RESERVED => 128, + Ip::ALL_NO_RESERVED => 128, + + Ip::V4_ONLY_PUBLIC => 32, + Ip::V6_ONLY_PUBLIC => 128, + Ip::ALL_ONLY_PUBLIC => 128, + + Ip::V4_ONLY_PRIVATE => 32, + Ip::V6_ONLY_PRIVATE => 128, + Ip::ALL_ONLY_PRIVATE => 128, + + Ip::V4_ONLY_RESERVED => 32, + Ip::V6_ONLY_RESERVED => 128, + Ip::ALL_ONLY_RESERVED => 128, ]; public string $version = Ip::ALL; @@ -45,13 +71,9 @@ class Cidr extends Constraint public int $netmaskMin = 0; public int $netmaskMax; - /** - * @param array|null $options - * @param string|null $version The CIDR version to validate (4, 6 or all, defaults to all) - * @param int|null $netmaskMin The lowest valid for a valid netmask (defaults to 0) - * @param int|null $netmaskMax The biggest valid for a valid netmask (defaults to 32 for IPv4, 128 for IPv6) - * @param string[]|null $groups - */ + /** @var callable|null */ + public $normalizer; + public function __construct( ?array $options = null, ?string $version = null, @@ -60,6 +82,7 @@ public function __construct( ?string $message = null, ?array $groups = null, $payload = null, + ?callable $normalizer = null, ) { $this->version = $version ?? $options['version'] ?? $this->version; @@ -70,6 +93,7 @@ public function __construct( $this->netmaskMin = $netmaskMin ?? $options['netmaskMin'] ?? $this->netmaskMin; $this->netmaskMax = $netmaskMax ?? $options['netmaskMax'] ?? self::NET_MAXES[$this->version]; $this->message = $message ?? $this->message; + $this->normalizer = $normalizer ?? $this->normalizer; unset($options['netmaskMin'], $options['netmaskMax'], $options['version']); @@ -77,6 +101,10 @@ public function __construct( throw new ConstraintDefinitionException(sprintf('The netmask range must be between 0 and %d.', self::NET_MAXES[$this->version])); } + if (null !== $this->normalizer && !\is_callable($this->normalizer)) { + throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + } + parent::__construct($options, $groups, $payload); } } diff --git a/Constraints/CidrValidator.php b/Constraints/CidrValidator.php index 38168ebb4..82373992f 100644 --- a/Constraints/CidrValidator.php +++ b/Constraints/CidrValidator.php @@ -16,6 +16,13 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; +/** + * Validates whether a value is a CIDR notation. + * + * @author Sorin Pop + * @author Calin Bolea + * @author Ninos Ego + */ class CidrValidator extends ConstraintValidator { public function validate($value, Constraint $constraint): void @@ -28,10 +35,16 @@ public function validate($value, Constraint $constraint): void return; } - if (!\is_string($value) && !$value instanceof \Stringable) { + if (!\is_scalar($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } + $value = (string) $value; + + if (null !== $constraint->normalizer) { + $value = ($constraint->normalizer)($value); + } + $cidrParts = explode('/', $value, 2); if (!isset($cidrParts[1]) @@ -49,14 +62,7 @@ public function validate($value, Constraint $constraint): void $ipAddress = $cidrParts[0]; $netmask = (int) $cidrParts[1]; - $validV4 = Ip::V6 !== $constraint->version - && filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) - && $netmask <= 32; - - $validV6 = Ip::V4 !== $constraint->version - && filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6); - - if (!$validV4 && !$validV6) { + if (!IpValidator::checkIP($ipAddress, $constraint->version)) { $this->context ->buildViolation($constraint->message) ->setCode(Cidr::INVALID_CIDR_ERROR) @@ -65,6 +71,10 @@ public function validate($value, Constraint $constraint): void return; } + if (filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) && $constraint->netmaskMax > 32) { + $constraint->netmaskMax = 32; + } + if ($netmask < $constraint->netmaskMin || $netmask > $constraint->netmaskMax) { $this->context ->buildViolation($constraint->netmaskRangeViolationMessage) diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 7fb5d1d03..4a8b98021 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -20,6 +20,7 @@ * * @author Bernhard Schussek * @author Joseph Bielawski + * @author Ninos Ego */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Ip extends Constraint @@ -28,21 +29,42 @@ class Ip extends Constraint public const V6 = '6'; public const ALL = 'all'; + // adds inverse FILTER_FLAG_NO_RES_RANGE and FILTER_FLAG_NO_PRIV_RANGE flags (skip both) + public const V4_NO_PUBLIC = '4_no_public'; + public const V6_NO_PUBLIC = '6_no_public'; + public const ALL_NO_PUBLIC = 'all_no_public'; + // adds FILTER_FLAG_NO_PRIV_RANGE flag (skip private ranges) - public const V4_NO_PRIV = '4_no_priv'; - public const V6_NO_PRIV = '6_no_priv'; - public const ALL_NO_PRIV = 'all_no_priv'; + public const V4_NO_PRIVATE = '4_no_priv'; + public const V4_NO_PRIV = self::V4_NO_PRIVATE; // BC: Alias + public const V6_NO_PRIVATE = '6_no_priv'; + public const V6_NO_PRIV = self::V6_NO_PRIVATE; // BC: Alias + public const ALL_NO_PRIVATE = 'all_no_priv'; + public const ALL_NO_PRIV = self::ALL_NO_PRIVATE; // BC: Alias // adds FILTER_FLAG_NO_RES_RANGE flag (skip reserved ranges) - public const V4_NO_RES = '4_no_res'; - public const V6_NO_RES = '6_no_res'; - public const ALL_NO_RES = 'all_no_res'; + public const V4_NO_RESERVED = '4_no_res'; + public const V4_NO_RES = self::V4_NO_RESERVED; // BC: Alias + public const V6_NO_RESERVED = '6_no_res'; + public const V6_NO_RES = self::V6_NO_RESERVED; // BC: Alias + public const ALL_NO_RESERVED = 'all_no_res'; + public const ALL_NO_RES = self::ALL_NO_RESERVED; // BC: Alias // adds FILTER_FLAG_NO_PRIV_RANGE and FILTER_FLAG_NO_RES_RANGE flags (skip both) public const V4_ONLY_PUBLIC = '4_public'; public const V6_ONLY_PUBLIC = '6_public'; public const ALL_ONLY_PUBLIC = 'all_public'; + // adds inverse FILTER_FLAG_NO_PRIV_RANGE + public const V4_ONLY_PRIVATE = '4_private'; + public const V6_ONLY_PRIVATE = '6_private'; + public const ALL_ONLY_PRIVATE = 'all_private'; + + // adds inverse FILTER_FLAG_NO_RES_RANGE + public const V4_ONLY_RESERVED = '4_reserved'; + public const V6_ONLY_RESERVED = '6_reserved'; + public const ALL_ONLY_RESERVED = 'all_reserved'; + public const INVALID_IP_ERROR = 'b1b427ae-9f6f-41b0-aa9b-84511fbb3c5b'; protected const VERSIONS = [ @@ -50,17 +72,29 @@ class Ip extends Constraint self::V6, self::ALL, - self::V4_NO_PRIV, - self::V6_NO_PRIV, - self::ALL_NO_PRIV, + self::V4_NO_PUBLIC, + self::V6_NO_PUBLIC, + self::ALL_NO_PUBLIC, - self::V4_NO_RES, - self::V6_NO_RES, - self::ALL_NO_RES, + self::V4_NO_PRIVATE, + self::V6_NO_PRIVATE, + self::ALL_NO_PRIVATE, + + self::V4_NO_RESERVED, + self::V6_NO_RESERVED, + self::ALL_NO_RESERVED, self::V4_ONLY_PUBLIC, self::V6_ONLY_PUBLIC, self::ALL_ONLY_PUBLIC, + + self::V4_ONLY_PRIVATE, + self::V6_ONLY_PRIVATE, + self::ALL_ONLY_PRIVATE, + + self::V4_ONLY_RESERVED, + self::V6_ONLY_RESERVED, + self::ALL_ONLY_RESERVED, ]; protected const ERROR_NAMES = [ diff --git a/Constraints/IpValidator.php b/Constraints/IpValidator.php index 8adba4bc9..e2bf0d29a 100644 --- a/Constraints/IpValidator.php +++ b/Constraints/IpValidator.php @@ -21,9 +21,50 @@ * * @author Bernhard Schussek * @author Joseph Bielawski + * @author Ninos Ego */ class IpValidator extends ConstraintValidator { + /** + * Checks whether an IP address is valid. + * + * @internal + */ + public static function checkIp(string $ip, mixed $version): bool + { + $flag = match ($version) { + Ip::V4, Ip::V4_NO_PUBLIC, Ip::V4_ONLY_PRIVATE, Ip::V4_ONLY_RESERVED => \FILTER_FLAG_IPV4, + Ip::V6, Ip::V6_NO_PUBLIC, Ip::V6_ONLY_PRIVATE, Ip::V6_ONLY_RESERVED => \FILTER_FLAG_IPV6, + Ip::V4_NO_PRIVATE => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_PRIV_RANGE, + Ip::V6_NO_PRIVATE => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE, + Ip::ALL_NO_PRIVATE => \FILTER_FLAG_NO_PRIV_RANGE, + Ip::V4_NO_RESERVED => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_RES_RANGE, + Ip::V6_NO_RESERVED => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_RES_RANGE, + Ip::ALL_NO_RESERVED => \FILTER_FLAG_NO_RES_RANGE, + Ip::V4_ONLY_PUBLIC => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, + Ip::V6_ONLY_PUBLIC => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, + Ip::ALL_ONLY_PUBLIC => \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, + default => 0, + }; + + if (!filter_var($ip, \FILTER_VALIDATE_IP, $flag)) { + return false; + } + + $inverseFlag = match ($version) { + Ip::V4_NO_PUBLIC, Ip::V6_NO_PUBLIC, Ip::ALL_NO_PUBLIC => \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, + Ip::V4_ONLY_PRIVATE, Ip::V6_ONLY_PRIVATE, Ip::ALL_ONLY_PRIVATE => \FILTER_FLAG_NO_PRIV_RANGE, + Ip::V4_ONLY_RESERVED, Ip::V6_ONLY_RESERVED, Ip::ALL_ONLY_RESERVED => \FILTER_FLAG_NO_RES_RANGE, + default => 0, + }; + + if ($inverseFlag && filter_var($ip, \FILTER_VALIDATE_IP, $inverseFlag)) { + return false; + } + + return true; + } + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Ip) { @@ -44,22 +85,7 @@ public function validate(mixed $value, Constraint $constraint): void $value = ($constraint->normalizer)($value); } - $flag = match ($constraint->version) { - Ip::V4 => \FILTER_FLAG_IPV4, - Ip::V6 => \FILTER_FLAG_IPV6, - Ip::V4_NO_PRIV => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_PRIV_RANGE, - Ip::V6_NO_PRIV => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE, - Ip::ALL_NO_PRIV => \FILTER_FLAG_NO_PRIV_RANGE, - Ip::V4_NO_RES => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_RES_RANGE, - Ip::V6_NO_RES => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_RES_RANGE, - Ip::ALL_NO_RES => \FILTER_FLAG_NO_RES_RANGE, - Ip::V4_ONLY_PUBLIC => \FILTER_FLAG_IPV4 | \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, - Ip::V6_ONLY_PUBLIC => \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, - Ip::ALL_ONLY_PUBLIC => \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE, - default => 0, - }; - - if (!filter_var($value, \FILTER_VALIDATE_IP, $flag)) { + if (!self::checkIp($value, $constraint->version)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Ip::INVALID_IP_ERROR) diff --git a/Tests/Constraints/CidrTest.php b/Tests/Constraints/CidrTest.php index c9239de4d..c2536a7b5 100644 --- a/Tests/Constraints/CidrTest.php +++ b/Tests/Constraints/CidrTest.php @@ -49,7 +49,15 @@ public function testForV6() public function testWithInvalidVersion() { - $availableVersions = [Ip::ALL, Ip::V4, Ip::V6]; + $availableVersions = [ + Ip::V4, Ip::V6, Ip::ALL, + Ip::V4_NO_PUBLIC, Ip::V6_NO_PUBLIC, Ip::ALL_NO_PUBLIC, + Ip::V4_NO_PRIVATE, Ip::V6_NO_PRIVATE, Ip::ALL_NO_PRIVATE, + Ip::V4_NO_RESERVED, Ip::V6_NO_RESERVED, Ip::ALL_NO_RESERVED, + Ip::V4_ONLY_PUBLIC, Ip::V6_ONLY_PUBLIC, Ip::ALL_ONLY_PUBLIC, + Ip::V4_ONLY_PRIVATE, Ip::V6_ONLY_PRIVATE, Ip::ALL_ONLY_PRIVATE, + Ip::V4_ONLY_RESERVED, Ip::V6_ONLY_RESERVED, Ip::ALL_ONLY_RESERVED, + ]; self::expectException(ConstraintDefinitionException::class); self::expectExceptionMessage(sprintf('The option "version" must be one of "%s".', implode('", "', $availableVersions))); diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 3c65d9d06..75b2c8ade 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -52,7 +52,7 @@ public function testExpectsStringCompatibleType() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate(123456, new Cidr()); + $this->validator->validate([123456], new Cidr()); } /** @@ -205,7 +205,6 @@ public static function getWithInvalidNetmask(): array return [ ['192.168.1.0/-1'], ['0.0.0.0/foobar'], - ['10.0.0.0/128'], ['123.45.67.178/aaa'], ['172.16.0.0//'], ['255.255.255.255/1/4'], @@ -223,7 +222,6 @@ public static function getWithInvalidMasksAndIps(): array { return [ ['0.0.0.0/foobar'], - ['10.0.0.0/128'], ['123.45.67.178/aaa'], ['172.16.0.0//'], ['172.16.0.0/a/'], @@ -243,6 +241,7 @@ public static function getOutOfRangeNetmask(): array { return [ ['10.0.0.0/24', Ip::V4, 10, 20], + ['10.0.0.0/128'], ['2001:0DB8:85A3:0000:0000:8A2E:0370:7334/24', Ip::V6, 10, 20], ]; } diff --git a/Tests/Constraints/IpValidatorTest.php b/Tests/Constraints/IpValidatorTest.php index 82068184d..a2277a3d8 100644 --- a/Tests/Constraints/IpValidatorTest.php +++ b/Tests/Constraints/IpValidatorTest.php @@ -185,6 +185,33 @@ public function testInvalidIpsV4($ip) ->assertRaised(); } + /** + * @dataProvider getValidPublicIpsV4 + */ + public function testInvalidNoPublicIpsV4($ip) + { + $constraint = new Ip([ + 'version' => Ip::V4_NO_PUBLIC, + 'message' => 'myMessage', + ]); + + $this->validator->validate($ip, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) + ->assertRaised(); + } + + public static function getValidPublicIpsV4() + { + return [ + ['8.0.0.0'], + ['90.0.0.0'], + ['110.0.0.110'], + ]; + } + public static function getInvalidIpsV4() { return [ @@ -201,12 +228,24 @@ public static function getInvalidIpsV4() } /** - * @dataProvider getInvalidPrivateIpsV4 + * @dataProvider getValidPrivateIpsV4 + */ + public function testValidPrivateIpsV4($ip) + { + $this->validator->validate($ip, new Ip([ + 'version' => Ip::V4_ONLY_PRIVATE, + ])); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidPrivateIpsV4 */ public function testInvalidPrivateIpsV4($ip) { $constraint = new Ip([ - 'version' => Ip::V4_NO_PRIV, + 'version' => Ip::V4_NO_PRIVATE, 'message' => 'myMessage', ]); @@ -218,7 +257,25 @@ public function testInvalidPrivateIpsV4($ip) ->assertRaised(); } - public static function getInvalidPrivateIpsV4() + /** + * @dataProvider getInvalidPrivateIpsV4 + */ + public function testInvalidOnlyPrivateIpsV4($ip) + { + $constraint = new Ip([ + 'version' => Ip::V4_ONLY_PRIVATE, + 'message' => 'myMessage', + ]); + + $this->validator->validate($ip, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) + ->assertRaised(); + } + + public static function getValidPrivateIpsV4() { return [ ['10.0.0.0'], @@ -227,13 +284,30 @@ public static function getInvalidPrivateIpsV4() ]; } + public static function getInvalidPrivateIpsV4() + { + return array_merge(self::getValidPublicIpsV4(), self::getValidReservedIpsV4()); + } + /** - * @dataProvider getInvalidReservedIpsV4 + * @dataProvider getValidReservedIpsV4 + */ + public function testValidReservedIpsV4($ip) + { + $this->validator->validate($ip, new Ip([ + 'version' => Ip::V4_ONLY_RESERVED, + ])); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidReservedIpsV4 */ public function testInvalidReservedIpsV4($ip) { $constraint = new Ip([ - 'version' => Ip::V4_NO_RES, + 'version' => Ip::V4_NO_RESERVED, 'message' => 'myMessage', ]); @@ -245,7 +319,25 @@ public function testInvalidReservedIpsV4($ip) ->assertRaised(); } - public static function getInvalidReservedIpsV4() + /** + * @dataProvider getInvalidReservedIpsV4 + */ + public function testInvalidOnlyReservedIpsV4($ip) + { + $constraint = new Ip([ + 'version' => Ip::V4_ONLY_RESERVED, + 'message' => 'myMessage', + ]); + + $this->validator->validate($ip, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) + ->assertRaised(); + } + + public static function getValidReservedIpsV4() { return [ ['0.0.0.0'], @@ -254,6 +346,11 @@ public static function getInvalidReservedIpsV4() ]; } + public static function getInvalidReservedIpsV4() + { + return array_merge(self::getValidPublicIpsV4(), self::getValidPrivateIpsV4()); + } + /** * @dataProvider getInvalidPublicIpsV4 */ @@ -274,7 +371,7 @@ public function testInvalidPublicIpsV4($ip) public static function getInvalidPublicIpsV4() { - return array_merge(self::getInvalidPrivateIpsV4(), self::getInvalidReservedIpsV4()); + return array_merge(self::getValidPrivateIpsV4(), self::getValidReservedIpsV4()); } /** @@ -320,7 +417,7 @@ public static function getInvalidIpsV6() public function testInvalidPrivateIpsV6($ip) { $constraint = new Ip([ - 'version' => Ip::V6_NO_PRIV, + 'version' => Ip::V6_NO_PRIVATE, 'message' => 'myMessage', ]); @@ -347,7 +444,7 @@ public static function getInvalidPrivateIpsV6() public function testInvalidReservedIpsV6($ip) { $constraint = new Ip([ - 'version' => Ip::V6_NO_RES, + 'version' => Ip::V6_NO_RESERVED, 'message' => 'myMessage', ]); @@ -419,7 +516,7 @@ public static function getInvalidIpsAll() public function testInvalidPrivateIpsAll($ip) { $constraint = new Ip([ - 'version' => Ip::ALL_NO_PRIV, + 'version' => Ip::ALL_NO_PRIVATE, 'message' => 'myMessage', ]); @@ -433,7 +530,7 @@ public function testInvalidPrivateIpsAll($ip) public static function getInvalidPrivateIpsAll() { - return array_merge(self::getInvalidPrivateIpsV4(), self::getInvalidPrivateIpsV6()); + return array_merge(self::getValidPrivateIpsV4(), self::getInvalidPrivateIpsV6()); } /** @@ -442,7 +539,7 @@ public static function getInvalidPrivateIpsAll() public function testInvalidReservedIpsAll($ip) { $constraint = new Ip([ - 'version' => Ip::ALL_NO_RES, + 'version' => Ip::ALL_NO_RESERVED, 'message' => 'myMessage', ]); @@ -456,7 +553,7 @@ public function testInvalidReservedIpsAll($ip) public static function getInvalidReservedIpsAll() { - return array_merge(self::getInvalidReservedIpsV4(), self::getInvalidReservedIpsV6()); + return array_merge(self::getValidReservedIpsV4(), self::getInvalidReservedIpsV6()); } /** From d3f5eff439f2d8fd259da71573d0ba16f9574d5e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 7 Feb 2024 08:23:55 +0100 Subject: [PATCH 033/149] revert accepting any scalar value --- Constraints/CidrValidator.php | 2 +- Tests/Constraints/CidrValidatorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Constraints/CidrValidator.php b/Constraints/CidrValidator.php index 82373992f..4fc78a782 100644 --- a/Constraints/CidrValidator.php +++ b/Constraints/CidrValidator.php @@ -35,7 +35,7 @@ public function validate($value, Constraint $constraint): void return; } - if (!\is_scalar($value) && !$value instanceof \Stringable) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 75b2c8ade..9274d81da 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -52,7 +52,7 @@ public function testExpectsStringCompatibleType() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate([123456], new Cidr()); + $this->validator->validate(123456, new Cidr()); } /** From fe1cf67f698333e7db52e5f0c43a247106107f24 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 9 Feb 2024 12:59:26 +0100 Subject: [PATCH 034/149] document deprecation and fix test --- Tests/Constraints/ExpressionSyntaxValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Constraints/ExpressionSyntaxValidatorTest.php b/Tests/Constraints/ExpressionSyntaxValidatorTest.php index d7e62cbd2..65be7fb2a 100644 --- a/Tests/Constraints/ExpressionSyntaxValidatorTest.php +++ b/Tests/Constraints/ExpressionSyntaxValidatorTest.php @@ -62,7 +62,7 @@ public function testExpressionWithoutNames() { $this->validator->validate('1 + 1', new ExpressionSyntax([ 'message' => 'myMessage', - ])); + ], null, null, [])); $this->assertNoViolation(); } From 6a73d479191a0bbbd9ffa3886af6e6ff6e79fb86 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Mon, 26 Feb 2024 12:10:02 +0100 Subject: [PATCH 035/149] [Validator] Simplify `NoSuspiciousCharactersValidator` --- .../NoSuspiciousCharactersValidator.php | 12 ++--- .../NoSuspiciousCharactersValidatorTest.php | 53 ++++++++++++------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/Constraints/NoSuspiciousCharactersValidator.php b/Constraints/NoSuspiciousCharactersValidator.php index d55a765f5..d82a62d57 100644 --- a/Constraints/NoSuspiciousCharactersValidator.php +++ b/Constraints/NoSuspiciousCharactersValidator.php @@ -18,7 +18,7 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; /** - * @author Mathieu Lechat + * @author Mathieu Lechat */ class NoSuspiciousCharactersValidator extends ConstraintValidator { @@ -94,18 +94,12 @@ public function validate(mixed $value, Constraint $constraint): void $checker->setChecks($checks); - if (!$checker->isSuspicious($value)) { + if (!$checker->isSuspicious($value, $errorCode)) { return; } foreach (self::CHECK_ERROR as $check => $error) { - if (!($checks & $check)) { - continue; - } - - $checker->setChecks($check); - - if (!$checker->isSuspicious($value)) { + if (!($errorCode & $check)) { continue; } diff --git a/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php b/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php index 6894d2f95..edacf3312 100644 --- a/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php +++ b/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php @@ -56,14 +56,23 @@ public static function provideNonSuspiciousStrings(): iterable /** * @dataProvider provideSuspiciousStrings */ - public function testSuspiciousStrings(string $string, array $options, string $errorCode, string $errorMessage) + public function testSuspiciousStrings(string $string, array $options, array $errors) { $this->validator->validate($string, new NoSuspiciousCharacters($options)); - $this->buildViolation($errorMessage) - ->setCode($errorCode) + $violations = $this->buildViolation(reset($errors)) + ->setCode(key($errors)) ->setParameter('{{ value }}', '"'.$string.'"') - ->assertRaised(); + ; + + while ($message = next($errors)) { + $violations = $violations->buildNextViolation($message) + ->setCode(key($errors)) + ->setParameter('{{ value }}', '"'.$string.'"') + ; + } + + $violations->assertRaised(); } public static function provideSuspiciousStrings(): iterable @@ -71,8 +80,7 @@ public static function provideSuspiciousStrings(): iterable yield 'Fails RESTRICTION_LEVEL check because of character outside ASCII range' => [ 'à', ['restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_ASCII], - NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR, - 'This value contains characters that are not allowed by the current restriction-level.', + [NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR => 'This value contains characters that are not allowed by the current restriction-level.'], ]; yield 'Fails RESTRICTION_LEVEL check because of mixed-script string' => [ @@ -81,8 +89,7 @@ public static function provideSuspiciousStrings(): iterable 'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_SINGLE_SCRIPT, 'locales' => ['en', 'zh_Hant_TW'], ], - NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR, - 'This value contains characters that are not allowed by the current restriction-level.', + [NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR => 'This value contains characters that are not allowed by the current restriction-level.'], ]; yield 'Fails RESTRICTION_LEVEL check because RESTRICTION_LEVEL_HIGH disallows Armenian script' => [ @@ -91,8 +98,7 @@ public static function provideSuspiciousStrings(): iterable 'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_HIGH, 'locales' => ['en', 'hy_AM'], ], - NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR, - 'This value contains characters that are not allowed by the current restriction-level.', + [NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR => 'This value contains characters that are not allowed by the current restriction-level.'], ]; yield 'Fails RESTRICTION_LEVEL check because RESTRICTION_LEVEL_MODERATE disallows Greek script' => [ @@ -101,8 +107,7 @@ public static function provideSuspiciousStrings(): iterable 'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_MODERATE, 'locales' => ['en', 'el_GR'], ], - NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR, - 'This value contains characters that are not allowed by the current restriction-level.', + [NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR => 'This value contains characters that are not allowed by the current restriction-level.'], ]; yield 'Fails RESTRICTION_LEVEL check because of characters missing from the configured locales’ scripts' => [ @@ -111,8 +116,7 @@ public static function provideSuspiciousStrings(): iterable 'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_MINIMAL, 'locales' => ['en'], ], - NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR, - 'This value contains characters that are not allowed by the current restriction-level.', + [NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR => 'This value contains characters that are not allowed by the current restriction-level.'], ]; yield 'Fails INVISIBLE check because of duplicated non-spacing mark' => [ @@ -120,8 +124,7 @@ public static function provideSuspiciousStrings(): iterable [ 'checks' => NoSuspiciousCharacters::CHECK_INVISIBLE, ], - NoSuspiciousCharacters::INVISIBLE_ERROR, - 'Using invisible characters is not allowed.', + [NoSuspiciousCharacters::INVISIBLE_ERROR => 'Using invisible characters is not allowed.'], ]; yield 'Fails MIXED_NUMBERS check because of different numbering systems' => [ @@ -129,8 +132,7 @@ public static function provideSuspiciousStrings(): iterable [ 'checks' => NoSuspiciousCharacters::CHECK_MIXED_NUMBERS, ], - NoSuspiciousCharacters::MIXED_NUMBERS_ERROR, - 'Mixing numbers from different scripts is not allowed.', + [NoSuspiciousCharacters::MIXED_NUMBERS_ERROR => 'Mixing numbers from different scripts is not allowed.'], ]; yield 'Fails HIDDEN_OVERLAY check because of hidden combining character' => [ @@ -138,8 +140,19 @@ public static function provideSuspiciousStrings(): iterable [ 'checks' => NoSuspiciousCharacters::CHECK_HIDDEN_OVERLAY, ], - NoSuspiciousCharacters::HIDDEN_OVERLAY_ERROR, - 'Using hidden overlay characters is not allowed.', + [NoSuspiciousCharacters::HIDDEN_OVERLAY_ERROR => 'Using hidden overlay characters is not allowed.'], + ]; + + yield 'Fails both HIDDEN_OVERLAY and RESTRICTION_LEVEL checks' => [ + 'i̇', + [ + 'checks' => NoSuspiciousCharacters::CHECK_HIDDEN_OVERLAY, + 'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_ASCII, + ], + [ + NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR => 'This value contains characters that are not allowed by the current restriction-level.', + NoSuspiciousCharacters::HIDDEN_OVERLAY_ERROR => 'Using hidden overlay characters is not allowed.', + ], ]; } From 50b3d816ad832ff4476af470a6927656570d59c3 Mon Sep 17 00:00:00 2001 From: Aurelien Pillevesse Date: Tue, 12 Mar 2024 20:17:37 +0100 Subject: [PATCH 036/149] use cpp --- Command/DebugCommand.php | 9 ++---- ConstraintValidatorFactory.php | 8 ++--- Constraints/Length.php | 1 - ContainerConstraintValidatorFactory.php | 7 ++--- Context/ExecutionContext.php | 24 +++++---------- Context/ExecutionContextFactory.php | 11 +++---- DataCollector/ValidatorDataCollector.php | 8 ++--- Exception/InvalidOptionsException.php | 10 +++---- Exception/MissingOptionsException.php | 10 +++---- Exception/UnexpectedValueException.php | 10 +++---- Exception/ValidationFailedException.php | 11 +++---- .../Factory/LazyLoadingMetadataFactory.php | 11 +++---- Mapping/Loader/FileLoader.php | 9 ++---- Mapping/Loader/LoaderChain.php | 9 ++---- Mapping/Loader/PropertyInfoLoader.php | 17 ++++------- Mapping/Loader/StaticMethodLoader.php | 8 ++--- Validator/LazyProperty.php | 8 ++--- Validator/RecursiveContextualValidator.php | 19 +++++------- Validator/RecursiveValidator.php | 20 +++++-------- Validator/TraceableValidator.php | 7 ++--- Violation/ConstraintViolationBuilder.php | 29 +++++++------------ 21 files changed, 90 insertions(+), 156 deletions(-) diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index 94fc8616f..acff8cbab 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -38,13 +38,10 @@ #[AsCommand(name: 'debug:validator', description: 'Display validation constraints for classes')] class DebugCommand extends Command { - private MetadataFactoryInterface $validator; - - public function __construct(MetadataFactoryInterface $validator) - { + public function __construct( + private MetadataFactoryInterface $validator, + ) { parent::__construct(); - - $this->validator = $validator; } protected function configure(): void diff --git a/ConstraintValidatorFactory.php b/ConstraintValidatorFactory.php index 51ea6e07a..67b254db6 100644 --- a/ConstraintValidatorFactory.php +++ b/ConstraintValidatorFactory.php @@ -24,11 +24,9 @@ */ class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface { - protected array $validators; - - public function __construct(array $validators = []) - { - $this->validators = $validators; + public function __construct( + protected array $validators = [], + ) { } public function getInstance(Constraint $constraint): ConstraintValidatorInterface diff --git a/Constraints/Length.php b/Constraints/Length.php index a80928bb3..ebbe4750f 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -66,7 +66,6 @@ class Length extends Constraint * @param self::COUNT_*|null $countUnit The character count unit for the length check (defaults to {@see Length::COUNT_CODEPOINTS}) * @param string[]|null $groups * @param array $options - * */ public function __construct( int|array|null $exactly = null, diff --git a/ContainerConstraintValidatorFactory.php b/ContainerConstraintValidatorFactory.php index 44d1412c6..b4e5690ee 100644 --- a/ContainerConstraintValidatorFactory.php +++ b/ContainerConstraintValidatorFactory.php @@ -22,12 +22,11 @@ */ class ContainerConstraintValidatorFactory implements ConstraintValidatorFactoryInterface { - private ContainerInterface $container; private array $validators; - public function __construct(ContainerInterface $container) - { - $this->container = $container; + public function __construct( + private ContainerInterface $container, + ) { $this->validators = []; } diff --git a/Context/ExecutionContext.php b/Context/ExecutionContext.php index f21ed90fb..78573181c 100644 --- a/Context/ExecutionContext.php +++ b/Context/ExecutionContext.php @@ -37,16 +37,6 @@ */ class ExecutionContext implements ExecutionContextInterface { - private ValidatorInterface $validator; - - /** - * The root value of the validated object graph. - */ - private mixed $root; - - private TranslatorInterface $translator; - private ?string $translationDomain; - /** * The violations generated in the current context. */ @@ -110,13 +100,15 @@ class ExecutionContext implements ExecutionContextInterface /** * @internal Called by {@link ExecutionContextFactory}. Should not be used in user code. + * + * @param mixed $root the root value of the validated object graph */ - public function __construct(ValidatorInterface $validator, mixed $root, TranslatorInterface $translator, ?string $translationDomain = null) - { - $this->validator = $validator; - $this->root = $root; - $this->translator = $translator; - $this->translationDomain = $translationDomain; + public function __construct( + private ValidatorInterface $validator, + private mixed $root, + private TranslatorInterface $translator, + private ?string $translationDomain = null, + ) { $this->violations = new ConstraintViolationList(); $this->cachedObjectsRefs = new \SplObjectStorage(); } diff --git a/Context/ExecutionContextFactory.php b/Context/ExecutionContextFactory.php index 9979059b6..4a47e87fe 100644 --- a/Context/ExecutionContextFactory.php +++ b/Context/ExecutionContextFactory.php @@ -23,13 +23,10 @@ */ class ExecutionContextFactory implements ExecutionContextFactoryInterface { - private TranslatorInterface $translator; - private ?string $translationDomain; - - public function __construct(TranslatorInterface $translator, ?string $translationDomain = null) - { - $this->translator = $translator; - $this->translationDomain = $translationDomain; + public function __construct( + private TranslatorInterface $translator, + private ?string $translationDomain = null, + ) { } public function createContext(ValidatorInterface $validator, mixed $root): ExecutionContextInterface diff --git a/DataCollector/ValidatorDataCollector.php b/DataCollector/ValidatorDataCollector.php index a50b16687..1b5439783 100644 --- a/DataCollector/ValidatorDataCollector.php +++ b/DataCollector/ValidatorDataCollector.php @@ -29,11 +29,9 @@ */ class ValidatorDataCollector extends DataCollector implements LateDataCollectorInterface { - private TraceableValidator $validator; - - public function __construct(TraceableValidator $validator) - { - $this->validator = $validator; + public function __construct( + private TraceableValidator $validator, + ) { $this->reset(); } diff --git a/Exception/InvalidOptionsException.php b/Exception/InvalidOptionsException.php index 9b724506a..69576760a 100644 --- a/Exception/InvalidOptionsException.php +++ b/Exception/InvalidOptionsException.php @@ -13,13 +13,11 @@ class InvalidOptionsException extends ValidatorException { - private array $options; - - public function __construct(string $message, array $options) - { + public function __construct( + string $message, + private array $options, + ) { parent::__construct($message); - - $this->options = $options; } public function getOptions(): array diff --git a/Exception/MissingOptionsException.php b/Exception/MissingOptionsException.php index 835a60fe0..d40d1c9af 100644 --- a/Exception/MissingOptionsException.php +++ b/Exception/MissingOptionsException.php @@ -13,13 +13,11 @@ class MissingOptionsException extends ValidatorException { - private array $options; - - public function __construct(string $message, array $options) - { + public function __construct( + string $message, + private array $options, + ) { parent::__construct($message); - - $this->options = $options; } public function getOptions(): array diff --git a/Exception/UnexpectedValueException.php b/Exception/UnexpectedValueException.php index 83a7e7a7d..1a65ebd7d 100644 --- a/Exception/UnexpectedValueException.php +++ b/Exception/UnexpectedValueException.php @@ -16,13 +16,11 @@ */ class UnexpectedValueException extends UnexpectedTypeException { - private string $expectedType; - - public function __construct(mixed $value, string $expectedType) - { + public function __construct( + mixed $value, + private string $expectedType, + ) { parent::__construct($value, $expectedType); - - $this->expectedType = $expectedType; } public function getExpectedType(): string diff --git a/Exception/ValidationFailedException.php b/Exception/ValidationFailedException.php index 57edbd9d5..fa8cc7cbf 100644 --- a/Exception/ValidationFailedException.php +++ b/Exception/ValidationFailedException.php @@ -18,13 +18,10 @@ */ class ValidationFailedException extends RuntimeException { - private ConstraintViolationListInterface $violations; - private mixed $value; - - public function __construct(mixed $value, ConstraintViolationListInterface $violations) - { - $this->violations = $violations; - $this->value = $value; + public function __construct( + private mixed $value, + private ConstraintViolationListInterface $violations, + ) { parent::__construct($violations); } diff --git a/Mapping/Factory/LazyLoadingMetadataFactory.php b/Mapping/Factory/LazyLoadingMetadataFactory.php index e8651417c..26cf61f49 100644 --- a/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -39,9 +39,6 @@ */ class LazyLoadingMetadataFactory implements MetadataFactoryInterface { - protected ?LoaderInterface $loader; - protected ?CacheItemPoolInterface $cache; - /** * The loaded metadata, indexed by class name. * @@ -49,10 +46,10 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface */ protected array $loadedClasses = []; - public function __construct(?LoaderInterface $loader = null, ?CacheItemPoolInterface $cache = null) - { - $this->loader = $loader; - $this->cache = $cache; + public function __construct( + protected ?LoaderInterface $loader = null, + protected ?CacheItemPoolInterface $cache = null, + ) { } /** diff --git a/Mapping/Loader/FileLoader.php b/Mapping/Loader/FileLoader.php index 9932ab244..03571fa77 100644 --- a/Mapping/Loader/FileLoader.php +++ b/Mapping/Loader/FileLoader.php @@ -23,8 +23,6 @@ */ abstract class FileLoader extends AbstractLoader { - protected string $file; - /** * Creates a new loader. * @@ -32,8 +30,9 @@ abstract class FileLoader extends AbstractLoader * * @throws MappingException If the file does not exist or is not readable */ - public function __construct(string $file) - { + public function __construct( + protected string $file, + ) { if (!is_file($file)) { throw new MappingException(sprintf('The mapping file "%s" does not exist.', $file)); } @@ -45,7 +44,5 @@ public function __construct(string $file) if (!stream_is_local($this->file)) { throw new MappingException(sprintf('The mapping file "%s" is not a local file.', $file)); } - - $this->file = $file; } } diff --git a/Mapping/Loader/LoaderChain.php b/Mapping/Loader/LoaderChain.php index 7a0cb8c47..ef08ad0c5 100644 --- a/Mapping/Loader/LoaderChain.php +++ b/Mapping/Loader/LoaderChain.php @@ -25,22 +25,19 @@ */ class LoaderChain implements LoaderInterface { - protected array $loaders; - /** * @param LoaderInterface[] $loaders The metadata loaders to use * * @throws MappingException If any of the loaders has an invalid type */ - public function __construct(array $loaders) - { + public function __construct( + protected array $loaders, + ) { foreach ($loaders as $loader) { if (!$loader instanceof LoaderInterface) { throw new MappingException(sprintf('Class "%s" is expected to implement LoaderInterface.', get_debug_type($loader))); } } - - $this->loaders = $loaders; } public function loadClassMetadata(ClassMetadata $metadata): bool diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index db6ac33ca..e1a5d2430 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -31,17 +31,12 @@ final class PropertyInfoLoader implements LoaderInterface { use AutoMappingTrait; - private PropertyListExtractorInterface $listExtractor; - private PropertyTypeExtractorInterface $typeExtractor; - private PropertyAccessExtractorInterface $accessExtractor; - private ?string $classValidatorRegexp; - - public function __construct(PropertyListExtractorInterface $listExtractor, PropertyTypeExtractorInterface $typeExtractor, PropertyAccessExtractorInterface $accessExtractor, ?string $classValidatorRegexp = null) - { - $this->listExtractor = $listExtractor; - $this->typeExtractor = $typeExtractor; - $this->accessExtractor = $accessExtractor; - $this->classValidatorRegexp = $classValidatorRegexp; + public function __construct( + private PropertyListExtractorInterface $listExtractor, + private PropertyTypeExtractorInterface $typeExtractor, + private PropertyAccessExtractorInterface $accessExtractor, + private ?string $classValidatorRegexp = null, + ) { } public function loadClassMetadata(ClassMetadata $metadata): bool diff --git a/Mapping/Loader/StaticMethodLoader.php b/Mapping/Loader/StaticMethodLoader.php index d7548cd94..06d174732 100644 --- a/Mapping/Loader/StaticMethodLoader.php +++ b/Mapping/Loader/StaticMethodLoader.php @@ -21,16 +21,14 @@ */ class StaticMethodLoader implements LoaderInterface { - protected string $methodName; - /** * Creates a new loader. * * @param string $methodName The name of the static method to call */ - public function __construct(string $methodName = 'loadValidatorMetadata') - { - $this->methodName = $methodName; + public function __construct( + protected string $methodName = 'loadValidatorMetadata', + ) { } public function loadClassMetadata(ClassMetadata $metadata): bool diff --git a/Validator/LazyProperty.php b/Validator/LazyProperty.php index aa76934e2..1826cdb4a 100644 --- a/Validator/LazyProperty.php +++ b/Validator/LazyProperty.php @@ -18,11 +18,9 @@ */ class LazyProperty { - private \Closure $propertyValueCallback; - - public function __construct(\Closure $propertyValueCallback) - { - $this->propertyValueCallback = $propertyValueCallback; + public function __construct( + private \Closure $propertyValueCallback, + ) { } public function getPropertyValue(): mixed diff --git a/Validator/RecursiveContextualValidator.php b/Validator/RecursiveContextualValidator.php index ebdae79b6..2949f3ed6 100644 --- a/Validator/RecursiveContextualValidator.php +++ b/Validator/RecursiveContextualValidator.php @@ -45,28 +45,23 @@ */ class RecursiveContextualValidator implements ContextualValidatorInterface { - private ExecutionContextInterface $context; private string $defaultPropertyPath; private array $defaultGroups; - private MetadataFactoryInterface $metadataFactory; - private ConstraintValidatorFactoryInterface $validatorFactory; - private array $objectInitializers; - private ?ContainerInterface $groupProviderLocator; /** * Creates a validator for the given context. * * @param ObjectInitializerInterface[] $objectInitializers The object initializers */ - public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = [], ?ContainerInterface $groupProviderLocator = null) - { - $this->context = $context; + public function __construct( + private ExecutionContextInterface $context, + private MetadataFactoryInterface $metadataFactory, + private ConstraintValidatorFactoryInterface $validatorFactory, + private array $objectInitializers = [], + private ?ContainerInterface $groupProviderLocator = null, + ) { $this->defaultPropertyPath = $context->getPropertyPath(); $this->defaultGroups = [$context->getGroup() ?: Constraint::DEFAULT_GROUP]; - $this->metadataFactory = $metadataFactory; - $this->validatorFactory = $validatorFactory; - $this->objectInitializers = $objectInitializers; - $this->groupProviderLocator = $groupProviderLocator; } public function atPath(string $path): static diff --git a/Validator/RecursiveValidator.php b/Validator/RecursiveValidator.php index 0f39e1984..12bbd539e 100644 --- a/Validator/RecursiveValidator.php +++ b/Validator/RecursiveValidator.php @@ -29,24 +29,18 @@ */ class RecursiveValidator implements ValidatorInterface { - protected ExecutionContextFactoryInterface $contextFactory; - protected MetadataFactoryInterface $metadataFactory; - protected ConstraintValidatorFactoryInterface $validatorFactory; - protected array $objectInitializers; - protected ?ContainerInterface $groupProviderLocator; - /** * Creates a new validator. * * @param ObjectInitializerInterface[] $objectInitializers The object initializers */ - public function __construct(ExecutionContextFactoryInterface $contextFactory, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = [], ?ContainerInterface $groupProviderLocator = null) - { - $this->contextFactory = $contextFactory; - $this->metadataFactory = $metadataFactory; - $this->validatorFactory = $validatorFactory; - $this->objectInitializers = $objectInitializers; - $this->groupProviderLocator = $groupProviderLocator; + public function __construct( + protected ExecutionContextFactoryInterface $contextFactory, + protected MetadataFactoryInterface $metadataFactory, + protected ConstraintValidatorFactoryInterface $validatorFactory, + protected array $objectInitializers = [], + protected ?ContainerInterface $groupProviderLocator = null, + ) { } public function startContext(mixed $root = null): ContextualValidatorInterface diff --git a/Validator/TraceableValidator.php b/Validator/TraceableValidator.php index 5235e3ec4..5442c53da 100644 --- a/Validator/TraceableValidator.php +++ b/Validator/TraceableValidator.php @@ -25,12 +25,11 @@ */ class TraceableValidator implements ValidatorInterface, ResetInterface { - private ValidatorInterface $validator; private array $collectedData = []; - public function __construct(ValidatorInterface $validator) - { - $this->validator = $validator; + public function __construct( + private ValidatorInterface $validator, + ) { } public function getCollectedData(): array diff --git a/Violation/ConstraintViolationBuilder.php b/Violation/ConstraintViolationBuilder.php index e6ce597df..9d39cbcde 100644 --- a/Violation/ConstraintViolationBuilder.php +++ b/Violation/ConstraintViolationBuilder.php @@ -26,30 +26,23 @@ */ class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface { - private ConstraintViolationList $violations; - private string|\Stringable $message; - private array $parameters; - private mixed $root; - private mixed $invalidValue; private string $propertyPath; - private TranslatorInterface $translator; - private string|false|null $translationDomain; private ?int $plural = null; - private ?Constraint $constraint; private ?string $code = null; private mixed $cause = null; - public function __construct(ConstraintViolationList $violations, ?Constraint $constraint, string|\Stringable $message, array $parameters, mixed $root, ?string $propertyPath, mixed $invalidValue, TranslatorInterface $translator, string|false|null $translationDomain = null) - { - $this->violations = $violations; - $this->message = $message; - $this->parameters = $parameters; - $this->root = $root; + public function __construct( + private ConstraintViolationList $violations, + private ?Constraint $constraint, + private string|\Stringable $message, + private array $parameters, + private mixed $root, + ?string $propertyPath, + private mixed $invalidValue, + private TranslatorInterface $translator, + private string|false|null $translationDomain = null, + ) { $this->propertyPath = $propertyPath ?? ''; - $this->invalidValue = $invalidValue; - $this->translator = $translator; - $this->translationDomain = $translationDomain; - $this->constraint = $constraint; } public function atPath(string $path): static From 2227616f65caca75c705a8386f1896d7d5c5807e Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 18 Mar 2024 20:27:13 +0100 Subject: [PATCH 037/149] chore: CS fixes --- Resources/bin/sync-iban-formats.php | 4 ++-- Tests/Constraints/ValidTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/bin/sync-iban-formats.php b/Resources/bin/sync-iban-formats.php index 4042bddf2..cace84ebd 100755 --- a/Resources/bin/sync-iban-formats.php +++ b/Resources/bin/sync-iban-formats.php @@ -11,7 +11,7 @@ */ if ('cli' !== \PHP_SAPI) { - throw new \Exception('This script must be run from the command line.'); + throw new Exception('This script must be run from the command line.'); } /* @@ -24,7 +24,7 @@ error_reporting(\E_ALL); set_error_handler(static function (int $type, string $msg, string $file, int $line): void { - throw new \ErrorException($msg, 0, $type, $file, $line); + throw new ErrorException($msg, 0, $type, $file, $line); }); echo "Collecting IBAN formats...\n"; diff --git a/Tests/Constraints/ValidTest.php b/Tests/Constraints/ValidTest.php index 4c3946f97..c56cdedd5 100644 --- a/Tests/Constraints/ValidTest.php +++ b/Tests/Constraints/ValidTest.php @@ -37,7 +37,7 @@ public function testGroupsAreNullByDefault() public function testAttributes() { - $metadata = new ClassMetaData(ValidDummy::class); + $metadata = new ClassMetadata(ValidDummy::class); $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); From 4eed24ff7a8602d36e257ce2fa5bec2eea79ce63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sun, 31 Mar 2024 15:15:18 +0200 Subject: [PATCH 038/149] Remove unnecessary empty usages --- Constraints/ImageValidator.php | 2 +- Constraints/NotBlankValidator.php | 2 +- Constraints/Regex.php | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Constraints/ImageValidator.php b/Constraints/ImageValidator.php index 436eb212b..eaf13fcbc 100644 --- a/Constraints/ImageValidator.php +++ b/Constraints/ImageValidator.php @@ -52,7 +52,7 @@ public function validate(mixed $value, Constraint $constraint): void $size = @getimagesize($value); - if (empty($size) || (0 === $size[0]) || (0 === $size[1])) { + if (!$size || (0 === $size[0]) || (0 === $size[1])) { $this->context->buildViolation($constraint->sizeNotDetectedMessage) ->setCode(Image::SIZE_NOT_DETECTED_ERROR) ->addViolation(); diff --git a/Constraints/NotBlankValidator.php b/Constraints/NotBlankValidator.php index acc81040c..2b21b3f3c 100644 --- a/Constraints/NotBlankValidator.php +++ b/Constraints/NotBlankValidator.php @@ -35,7 +35,7 @@ public function validate(mixed $value, Constraint $constraint): void $value = ($constraint->normalizer)($value); } - if (false === $value || (empty($value) && '0' != $value)) { + if (false === $value || (!$value && '0' != $value)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(NotBlank::IS_BLANK_ERROR) diff --git a/Constraints/Regex.php b/Constraints/Regex.php index d5342e7cb..1ce86eec3 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -91,9 +91,7 @@ public function getHtmlPattern(): ?string { // If htmlPattern is specified, use it if (null !== $this->htmlPattern) { - return empty($this->htmlPattern) - ? null - : $this->htmlPattern; + return $this->htmlPattern ?: null; } // Quit if delimiters not at very beginning/end (e.g. when options are passed) From f1e74b6436973e37185ddb430734640aefd9ecf6 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Tue, 2 Apr 2024 19:42:38 +0200 Subject: [PATCH 039/149] [Validator] Update internal phpdoc --- Context/ExecutionContext.php | 2 +- Violation/ConstraintViolationBuilder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Context/ExecutionContext.php b/Context/ExecutionContext.php index 12973b17a..2c4315607 100644 --- a/Context/ExecutionContext.php +++ b/Context/ExecutionContext.php @@ -33,7 +33,7 @@ * * @see ExecutionContextInterface * - * @internal since version 2.5. Code against ExecutionContextInterface instead. + * @internal */ class ExecutionContext implements ExecutionContextInterface { diff --git a/Violation/ConstraintViolationBuilder.php b/Violation/ConstraintViolationBuilder.php index 9e198c6da..aa6525438 100644 --- a/Violation/ConstraintViolationBuilder.php +++ b/Violation/ConstraintViolationBuilder.php @@ -22,7 +22,7 @@ * * @author Bernhard Schussek * - * @internal since version 2.5. Code against ConstraintViolationBuilderInterface instead. + * @internal */ class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface { From f93e9b011e72045badf0bf7b45041a7b3fa95a0a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 3 Apr 2024 20:43:45 +0200 Subject: [PATCH 040/149] set the password strength as a violation parameter --- CHANGELOG.md | 1 + Constraints/PasswordStrengthValidator.php | 1 + Tests/Constraints/PasswordStrengthValidatorTest.php | 10 ++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b08b710..9cbc21d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Add the calculated strength to violations in `PasswordStrengthValidator` * Add support for `Stringable` values when using the `Cidr`, `CssColor`, `ExpressionSyntax` and `PasswordStrength` constraints * Add `MacAddress` constraint * Add `*_NO_PUBLIC`, `*_ONLY_PRIVATE` and `*_ONLY_RESERVED` versions to `Ip` constraint diff --git a/Constraints/PasswordStrengthValidator.php b/Constraints/PasswordStrengthValidator.php index 72227b85a..96a4a74f7 100644 --- a/Constraints/PasswordStrengthValidator.php +++ b/Constraints/PasswordStrengthValidator.php @@ -45,6 +45,7 @@ public function validate(#[\SensitiveParameter] mixed $value, Constraint $constr if ($strength < $constraint->minScore) { $this->context->buildViolation($constraint->message) ->setCode(PasswordStrength::PASSWORD_STRENGTH_ERROR) + ->setParameter('{{ strength }}', $strength) ->addViolation(); } } diff --git a/Tests/Constraints/PasswordStrengthValidatorTest.php b/Tests/Constraints/PasswordStrengthValidatorTest.php index e279843f3..78e7951a1 100644 --- a/Tests/Constraints/PasswordStrengthValidatorTest.php +++ b/Tests/Constraints/PasswordStrengthValidatorTest.php @@ -40,6 +40,7 @@ public function testValidValues(string|\Stringable $value, int $expectedStrength $this->buildViolation('The password strength is too low. Please use a stronger password.') ->setCode(PasswordStrength::PASSWORD_STRENGTH_ERROR) + ->setParameter('{{ strength }}', $expectedStrength) ->assertRaised(); } @@ -55,13 +56,15 @@ public static function getValidValues(): iterable /** * @dataProvider provideInvalidConstraints */ - public function testThePasswordIsWeak(PasswordStrength $constraint, string $password, string $expectedMessage, string $expectedCode, array $parameters = []) + public function testThePasswordIsWeak(PasswordStrength $constraint, string $password, string $expectedMessage, string $expectedCode, string $strength) { $this->validator->validate($password, $constraint); $this->buildViolation($expectedMessage) ->setCode($expectedCode) - ->setParameters($parameters) + ->setParameters([ + '{{ strength }}' => $strength, + ]) ->assertRaised(); } @@ -72,18 +75,21 @@ public static function provideInvalidConstraints(): iterable 'password', 'The password strength is too low. Please use a stronger password.', PasswordStrength::PASSWORD_STRENGTH_ERROR, + '0', ]; yield [ new PasswordStrength(minScore: PasswordStrength::STRENGTH_VERY_STRONG), 'Good password?', 'The password strength is too low. Please use a stronger password.', PasswordStrength::PASSWORD_STRENGTH_ERROR, + '1', ]; yield [ new PasswordStrength(message: 'This password should be strong.'), 'password', 'This password should be strong.', PasswordStrength::PASSWORD_STRENGTH_ERROR, + '0', ]; } } From be567a340d9a295558fc2d39dcc9879f300e0ed7 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 26 Oct 2023 10:56:04 +0200 Subject: [PATCH 041/149] [PropertyInfo] Deprecate PropertyInfo Type Co-authored-by: Baptiste Leduc --- Mapping/Loader/PropertyInfoLoader.php | 165 +++++++++++++++--- .../Mapping/Loader/PropertyInfoLoaderTest.php | 150 +++++++++++----- composer.json | 1 + 3 files changed, 245 insertions(+), 71 deletions(-) diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index e1a5d2430..f878974ec 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -15,6 +15,12 @@ use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type as PropertyInfoType; +use Symfony\Component\TypeInfo\Type as TypeInfoType; +use Symfony\Component\TypeInfo\Type\CollectionType; +use Symfony\Component\TypeInfo\Type\IntersectionType; +use Symfony\Component\TypeInfo\Type\ObjectType; +use Symfony\Component\TypeInfo\Type\UnionType; +use Symfony\Component\TypeInfo\TypeIdentifier; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; @@ -57,7 +63,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool continue; } - $types = $this->typeExtractor->getTypes($className, $property); + $types = $this->getPropertyTypes($className, $property); if (null === $types) { continue; } @@ -95,42 +101,92 @@ public function loadClassMetadata(ClassMetadata $metadata): bool } $loaded = true; - $builtinTypes = []; - $nullable = false; - $scalar = true; - foreach ($types as $type) { - $builtinTypes[] = $type->getBuiltinType(); - - if ($scalar && !\in_array($type->getBuiltinType(), [PropertyInfoType::BUILTIN_TYPE_INT, PropertyInfoType::BUILTIN_TYPE_FLOAT, PropertyInfoType::BUILTIN_TYPE_STRING, PropertyInfoType::BUILTIN_TYPE_BOOL], true)) { - $scalar = false; + + // BC layer for PropertyTypeExtractorInterface::getTypes(). + // Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). + if (\is_array($types)) { + $builtinTypes = []; + $nullable = false; + $scalar = true; + + foreach ($types as $type) { + $builtinTypes[] = $type->getBuiltinType(); + + if ($scalar && !\in_array($type->getBuiltinType(), ['int', 'float', 'string', 'bool'], true)) { + $scalar = false; + } + + if (!$nullable && $type->isNullable()) { + $nullable = true; + } + } + + if (!$hasTypeConstraint) { + if (1 === \count($builtinTypes)) { + if ($types[0]->isCollection() && \count($collectionValueType = $types[0]->getCollectionValueTypes()) > 0) { + [$collectionValueType] = $collectionValueType; + $this->handleAllConstraintLegacy($property, $allConstraint, $collectionValueType, $metadata); + } + + $metadata->addPropertyConstraint($property, $this->getTypeConstraintLegacy($builtinTypes[0], $types[0])); + } elseif ($scalar) { + $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar'])); + } + } + + if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) { + $metadata->addPropertyConstraint($property, new NotNull()); } + } else { + if ($hasTypeConstraint) { + continue; + } + + $type = $types; + $nullable = false; - if (!$nullable && $type->isNullable()) { + if ($type instanceof UnionType && $type->isNullable()) { $nullable = true; + $type = $type->asNonNullable(); } - } - if (!$hasTypeConstraint) { - if (1 === \count($builtinTypes)) { - if ($types[0]->isCollection() && \count($collectionValueType = $types[0]->getCollectionValueTypes()) > 0) { - [$collectionValueType] = $collectionValueType; - $this->handleAllConstraint($property, $allConstraint, $collectionValueType, $metadata); - } - $metadata->addPropertyConstraint($property, $this->getTypeConstraint($builtinTypes[0], $types[0])); - } elseif ($scalar) { - $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar'])); + if ($type instanceof CollectionType) { + $this->handleAllConstraint($property, $allConstraint, $type->getCollectionValueType(), $metadata); + } + + if (null !== $typeConstraint = $this->getTypeConstraint($type)) { + $metadata->addPropertyConstraint($property, $typeConstraint); } - } - if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) { - $metadata->addPropertyConstraint($property, new NotNull()); + if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) { + $metadata->addPropertyConstraint($property, new NotNull()); + } } } return $loaded; } - private function getTypeConstraint(string $builtinType, PropertyInfoType $type): Type + /** + * BC layer for PropertyTypeExtractorInterface::getTypes(). + * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). + * + * @return TypeInfoType|list|null + */ + private function getPropertyTypes(string $className, string $property): TypeInfoType|array|null + { + if (method_exists($this->typeExtractor, 'getType')) { + return $this->typeExtractor->getType($className, $property); + } + + return $this->typeExtractor->getTypes($className, $property); + } + + /** + * BC layer for PropertyTypeExtractorInterface::getTypes(). + * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). + */ + private function getTypeConstraintLegacy(string $builtinType, PropertyInfoType $type): Type { if (PropertyInfoType::BUILTIN_TYPE_OBJECT === $builtinType && null !== $className = $type->getClassName()) { return new Type(['type' => $className]); @@ -139,7 +195,64 @@ private function getTypeConstraint(string $builtinType, PropertyInfoType $type): return new Type(['type' => $builtinType]); } - private function handleAllConstraint(string $property, ?All $allConstraint, PropertyInfoType $propertyInfoType, ClassMetadata $metadata): void + private function getTypeConstraint(TypeInfoType $type): ?Type + { + if ($type instanceof UnionType || $type instanceof IntersectionType) { + return ($type->isA(TypeIdentifier::INT) || $type->isA(TypeIdentifier::FLOAT) || $type->isA(TypeIdentifier::STRING) || $type->isA(TypeIdentifier::BOOL)) ? new Type(['type' => 'scalar']) : null; + } + + $baseType = $type->getBaseType(); + + if ($baseType instanceof ObjectType) { + return new Type(['type' => $baseType->getClassName()]); + } + + if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) { + return new Type(['type' => $baseType->getTypeIdentifier()->value]); + } + + return null; + } + + private function handleAllConstraint(string $property, ?All $allConstraint, TypeInfoType $type, ClassMetadata $metadata): void + { + $containsTypeConstraint = false; + $containsNotNullConstraint = false; + if (null !== $allConstraint) { + foreach ($allConstraint->constraints as $constraint) { + if ($constraint instanceof Type) { + $containsTypeConstraint = true; + } elseif ($constraint instanceof NotNull) { + $containsNotNullConstraint = true; + } + } + } + + $constraints = []; + if (!$containsNotNullConstraint && !$type->isNullable()) { + $constraints[] = new NotNull(); + } + + if (!$containsTypeConstraint && null !== $typeConstraint = $this->getTypeConstraint($type)) { + $constraints[] = $typeConstraint; + } + + if (!$constraints) { + return; + } + + if (null === $allConstraint) { + $metadata->addPropertyConstraint($property, new All(['constraints' => $constraints])); + } else { + $allConstraint->constraints = array_merge($allConstraint->constraints, $constraints); + } + } + + /** + * BC layer for PropertyTypeExtractorInterface::getTypes(). + * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0). + */ + private function handleAllConstraintLegacy(string $property, ?All $allConstraint, PropertyInfoType $propertyInfoType, ClassMetadata $metadata): void { $containsTypeConstraint = false; $containsNotNullConstraint = false; @@ -159,7 +272,7 @@ private function handleAllConstraint(string $property, ?All $allConstraint, Prop } if (!$containsTypeConstraint) { - $constraints[] = $this->getTypeConstraint($propertyInfoType->getBuiltinType(), $propertyInfoType); + $constraints[] = $this->getTypeConstraintLegacy($propertyInfoType->getBuiltinType(), $propertyInfoType); } if (null === $allConstraint) { diff --git a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php index d07994c15..c4dabc893 100644 --- a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php +++ b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php @@ -12,8 +12,11 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; use PHPUnit\Framework\TestCase; -use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; -use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\PropertyInfo\Type as LegacyType; +use Symfony\Component\TypeInfo\Type; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Iban; use Symfony\Component\Validator\Constraints\NotBlank; @@ -35,8 +38,8 @@ class PropertyInfoLoaderTest extends TestCase { public function testLoadClassMetadata() { - $propertyInfoStub = $this->createMock(PropertyInfoExtractorInterface::class); - $propertyInfoStub + $propertyListExtractor = $this->createMock(PropertyListExtractorInterface::class); + $propertyListExtractor ->method('getProperties') ->willReturn([ 'nullableString', @@ -54,24 +57,62 @@ public function testLoadClassMetadata() 'noAutoMapping', ]) ; - $propertyInfoStub - ->method('getTypes') - ->will($this->onConsecutiveCalls( - [new Type(Type::BUILTIN_TYPE_STRING, true)], - [new Type(Type::BUILTIN_TYPE_STRING)], - [new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_BOOL)], - [new Type(Type::BUILTIN_TYPE_OBJECT, true, Entity::class)], - [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, Entity::class))], - [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)], - [new Type(Type::BUILTIN_TYPE_FLOAT, true)], // The existing constraint is float - [new Type(Type::BUILTIN_TYPE_STRING, true)], - [new Type(Type::BUILTIN_TYPE_STRING, true)], - [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, null, new Type(Type::BUILTIN_TYPE_FLOAT))], - [new Type(Type::BUILTIN_TYPE_STRING)], - [new Type(Type::BUILTIN_TYPE_STRING)] - )) - ; - $propertyInfoStub + + $propertyTypeExtractor = new class() implements PropertyTypeExtractorInterface { + private int $i = 0; + private int $j = 0; + private array $types; + private array $legacyTypes; + + public function getType(string $class, string $property, array $context = []): ?Type + { + $this->types ??= [ + Type::nullable(Type::string()), + Type::string(), + Type::union(Type::string(), Type::int(), Type::bool(), Type::null()), + Type::nullable(Type::object(Entity::class)), + Type::nullable(Type::array(Type::object(Entity::class))), + Type::nullable(Type::array()), + Type::nullable(Type::float()), // The existing constraint is float + Type::nullable(Type::string()), + Type::nullable(Type::string()), + Type::nullable(Type::array(Type::float())), + Type::string(), + Type::string(), + ]; + + $type = $this->types[$this->i]; + ++$this->i; + + return $type; + } + + public function getTypes(string $class, string $property, array $context = []): ?array + { + $this->legacyTypes ??= [ + [new LegacyType('string', true)], + [new LegacyType('string')], + [new LegacyType('string', true), new LegacyType('int'), new LegacyType('bool')], + [new LegacyType('object', true, Entity::class)], + [new LegacyType('array', true, null, true, null, new LegacyType('object', false, Entity::class))], + [new LegacyType('array', true, null, true)], + [new LegacyType('float', true)], // The existing constraint is float + [new LegacyType('string', true)], + [new LegacyType('string', true)], + [new LegacyType('array', true, null, true, null, new LegacyType('float'))], + [new LegacyType('string')], + [new LegacyType('string')], + ]; + + $legacyType = $this->legacyTypes[$this->j]; + ++$this->j; + + return $legacyType; + } + }; + + $propertyAccessExtractor = $this->createMock(PropertyAccessExtractorInterface::class); + $propertyAccessExtractor ->method('isWritable') ->will($this->onConsecutiveCalls( true, @@ -89,7 +130,7 @@ public function testLoadClassMetadata() )) ; - $propertyInfoLoader = new PropertyInfoLoader($propertyInfoStub, $propertyInfoStub, $propertyInfoStub, '{.*}'); + $propertyInfoLoader = new PropertyInfoLoader($propertyListExtractor, $propertyTypeExtractor, $propertyAccessExtractor, '{.*}'); $validator = Validation::createValidatorBuilder() ->enableAttributeMapping() @@ -170,7 +211,6 @@ public function testLoadClassMetadata() $this->assertInstanceOf(TypeConstraint::class, $alreadyPartiallyMappedCollectionConstraints[0]->constraints[0]); $this->assertSame('string', $alreadyPartiallyMappedCollectionConstraints[0]->constraints[0]->type); $this->assertInstanceOf(Iban::class, $alreadyPartiallyMappedCollectionConstraints[0]->constraints[1]); - $this->assertInstanceOf(NotNull::class, $alreadyPartiallyMappedCollectionConstraints[0]->constraints[2]); $readOnlyMetadata = $classMetadata->getPropertyMetadata('readOnly'); $this->assertEmpty($readOnlyMetadata); @@ -188,17 +228,27 @@ public function testLoadClassMetadata() */ public function testClassValidator(bool $expected, ?string $classValidatorRegexp = null) { - $propertyInfoStub = $this->createMock(PropertyInfoExtractorInterface::class); - $propertyInfoStub + $propertyListExtractor = $this->createMock(PropertyListExtractorInterface::class); + $propertyListExtractor ->method('getProperties') ->willReturn(['string']) ; - $propertyInfoStub - ->method('getTypes') - ->willReturn([new Type(Type::BUILTIN_TYPE_STRING)]) - ; - $propertyInfoLoader = new PropertyInfoLoader($propertyInfoStub, $propertyInfoStub, $propertyInfoStub, $classValidatorRegexp); + $propertyTypeExtractor = new class() implements PropertyTypeExtractorInterface { + public function getType(string $class, string $property, array $context = []): ?Type + { + return Type::string(); + } + + public function getTypes(string $class, string $property, array $context = []): ?array + { + return [new LegacyType('string')]; + } + }; + + $propertyAccessExtractor = $this->createMock(PropertyAccessExtractorInterface::class); + + $propertyInfoLoader = new PropertyInfoLoader($propertyListExtractor, $propertyTypeExtractor, $propertyAccessExtractor, $classValidatorRegexp); $classMetadata = new ClassMetadata(PropertyInfoLoaderEntity::class); $this->assertSame($expected, $propertyInfoLoader->loadClassMetadata($classMetadata)); @@ -214,21 +264,31 @@ public static function regexpProvider(): array ]; } - public function testClassNoAutoMapping() + public function testClassNoAutoMapping(?PropertyTypeExtractorInterface $propertyListExtractor = null) { - $propertyInfoStub = $this->createMock(PropertyInfoExtractorInterface::class); - $propertyInfoStub - ->method('getProperties') - ->willReturn(['string', 'autoMappingExplicitlyEnabled']) - ; - $propertyInfoStub - ->method('getTypes') - ->willReturnOnConsecutiveCalls( - [new Type(Type::BUILTIN_TYPE_STRING)], - [new Type(Type::BUILTIN_TYPE_BOOL)] - ); - - $propertyInfoLoader = new PropertyInfoLoader($propertyInfoStub, $propertyInfoStub, $propertyInfoStub, '{.*}'); + if (null === $propertyListExtractor) { + $propertyListExtractor = $this->createMock(PropertyListExtractorInterface::class); + $propertyListExtractor + ->method('getProperties') + ->willReturn(['string', 'autoMappingExplicitlyEnabled']) + ; + + $propertyTypeExtractor = new class() implements PropertyTypeExtractorInterface { + public function getType(string $class, string $property, array $context = []): ?Type + { + return Type::string(); + } + + public function getTypes(string $class, string $property, array $context = []): ?array + { + return [new LegacyType('string')]; + } + }; + } + + $propertyAccessExtractor = $this->createMock(PropertyAccessExtractorInterface::class); + + $propertyInfoLoader = new PropertyInfoLoader($propertyListExtractor, $propertyTypeExtractor, $propertyAccessExtractor, '{.*}'); $validator = Validation::createValidatorBuilder() ->enableAttributeMapping() ->addLoader($propertyInfoLoader) diff --git a/composer.json b/composer.json index 4f55dd2d1..31d88f870 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation": "^6.4.3|^7.0.3", + "symfony/type-info": "^7.1", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { From c0271c897ef98c5672d5d1810428d6b50b471783 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 26 Mar 2024 17:01:05 +0100 Subject: [PATCH 042/149] [Validator] Add a `requireTld` option to `Url` constraint --- CHANGELOG.md | 1 + Constraints/Url.php | 7 +++++ Constraints/UrlValidator.php | 13 +++++++++ Tests/Constraints/UrlTest.php | 12 ++++++++ Tests/Constraints/UrlValidatorTest.php | 39 ++++++++++++++++++++++++++ 5 files changed, 72 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b08b710..49544a346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Possibility to use all `Ip` constraint versions for `Cidr` constraint * Add `list` and `associative_array` types to `Type` constraint * Add the `Charset` constraint + * Add the `requireTld` option to the `Url` constraint 7.0 --- diff --git a/Constraints/Url.php b/Constraints/Url.php index 17d72d523..b16496b38 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -23,14 +23,18 @@ class Url extends Constraint { public const INVALID_URL_ERROR = '57c2f299-1154-4870-89bb-ef3b1f5ad229'; + public const MISSING_TLD_ERROR = '8a5d387f-0716-46b4-844b-67367faf435a'; protected const ERROR_NAMES = [ self::INVALID_URL_ERROR => 'INVALID_URL_ERROR', + self::MISSING_TLD_ERROR => 'MISSING_TLD_ERROR', ]; public string $message = 'This value is not a valid URL.'; + public string $tldMessage = 'This URL does not contain a TLD.'; public array $protocols = ['http', 'https']; public bool $relativeProtocol = false; + public bool $requireTld = false; /** @var callable|null */ public $normalizer; @@ -39,6 +43,7 @@ class Url extends Constraint * @param string[]|null $protocols The protocols considered to be valid for the URL (e.g. http, https, ftp, etc.) (defaults to ['http', 'https'] * @param bool|null $relativeProtocol Whether to accept URL without the protocol (i.e. //example.com) (defaults to false) * @param string[]|null $groups + * @param bool|null $requireTld Whether to require the URL to include a top-level domain (defaults to false) */ public function __construct( ?array $options = null, @@ -48,6 +53,7 @@ public function __construct( ?callable $normalizer = null, ?array $groups = null, mixed $payload = null, + ?bool $requireTld = null, ) { parent::__construct($options, $groups, $payload); @@ -55,6 +61,7 @@ public function __construct( $this->protocols = $protocols ?? $this->protocols; $this->relativeProtocol = $relativeProtocol ?? $this->relativeProtocol; $this->normalizer = $normalizer ?? $this->normalizer; + $this->requireTld = $requireTld ?? $this->requireTld; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); diff --git a/Constraints/UrlValidator.php b/Constraints/UrlValidator.php index a2d08e544..fb342a6fa 100644 --- a/Constraints/UrlValidator.php +++ b/Constraints/UrlValidator.php @@ -79,5 +79,18 @@ public function validate(mixed $value, Constraint $constraint): void return; } + + if ($constraint->requireTld) { + $urlHost = parse_url(https://github.com/$value,%20/PHP_URL_HOST); + // the host of URLs with a TLD must include at least a '.' (but it can't be an IP address like '127.0.0.1') + if (!str_contains($urlHost, '.') || filter_var($urlHost, \FILTER_VALIDATE_IP)) { + $this->context->buildViolation($constraint->tldMessage) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Url::MISSING_TLD_ERROR) + ->addViolation(); + + return; + } + } } } diff --git a/Tests/Constraints/UrlTest.php b/Tests/Constraints/UrlTest.php index a7b053904..edfb7cb78 100644 --- a/Tests/Constraints/UrlTest.php +++ b/Tests/Constraints/UrlTest.php @@ -52,17 +52,26 @@ public function testAttributes() self::assertSame(['http', 'https'], $aConstraint->protocols); self::assertFalse($aConstraint->relativeProtocol); self::assertNull($aConstraint->normalizer); + self::assertFalse($aConstraint->requireTld); [$bConstraint] = $metadata->properties['b']->getConstraints(); self::assertSame(['ftp', 'gopher'], $bConstraint->protocols); self::assertSame('trim', $bConstraint->normalizer); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'UrlDummy'], $bConstraint->groups); + self::assertFalse($bConstraint->requireTld); [$cConstraint] = $metadata->properties['c']->getConstraints(); self::assertTrue($cConstraint->relativeProtocol); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); + self::assertFalse($cConstraint->requireTld); + + [$dConstraint] = $metadata->properties['d']->getConstraints(); + self::assertSame(['http', 'https'], $aConstraint->protocols); + self::assertFalse($aConstraint->relativeProtocol); + self::assertNull($aConstraint->normalizer); + self::assertTrue($dConstraint->requireTld); } } @@ -76,4 +85,7 @@ class UrlDummy #[Url(relativeProtocol: true, groups: ['my_group'], payload: 'some attached data')] private $c; + + #[Url(requireTld: true)] + private $d; } diff --git a/Tests/Constraints/UrlValidatorTest.php b/Tests/Constraints/UrlValidatorTest.php index 18d6e9a49..58111a345 100644 --- a/Tests/Constraints/UrlValidatorTest.php +++ b/Tests/Constraints/UrlValidatorTest.php @@ -311,6 +311,45 @@ public static function getValidCustomUrls() ['git://[::1]/'], ]; } + + /** + * @dataProvider getUrlsForRequiredTld + */ + public function testRequiredTld(string $url, bool $requireTld, bool $isValid) + { + $constraint = new Url([ + 'requireTld' => $requireTld, + ]); + + $this->validator->validate($url, $constraint); + + if ($isValid) { + $this->assertNoViolation(); + } else { + $this->buildViolation($constraint->tldMessage) + ->setParameter('{{ value }}', '"'.$url.'"') + ->setCode(Url::MISSING_TLD_ERROR) + ->assertRaised(); + } + } + + public static function getUrlsForRequiredTld(): iterable + { + yield ['https://aaa/', true, false]; + yield ['https://aaa/', false, true]; + yield ['https://localhost/', true, false]; + yield ['https://localhost/', false, true]; + yield ['http://127.0.0.1/', false, true]; + yield ['http://127.0.0.1/', true, false]; + yield ['http://user.pass@local/', false, true]; + yield ['http://user.pass@local/', true, false]; + yield ['https://example.com/', true, true]; + yield ['https://example.com/', false, true]; + yield ['http://foo/bar.png', false, true]; + yield ['http://foo/bar.png', true, false]; + yield ['https://example.com.org/', true, true]; + yield ['https://example.com.org/', false, true]; + } } class EmailProvider From 252517977f1d15e79c4933dc2deb496ba165232b Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Tue, 9 Apr 2024 13:55:09 +0200 Subject: [PATCH 043/149] [Validator] Deprecate `Bic::INVALID_BANK_CODE_ERROR` --- CHANGELOG.md | 1 + Constraints/Bic.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6d7469ff..67f91ec0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG * Add `list` and `associative_array` types to `Type` constraint * Add the `Charset` constraint * Add the `requireTld` option to the `Url` constraint + * Deprecate `Bic::INVALID_BANK_CODE_ERROR` 7.0 --- diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 37e0bd503..625fb7213 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -29,6 +29,9 @@ class Bic extends Constraint { public const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c'; public const INVALID_CHARACTERS_ERROR = 'f424c529-7add-4417-8f2d-4b656e4833e2'; + /** + * @deprecated since Symfony 7.1, to be removed in 8.0 + */ public const INVALID_BANK_CODE_ERROR = '00559357-6170-4f29-aebd-d19330aa19cf'; public const INVALID_COUNTRY_CODE_ERROR = '1ce76f8d-3c1f-451c-9e62-fe9c3ed486ae'; public const INVALID_CASE_ERROR = '11884038-3312-4ae5-9d04-699f782130c7'; From 9c5521801090c46478de7794f2e02199c94c4e82 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 12 Apr 2024 09:29:55 +0200 Subject: [PATCH 044/149] [Validator] Document `Compound::getConstraints` options --- Constraint.php | 3 +++ Constraints/Compound.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Constraint.php b/Constraint.php index 42cc52da2..7b65406d2 100644 --- a/Constraint.php +++ b/Constraint.php @@ -121,6 +121,9 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed } } + /** + * @return array + */ protected function normalizeOptions(mixed $options): array { $normalizedOptions = []; diff --git a/Constraints/Compound.php b/Constraints/Compound.php index f6e875c01..4e7f7cd60 100644 --- a/Constraints/Compound.php +++ b/Constraints/Compound.php @@ -46,6 +46,8 @@ final public function validatedBy(): string } /** + * @param array $options + * * @return Constraint[] */ abstract protected function getConstraints(array $options): array; From 3d2255a755d006c91bceb3caf8bbe0b4c3c6afac Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 13 Apr 2024 09:22:51 +0200 Subject: [PATCH 045/149] prepare for changing the default value of the requireTld option --- CHANGELOG.md | 1 + Constraints/Url.php | 4 ++++ Tests/Constraints/UrlTest.php | 22 +++++++++++++++------ Tests/Constraints/UrlValidatorTest.php | 27 ++++++++++++++++---------- composer.json | 1 + 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67f91ec0f..006040582 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Deprecate not passing a value for the `requireTld` option to the `Url` constraint (the default value will become `true` in 8.0) * Add the calculated strength to violations in `PasswordStrengthValidator` * Add support for `Stringable` values when using the `Cidr`, `CssColor`, `ExpressionSyntax` and `PasswordStrength` constraints * Add `MacAddress` constraint diff --git a/Constraints/Url.php b/Constraints/Url.php index b16496b38..435f744bd 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -57,6 +57,10 @@ public function __construct( ) { parent::__construct($options, $groups, $payload); + if (null === ($options['requireTld'] ?? $requireTld)) { + trigger_deprecation('symfony/validator', '7.1', 'Not passing a value for the "requireTld" option to the Url constraint is deprecated. Its default value will change to "true".'); + } + $this->message = $message ?? $this->message; $this->protocols = $protocols ?? $this->protocols; $this->relativeProtocol = $relativeProtocol ?? $this->relativeProtocol; diff --git a/Tests/Constraints/UrlTest.php b/Tests/Constraints/UrlTest.php index edfb7cb78..1d641aa92 100644 --- a/Tests/Constraints/UrlTest.php +++ b/Tests/Constraints/UrlTest.php @@ -24,7 +24,7 @@ class UrlTest extends TestCase { public function testNormalizerCanBeSet() { - $url = new Url(['normalizer' => 'trim']); + $url = new Url(['normalizer' => 'trim', 'requireTld' => true]); $this->assertEquals('trim', $url->normalizer); } @@ -33,14 +33,14 @@ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The "normalizer" option must be a valid callable ("string" given).'); - new Url(['normalizer' => 'Unknown Callable']); + new Url(['normalizer' => 'Unknown Callable', 'requireTld' => true]); } public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The "normalizer" option must be a valid callable ("stdClass" given).'); - new Url(['normalizer' => new \stdClass()]); + new Url(['normalizer' => new \stdClass(), 'requireTld' => true]); } public function testAttributes() @@ -73,17 +73,27 @@ public function testAttributes() self::assertNull($aConstraint->normalizer); self::assertTrue($dConstraint->requireTld); } + + /** + * @group legacy + */ + public function testRequireTldDefaultsToFalse() + { + $constraint = new Url(); + + $this->assertFalse($constraint->requireTld); + } } class UrlDummy { - #[Url] + #[Url(requireTld: false)] private $a; - #[Url(message: 'myMessage', protocols: ['ftp', 'gopher'], normalizer: 'trim')] + #[Url(message: 'myMessage', protocols: ['ftp', 'gopher'], normalizer: 'trim', requireTld: false)] private $b; - #[Url(relativeProtocol: true, groups: ['my_group'], payload: 'some attached data')] + #[Url(relativeProtocol: true, groups: ['my_group'], payload: 'some attached data', requireTld: false)] private $c; #[Url(requireTld: true)] diff --git a/Tests/Constraints/UrlValidatorTest.php b/Tests/Constraints/UrlValidatorTest.php index 58111a345..d1ae946eb 100644 --- a/Tests/Constraints/UrlValidatorTest.php +++ b/Tests/Constraints/UrlValidatorTest.php @@ -25,21 +25,21 @@ protected function createValidator(): UrlValidator public function testNullIsValid() { - $this->validator->validate(null, new Url()); + $this->validator->validate(null, new Url(requireTld: true)); $this->assertNoViolation(); } public function testEmptyStringIsValid() { - $this->validator->validate('', new Url()); + $this->validator->validate('', new Url(requireTld: true)); $this->assertNoViolation(); } public function testEmptyStringFromObjectIsValid() { - $this->validator->validate(new EmailProvider(), new Url()); + $this->validator->validate(new EmailProvider(), new Url(requireTld: true)); $this->assertNoViolation(); } @@ -47,7 +47,7 @@ public function testEmptyStringFromObjectIsValid() public function testExpectsStringCompatibleType() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate(new \stdClass(), new Url()); + $this->validator->validate(new \stdClass(), new Url(requireTld: true)); } /** @@ -55,7 +55,7 @@ public function testExpectsStringCompatibleType() */ public function testValidUrls($url) { - $this->validator->validate($url, new Url()); + $this->validator->validate($url, new Url(requireTld: false)); $this->assertNoViolation(); } @@ -65,7 +65,10 @@ public function testValidUrls($url) */ public function testValidUrlsWithWhitespaces($url) { - $this->validator->validate($url, new Url(['normalizer' => 'trim'])); + $this->validator->validate($url, new Url([ + 'normalizer' => 'trim', + 'requireTld' => true, + ])); $this->assertNoViolation(); } @@ -78,6 +81,7 @@ public function testValidRelativeUrl($url) { $constraint = new Url([ 'relativeProtocol' => true, + 'requireTld' => false, ]); $this->validator->validate($url, $constraint); @@ -196,6 +200,7 @@ public function testInvalidUrls($url) { $constraint = new Url([ 'message' => 'myMessage', + 'requireTld' => false, ]); $this->validator->validate($url, $constraint); @@ -215,6 +220,7 @@ public function testInvalidRelativeUrl($url) $constraint = new Url([ 'message' => 'myMessage', 'relativeProtocol' => true, + 'requireTld' => false, ]); $this->validator->validate($url, $constraint); @@ -292,10 +298,11 @@ public static function getInvalidUrls() /** * @dataProvider getValidCustomUrls */ - public function testCustomProtocolIsValid($url) + public function testCustomProtocolIsValid($url, $requireTld) { $constraint = new Url([ 'protocols' => ['ftp', 'file', 'git'], + 'requireTld' => $requireTld, ]); $this->validator->validate($url, $constraint); @@ -306,9 +313,9 @@ public function testCustomProtocolIsValid($url) public static function getValidCustomUrls() { return [ - ['ftp://example.com'], - ['file://127.0.0.1'], - ['git://[::1]/'], + ['ftp://example.com', true], + ['file://127.0.0.1', false], + ['git://[::1]/', false], ]; } diff --git a/composer.json b/composer.json index 31d88f870..fe1dd5515 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php83": "^1.27", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/translation-contracts": "^2.5|^3" }, "require-dev": { From 7e721686dd9ebcecf73a06b96694cbeebd75a558 Mon Sep 17 00:00:00 2001 From: Ninos Ego Date: Wed, 3 Apr 2024 14:03:51 +0200 Subject: [PATCH 046/149] [Validator] Add support for types (`ALL*`, `LOCAL_*`, `UNIVERSAL_*`, `UNICAST_*`, `MULTICAST_*`, `BROADCAST`) in `MacAddress` constraint --- Constraints/MacAddress.php | 41 ++ Constraints/MacAddressValidator.php | 67 ++- Tests/Constraints/MacAddressTest.php | 14 +- Tests/Constraints/MacAddressValidatorTest.php | 420 ++++++++++++++++++ 4 files changed, 538 insertions(+), 4 deletions(-) diff --git a/Constraints/MacAddress.php b/Constraints/MacAddress.php index 7f1f63254..d0062bc56 100644 --- a/Constraints/MacAddress.php +++ b/Constraints/MacAddress.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * Validates that a value is a valid MAC address. @@ -21,22 +22,62 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class MacAddress extends Constraint { + public const ALL = 'all'; + public const ALL_NO_BROADCAST = 'all_no_broadcast'; + public const LOCAL_ALL = 'local_all'; + public const LOCAL_NO_BROADCAST = 'local_no_broadcast'; + public const LOCAL_UNICAST = 'local_unicast'; + public const LOCAL_MULTICAST = 'local_multicast'; + public const LOCAL_MULTICAST_NO_BROADCAST = 'local_multicast_no_broadcast'; + public const UNIVERSAL_ALL = 'universal_all'; + public const UNIVERSAL_UNICAST = 'universal_unicast'; + public const UNIVERSAL_MULTICAST = 'universal_multicast'; + public const UNICAST_ALL = 'unicast_all'; + public const MULTICAST_ALL = 'multicast_all'; + public const MULTICAST_NO_BROADCAST = 'multicast_no_broadcast'; + public const BROADCAST = 'broadcast'; + public const INVALID_MAC_ERROR = 'a183fbff-6968-43b4-82a2-cc5cf7150036'; + private const TYPES = [ + self::ALL, + self::ALL_NO_BROADCAST, + self::LOCAL_ALL, + self::LOCAL_NO_BROADCAST, + self::LOCAL_UNICAST, + self::LOCAL_MULTICAST, + self::LOCAL_MULTICAST_NO_BROADCAST, + self::UNIVERSAL_ALL, + self::UNIVERSAL_UNICAST, + self::UNIVERSAL_MULTICAST, + self::UNICAST_ALL, + self::MULTICAST_ALL, + self::MULTICAST_NO_BROADCAST, + self::BROADCAST, + ]; + protected const ERROR_NAMES = [ self::INVALID_MAC_ERROR => 'INVALID_MAC_ERROR', ]; public ?\Closure $normalizer; + /** + * @param self::ALL*|self::LOCAL_*|self::UNIVERSAL_*|self::UNICAST_*|self::MULTICAST_*|self::BROADCAST $type A mac address type to validate (defaults to {@see self::ALL}) + */ public function __construct( public string $message = 'This value is not a valid MAC address.', + public string $type = self::ALL, ?callable $normalizer = null, ?array $groups = null, mixed $payload = null, ) { parent::__construct(null, $groups, $payload); + if (!\in_array($this->type, self::TYPES, true)) { + throw new ConstraintDefinitionException(sprintf('The option "type" must be one of "%s".', implode('", "', self::TYPES))); + } + $this->normalizer = null !== $normalizer ? $normalizer(...) : null; } } diff --git a/Constraints/MacAddressValidator.php b/Constraints/MacAddressValidator.php index a93f90b4d..803798128 100644 --- a/Constraints/MacAddressValidator.php +++ b/Constraints/MacAddressValidator.php @@ -43,11 +43,76 @@ public function validate(mixed $value, Constraint $constraint): void $value = ($constraint->normalizer)($value); } - if (!filter_var($value, \FILTER_VALIDATE_MAC)) { + if (!self::checkMac($value, $constraint->type)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(MacAddress::INVALID_MAC_ERROR) ->addViolation(); } } + + /** + * Checks whether a MAC address is valid. + */ + private static function checkMac(string $mac, string $type): bool + { + if (!filter_var($mac, \FILTER_VALIDATE_MAC)) { + return false; + } + + return match ($type) { + MacAddress::ALL => true, + MacAddress::ALL_NO_BROADCAST => !self::isBroadcast($mac), + MacAddress::LOCAL_ALL => self::isLocal($mac), + MacAddress::LOCAL_NO_BROADCAST => self::isLocal($mac) && !self::isBroadcast($mac), + MacAddress::LOCAL_UNICAST => self::isLocal($mac) && self::isUnicast($mac), + MacAddress::LOCAL_MULTICAST => self::isLocal($mac) && !self::isUnicast($mac), + MacAddress::LOCAL_MULTICAST_NO_BROADCAST => self::isLocal($mac) && !self::isUnicast($mac) && !self::isBroadcast($mac), + MacAddress::UNIVERSAL_ALL => !self::isLocal($mac), + MacAddress::UNIVERSAL_UNICAST => !self::isLocal($mac) && self::isUnicast($mac), + MacAddress::UNIVERSAL_MULTICAST => !self::isLocal($mac) && !self::isUnicast($mac), + MacAddress::UNICAST_ALL => self::isUnicast($mac), + MacAddress::MULTICAST_ALL => !self::isUnicast($mac), + MacAddress::MULTICAST_NO_BROADCAST => !self::isUnicast($mac) && !self::isBroadcast($mac), + MacAddress::BROADCAST => self::isBroadcast($mac), + }; + } + + /** + * Checks whether a MAC address is unicast or multicast. + */ + private static function isUnicast(string $mac): bool + { + return match (self::sanitize($mac)[1]) { + '0', '4', '8', 'c', '2', '6', 'a', 'e' => true, + default => false, + }; + } + + /** + * Checks whether a MAC address is local or universal. + */ + private static function isLocal(string $mac): bool + { + return match (self::sanitize($mac)[1]) { + '2', '6', 'a', 'e', '3', '7', 'b', 'f' => true, + default => false, + }; + } + + /** + * Checks whether a MAC address is broadcast. + */ + private static function isBroadcast(string $mac): bool + { + return 'ffffffffffff' === self::sanitize($mac); + } + + /** + * Returns the sanitized MAC address. + */ + private static function sanitize(string $mac): string + { + return strtolower(str_replace([':', '-', '.'], '', $mac)); + } } diff --git a/Tests/Constraints/MacAddressTest.php b/Tests/Constraints/MacAddressTest.php index 0669f3ed8..94cbfedd4 100644 --- a/Tests/Constraints/MacAddressTest.php +++ b/Tests/Constraints/MacAddressTest.php @@ -37,11 +37,16 @@ public function testAttributes() [$aConstraint] = $metadata->properties['a']->getConstraints(); self::assertSame('myMessage', $aConstraint->message); self::assertEquals(trim(...), $aConstraint->normalizer); + self::assertSame(MacAddress::ALL, $aConstraint->type); self::assertSame(['Default', 'MacAddressDummy'], $aConstraint->groups); [$bConstraint] = $metadata->properties['b']->getConstraints(); - self::assertSame(['my_group'], $bConstraint->groups); - self::assertSame('some attached data', $bConstraint->payload); + self::assertSame(MacAddress::LOCAL_UNICAST, $bConstraint->type); + self::assertSame(['Default', 'MacAddressDummy'], $bConstraint->groups); + + [$cConstraint] = $metadata->properties['c']->getConstraints(); + self::assertSame(['my_group'], $cConstraint->groups); + self::assertSame('some attached data', $cConstraint->payload); } } @@ -50,6 +55,9 @@ class MacAddressDummy #[MacAddress(message: 'myMessage', normalizer: 'trim')] private $a; - #[MacAddress(groups: ['my_group'], payload: 'some attached data')] + #[MacAddress(type: MacAddress::LOCAL_UNICAST)] private $b; + + #[MacAddress(groups: ['my_group'], payload: 'some attached data')] + private $c; } diff --git a/Tests/Constraints/MacAddressValidatorTest.php b/Tests/Constraints/MacAddressValidatorTest.php index 2552d4bc9..d755df486 100644 --- a/Tests/Constraints/MacAddressValidatorTest.php +++ b/Tests/Constraints/MacAddressValidatorTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Constraints\MacAddress; use Symfony\Component\Validator\Constraints\MacAddressValidator; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -46,6 +47,12 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new MacAddress()); } + public function testInvalidValidatorType() + { + $this->expectException(ConstraintDefinitionException::class); + new MacAddress(type: 666); + } + /** * @dataProvider getValidMacs */ @@ -65,9 +72,422 @@ public static function getValidMacs(): array ['ff-ff-ff-ff-ff-ff'], ['FF:FF:FF:FF:FF:FF'], ['FF-FF-FF-FF-FF-FF'], + ['FFFF.FFFF.FFFF'], + ]; + } + + public static function getValidLocalUnicastMacs(): array + { + return [ + ['02:00:00:00:00:00'], + ['16-00-00-00-00-00'], + ['2a-00-00-00-00-00'], + ['3e-00-00-00-00-00'], + ['3E00.0000.0000'], + ]; + } + + public static function getValidLocalMulticastMacs(): array + { + return [ + ['03:00:00:00:00:00'], + ['17-00-00-00-00-00'], + ['2b-00-00-00-00-00'], + ['3f-00-00-00-00-00'], + ['3F00.0000.0000'], ]; } + public static function getValidUniversalUnicastMacs(): array + { + return [ + ['00:00:00:00:00:00'], + ['14-00-00-00-00-00'], + ['28-00-00-00-00-00'], + ['3c-00-00-00-00-00'], + ['3C00.0000.0000'], + ]; + } + + public static function getValidUniversalMulticastMacs(): array + { + return [ + ['01:00:00:00:00:00'], + ['15-00-00-00-00-00'], + ['29-00-00-00-00-00'], + ['3d-00-00-00-00-00'], + ['3D00.0000.0000'], + ]; + } + + public static function getValidBroadcastMacs(): array + { + return [ + ['ff:ff:ff:ff:ff:ff'], + ['FF-ff-FF-ff-FF-ff'], + ['fFff.ffff.fffF'], + ]; + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testValidAllNoBroadcastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::ALL_NO_BROADCAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidBroadcastMacs + */ + public function testInvalidAllNoBroadcastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::ALL_NO_BROADCAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidBroadcastMacs + */ + public function testValidLocalMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_ALL)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testInvalidLocalMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_ALL); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + */ + public function testValidLocalNoBroadcastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_NO_BROADCAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + * @dataProvider getValidBroadcastMacs + */ + public function testInvalidLocalNoBroadcastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_NO_BROADCAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + */ + public function testValidLocalUnicastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_UNICAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testInvalidLocalUnicastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_UNICAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidBroadcastMacs + */ + public function testValidLocalMulticastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_MULTICAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testInvalidLocalMulticastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_MULTICAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalMulticastMacs + */ + public function testValidLocalMulticastNoBroadcastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_MULTICAST_NO_BROADCAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + * @dataProvider getValidBroadcastMacs + */ + public function testInvalidLocalMulticastNoBroadcastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_MULTICAST_NO_BROADCAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testValidUniversalMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::UNIVERSAL_ALL)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + */ + public function testInvalidUniversalMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::UNIVERSAL_ALL); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidUniversalUnicastMacs + */ + public function testValidUniversalUnicastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::UNIVERSAL_UNICAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testInvalidUniversalUnicastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::UNIVERSAL_UNICAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidUniversalMulticastMacs + */ + public function testValidUniversalMulticastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::UNIVERSAL_MULTICAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalUnicastMacs + */ + public function testInvalidUniversalMulticastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::UNIVERSAL_MULTICAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidUniversalUnicastMacs + */ + public function testUnicastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::UNICAST_ALL)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testInvalidUnicastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::UNICAST_ALL); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalMulticastMacs + * @dataProvider getValidBroadcastMacs + */ + public function testMulticastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::MULTICAST_ALL)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidUniversalUnicastMacs + */ + public function testInvalidMulticastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::MULTICAST_ALL); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testMulticastNoBroadcastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::MULTICAST_NO_BROADCAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidBroadcastMacs + */ + public function testInvalidMulticastNoBroadcastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::MULTICAST_NO_BROADCAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider getValidBroadcastMacs + */ + public function testBroadcastMacs($mac) + { + $this->validator->validate($mac, new MacAddress(type: MacAddress::BROADCAST)); + + $this->assertNoViolation(); + } + + /** + * @dataProvider getValidLocalUnicastMacs + * @dataProvider getValidLocalMulticastMacs + * @dataProvider getValidUniversalUnicastMacs + * @dataProvider getValidUniversalMulticastMacs + */ + public function testInvalidBroadcastMacs($mac) + { + $constraint = new MacAddress('myMessage', type: MacAddress::BROADCAST); + + $this->validator->validate($mac, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + /** * @dataProvider getValidMacsWithWhitespaces */ From d013e693e919da6754fa3af5891bc6e83a3f23bc Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 15 Apr 2024 16:56:40 +0200 Subject: [PATCH 047/149] [Validator] Update `@internal` description --- Context/ExecutionContextFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Context/ExecutionContextFactory.php b/Context/ExecutionContextFactory.php index 4a47e87fe..a6a2844ad 100644 --- a/Context/ExecutionContextFactory.php +++ b/Context/ExecutionContextFactory.php @@ -19,7 +19,7 @@ * * @author Bernhard Schussek * - * @internal version 2.5. Code against ExecutionContextFactoryInterface instead. + * @internal */ class ExecutionContextFactory implements ExecutionContextFactoryInterface { From 2a4e0ba55ae02e3e5c3dcefe41c635af2a9f4f47 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 16 Apr 2024 18:11:55 +0200 Subject: [PATCH 048/149] [Validator] Update the "no TLD" error message --- Constraints/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/Url.php b/Constraints/Url.php index b16496b38..dfd6ea465 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -31,7 +31,7 @@ class Url extends Constraint ]; public string $message = 'This value is not a valid URL.'; - public string $tldMessage = 'This URL does not contain a TLD.'; + public string $tldMessage = 'This URL is missing a top-level domain.'; public array $protocols = ['http', 'https']; public bool $relativeProtocol = false; public bool $requireTld = false; From 0763ff6d1596c31e8e67ead43be56d31f6a1d691 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 24 Apr 2024 11:59:21 +0200 Subject: [PATCH 049/149] add missing HasNamedArguments attribute --- Constraints/Charset.php | 2 ++ Constraints/MacAddress.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Constraints/Charset.php b/Constraints/Charset.php index 7afffcfa3..8b1d482ca 100644 --- a/Constraints/Charset.php +++ b/Constraints/Charset.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -26,6 +27,7 @@ final class Charset extends Constraint self::BAD_ENCODING_ERROR => 'BAD_ENCODING_ERROR', ]; + #[HasNamedArguments] public function __construct( public array|string $encodings = [], public string $message = 'The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.', diff --git a/Constraints/MacAddress.php b/Constraints/MacAddress.php index d0062bc56..12c41a247 100644 --- a/Constraints/MacAddress.php +++ b/Constraints/MacAddress.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -65,6 +66,7 @@ class MacAddress extends Constraint /** * @param self::ALL*|self::LOCAL_*|self::UNIVERSAL_*|self::UNICAST_*|self::MULTICAST_*|self::BROADCAST $type A mac address type to validate (defaults to {@see self::ALL}) */ + #[HasNamedArguments] public function __construct( public string $message = 'This value is not a valid MAC address.', public string $type = self::ALL, From 0254dc371b5f8855fb3237d54bb778246ab5efb1 Mon Sep 17 00:00:00 2001 From: sam-bee <130986804+sam-bee@users.noreply.github.com> Date: Mon, 13 May 2024 12:43:33 +0100 Subject: [PATCH 050/149] Because PHP 8.4 is adding deprecation warnings for non-nullable parameters with null default, change typehints --- Validation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Validation.php b/Validation.php index 45c593003..c33900a47 100644 --- a/Validation.php +++ b/Validation.php @@ -40,7 +40,7 @@ public static function createCallable(Constraint|ValidatorInterface|null $constr /** * Creates a callable that returns true/false instead of throwing validation exceptions. * - * @return callable(mixed $value, ConstraintViolationListInterface &$violations = null): bool + * @return callable(mixed $value, ?ConstraintViolationListInterface &$violations = null): bool */ public static function createIsValidCallable(Constraint|ValidatorInterface|null $constraintOrValidator = null, Constraint ...$constraints): callable { From a9d50c134e26e5edde87ff97fa90f6154df50162 Mon Sep 17 00:00:00 2001 From: Maximilian Beckers Date: Wed, 15 May 2024 09:10:53 +0200 Subject: [PATCH 051/149] [Validator] BicValidator add strict mode to validate bics in strict mode --- Constraints/Bic.php | 30 ++++++++++++++++++++++-- Constraints/BicValidator.php | 7 ++++-- Tests/Constraints/BicValidatorTest.php | 31 +++++++++++++++++++++++++ Tests/Constraints/IbanValidatorTest.php | 1 + 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 625fb7213..22640bedf 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -15,6 +15,7 @@ use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Exception\LogicException; /** @@ -27,6 +28,14 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Bic extends Constraint { + public const VALIDATION_MODE_STRICT = 'strict'; + public const VALIDATION_MODE_CASE_INSENSITIVE = 'case-insensitive'; + + public const VALIDATION_MODES = [ + self::VALIDATION_MODE_STRICT, + self::VALIDATION_MODE_CASE_INSENSITIVE, + ]; + public const INVALID_LENGTH_ERROR = '66dad313-af0b-4214-8566-6c799be9789c'; public const INVALID_CHARACTERS_ERROR = 'f424c529-7add-4417-8f2d-4b656e4833e2'; /** @@ -49,18 +58,34 @@ class Bic extends Constraint public string $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.'; public ?string $iban = null; public ?string $ibanPropertyPath = null; + public ?string $mode = self::VALIDATION_MODE_STRICT; /** * @param array|null $options * @param string|null $iban An IBAN value to validate that its country code is the same as the BIC's one * @param string|null $ibanPropertyPath Property path to the IBAN value when validating objects * @param string[]|null $groups + * @param string|null $mode The mode used to validate the BIC; pass null to use the default mode (strict) */ - public function __construct(?array $options = null, ?string $message = null, ?string $iban = null, ?string $ibanPropertyPath = null, ?string $ibanMessage = null, ?array $groups = null, mixed $payload = null) - { + public function __construct( + ?array $options = null, + ?string $message = null, + ?string $iban = null, + ?string $ibanPropertyPath = null, + ?string $ibanMessage = null, + ?array $groups = null, + mixed $payload = null, + ?string $mode = 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 (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::VALIDATION_MODES, true)) { + throw new InvalidArgumentException('The "mode" parameter value is not valid.'); + } + if (null !== $mode && !\in_array($mode, self::VALIDATION_MODES, true)) { + throw new InvalidArgumentException('The "mode" parameter value is not valid.'); + } parent::__construct($options, $groups, $payload); @@ -68,6 +93,7 @@ public function __construct(?array $options = null, ?string $message = null, ?st $this->ibanMessage = $ibanMessage ?? $this->ibanMessage; $this->iban = $iban ?? $this->iban; $this->ibanPropertyPath = $ibanPropertyPath ?? $this->ibanPropertyPath; + $this->mode = $mode ?? $this->mode; 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.'); diff --git a/Constraints/BicValidator.php b/Constraints/BicValidator.php index 19b88b689..d0d1b5084 100644 --- a/Constraints/BicValidator.php +++ b/Constraints/BicValidator.php @@ -100,6 +100,9 @@ public function validate(mixed $value, Constraint $constraint): void } $bicCountryCode = substr($canonicalize, 4, 2); + if (Bic::VALIDATION_MODE_CASE_INSENSITIVE === $constraint->mode) { + $bicCountryCode = strtoupper($bicCountryCode); + } if (!isset(self::BIC_COUNTRY_TO_IBAN_COUNTRY_MAP[$bicCountryCode]) && !Countries::exists($bicCountryCode)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) @@ -109,8 +112,8 @@ public function validate(mixed $value, Constraint $constraint): void return; } - // should contain uppercase characters only - if (strtoupper($canonicalize) !== $canonicalize) { + // should contain uppercase characters only in strict mode + if (Bic::VALIDATION_MODE_STRICT === $constraint->mode && strtoupper($canonicalize) !== $canonicalize) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Bic::INVALID_CASE_ERROR) diff --git a/Tests/Constraints/BicValidatorTest.php b/Tests/Constraints/BicValidatorTest.php index 8b3c81542..b71c7dac5 100644 --- a/Tests/Constraints/BicValidatorTest.php +++ b/Tests/Constraints/BicValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraints\Bic; use Symfony\Component\Validator\Constraints\BicValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; @@ -301,6 +302,36 @@ public static function getValidBicSpecialCases() yield ['CAIXICBBXXX', 'ES79 2100 0813 6101 2345 6789']; yield ['CAIXEABBXXX', 'ES79 2100 0813 6101 2345 6789']; } + + /** + * @dataProvider getValidBicsWithNormalizerToUpper + */ + public function testValidBicsWithNormalizerToUpper($bic) + { + $this->validator->validate($bic, new Bic(mode: Bic::VALIDATION_MODE_CASE_INSENSITIVE)); + + $this->assertNoViolation(); + } + + public static function getValidBicsWithNormalizerToUpper() + { + return [ + ['ASPKAT2LXXX'], + ['ASPKat2LXXX'], + ['ASPKaT2LXXX'], + ['ASPKAt2LXXX'], + ['aspkat2lxxx'], + ]; + } + + public function testFailOnInvalidMode() + { + $this->expectException(InvalidArgumentException::class); + $this->validator->validate('ASPKAT2LXXX', new Bic(mode: 'invalid')); + + $this->expectException(InvalidArgumentException::class); + $this->validator->validate('ASPKAT2LXXX', new Bic(options: ['mode' => 'invalid'])); + } } class BicComparisonTestClass diff --git a/Tests/Constraints/IbanValidatorTest.php b/Tests/Constraints/IbanValidatorTest.php index 212d5329f..390f106df 100644 --- a/Tests/Constraints/IbanValidatorTest.php +++ b/Tests/Constraints/IbanValidatorTest.php @@ -53,6 +53,7 @@ public static function getValidIbans() return [ ['CH9300762011623852957'], // Switzerland without spaces ['CH93 0076 2011 6238 5295 7'], // Switzerland with multiple spaces + ['ch93 0076 2011 6238 5295 7'], // Switzerland lower case // Country list // http://www.rbs.co.uk/corporate/international/g0/guide-to-international-business/regulatory-information/iban/iban-example.ashx From 46e7c0231bbd5f94d15fc25adb8abafc4b78da74 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 20 May 2024 17:59:42 +0200 Subject: [PATCH 052/149] Fix CharsetValidator with string encoding --- Constraints/CharsetValidator.php | 2 +- Tests/Constraints/CharsetValidatorTest.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Constraints/CharsetValidator.php b/Constraints/CharsetValidator.php index 5cde6ae90..63259a45d 100644 --- a/Constraints/CharsetValidator.php +++ b/Constraints/CharsetValidator.php @@ -38,7 +38,7 @@ public function validate(mixed $value, Constraint $constraint): void if (!\in_array(mb_detect_encoding($value, $constraint->encodings, true), (array) $constraint->encodings, true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ detected }}', mb_detect_encoding($value, strict: true)) - ->setParameter('{{ encodings }}', implode(', ', $constraint->encodings)) + ->setParameter('{{ encodings }}', implode(', ', (array) $constraint->encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) ->addViolation(); } diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 88998dcdb..5c2f2c884 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -37,13 +37,13 @@ public function testEncodingIsValid(string|\Stringable $value, array|string $enc /** * @dataProvider provideInvalidValues */ - public function testInvalidValues(string $value, array $encodings) + public function testInvalidValues(string $value, array|string $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); $this->buildViolation('The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.') ->setParameter('{{ detected }}', 'UTF-8') - ->setParameter('{{ encodings }}', implode(', ', $encodings)) + ->setParameter('{{ encodings }}', implode(', ', (array) $encodings)) ->setCode(Charset::BAD_ENCODING_ERROR) ->assertRaised(); } @@ -73,6 +73,7 @@ public static function provideValidValues() public static function provideInvalidValues() { + yield ['my non-Äscîi string', 'ASCII']; yield ['my non-Äscîi string', ['ASCII']]; yield ['😊', ['7bit']]; } From 3d50dca8d0fcc598581471edad56d5e762b39c0f Mon Sep 17 00:00:00 2001 From: Yannick Date: Fri, 10 May 2024 11:54:20 +0200 Subject: [PATCH 053/149] [Validator] Make `PasswordStrengthValidator::estimateStrength()` public --- CHANGELOG.md | 5 + Constraints/PasswordStrengthValidator.php | 2 +- .../PasswordStrengthValidatorTest.php | 16 +++ ...sswordStrengthValidatorWithClosureTest.php | 104 ++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 006040582..4e64932a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Make `PasswordStrengthValidator::estimateStrength()` public + 7.1 --- diff --git a/Constraints/PasswordStrengthValidator.php b/Constraints/PasswordStrengthValidator.php index 96a4a74f7..509ea2a42 100644 --- a/Constraints/PasswordStrengthValidator.php +++ b/Constraints/PasswordStrengthValidator.php @@ -57,7 +57,7 @@ public function validate(#[\SensitiveParameter] mixed $value, Constraint $constr * * @return PasswordStrength::STRENGTH_* */ - private static function estimateStrength(#[\SensitiveParameter] string $password): int + public static function estimateStrength(#[\SensitiveParameter] string $password): int { if (!$length = \strlen($password)) { return PasswordStrength::STRENGTH_VERY_WEAK; diff --git a/Tests/Constraints/PasswordStrengthValidatorTest.php b/Tests/Constraints/PasswordStrengthValidatorTest.php index 78e7951a1..fb063f4a7 100644 --- a/Tests/Constraints/PasswordStrengthValidatorTest.php +++ b/Tests/Constraints/PasswordStrengthValidatorTest.php @@ -92,4 +92,20 @@ public static function provideInvalidConstraints(): iterable '0', ]; } + + /** + * @dataProvider getPasswordValues + */ + public function testStrengthEstimator(string $password, int $expectedStrength) + { + self::assertSame($expectedStrength, PasswordStrengthValidator::estimateStrength((string) $password)); + } + + public static function getPasswordValues(): iterable + { + yield ['How-is-this', PasswordStrength::STRENGTH_WEAK]; + yield ['Reasonable-pwd', PasswordStrength::STRENGTH_MEDIUM]; + yield ['This 1s a very g00d Pa55word! ;-)', PasswordStrength::STRENGTH_VERY_STRONG]; + yield ['pudding-smack-👌🏼-fox-😎', PasswordStrength::STRENGTH_VERY_STRONG]; + } } diff --git a/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php b/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php new file mode 100644 index 000000000..3d24e8c5b --- /dev/null +++ b/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\PasswordStrength; +use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; + +class PasswordStrengthValidatorWithClosureTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): PasswordStrengthValidator + { + return new PasswordStrengthValidator(static function (string $value) { + $length = \strlen($value); + + return match (true) { + $length < 6 => PasswordStrength::STRENGTH_VERY_WEAK, + $length < 10 => PasswordStrength::STRENGTH_WEAK, + $length < 15 => PasswordStrength::STRENGTH_MEDIUM, + $length < 20 => PasswordStrength::STRENGTH_STRONG, + default => PasswordStrength::STRENGTH_VERY_STRONG, + }; + }); + } + + /** + * @dataProvider getValidValues + */ + public function testValidValues(string|\Stringable $value, int $expectedStrength) + { + $this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength)); + + $this->assertNoViolation(); + + if (PasswordStrength::STRENGTH_VERY_STRONG === $expectedStrength) { + return; + } + + $this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength + 1)); + + $this->buildViolation('The password strength is too low. Please use a stronger password.') + ->setCode(PasswordStrength::PASSWORD_STRENGTH_ERROR) + ->setParameter('{{ strength }}', $expectedStrength) + ->assertRaised(); + } + + public static function getValidValues(): iterable + { + yield ['az34tyu', PasswordStrength::STRENGTH_WEAK]; + yield ['A med1um one', PasswordStrength::STRENGTH_MEDIUM]; + yield ['a str0ng3r one doh', PasswordStrength::STRENGTH_STRONG]; + yield [new StringableValue('HeloW0rld'), PasswordStrength::STRENGTH_WEAK]; + } + + /** + * @dataProvider provideInvalidConstraints + */ + public function testThePasswordIsWeak(PasswordStrength $constraint, string $password, string $expectedMessage, string $expectedCode, string $strength) + { + $this->validator->validate($password, $constraint); + + $this->buildViolation($expectedMessage) + ->setCode($expectedCode) + ->setParameters([ + '{{ strength }}' => $strength, + ]) + ->assertRaised(); + } + + public static function provideInvalidConstraints(): iterable + { + yield [ + new PasswordStrength(), + 'password', + 'The password strength is too low. Please use a stronger password.', + PasswordStrength::PASSWORD_STRENGTH_ERROR, + (string) PasswordStrength::STRENGTH_WEAK, + ]; + yield [ + new PasswordStrength(minScore: PasswordStrength::STRENGTH_VERY_STRONG), + 'Good password?', + 'The password strength is too low. Please use a stronger password.', + PasswordStrength::PASSWORD_STRENGTH_ERROR, + (string) PasswordStrength::STRENGTH_MEDIUM, + ]; + yield [ + new PasswordStrength(message: 'This password should be strong.'), + 'password', + 'This password should be strong.', + PasswordStrength::PASSWORD_STRENGTH_ERROR, + (string) PasswordStrength::STRENGTH_WEAK, + ]; + } +} From fcab7598968b21c361becc930fcae8846638c4c0 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 4 Jun 2024 07:46:00 +0200 Subject: [PATCH 054/149] do not modify a constraint during validation to not leak its context --- Constraints/CidrValidator.php | 8 +++++--- Tests/Constraints/CidrValidatorTest.php | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Constraints/CidrValidator.php b/Constraints/CidrValidator.php index 4fc78a782..1c6f4c0bc 100644 --- a/Constraints/CidrValidator.php +++ b/Constraints/CidrValidator.php @@ -71,11 +71,13 @@ public function validate($value, Constraint $constraint): void return; } - if (filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) && $constraint->netmaskMax > 32) { - $constraint->netmaskMax = 32; + $netmaskMax = $constraint->netmaskMax; + + if (filter_var($ipAddress, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) && $netmaskMax > 32) { + $netmaskMax = 32; } - if ($netmask < $constraint->netmaskMin || $netmask > $constraint->netmaskMax) { + if ($netmask < $constraint->netmaskMin || $netmask > $netmaskMax) { $this->context ->buildViolation($constraint->netmaskRangeViolationMessage) ->setParameter('{{ min }}', $constraint->netmaskMin) diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 9274d81da..5ac531094 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -255,4 +255,17 @@ public static function getWithWrongVersion(): array ['2001:0db8:85a3:0000:0000:8a2e:0370:7334/13', Ip::V4], ]; } + + public function testDoesNotModifyContextBetweenValidations() + { + $constraint = new Cidr(); + + $this->validator->validate('1.2.3.4/28', $constraint); + + $this->assertNoViolation(); + + $this->validator->validate('::1/64', $constraint); + + $this->assertNoViolation(); + } } From bc5028f6c25896cc896ec8903542ae63cec2b266 Mon Sep 17 00:00:00 2001 From: symfonyaml Date: Sat, 3 Feb 2024 20:07:16 +0100 Subject: [PATCH 055/149] [Validator] Add `Yaml` constraint for validating YAML content --- CHANGELOG.md | 1 + Constraints/Yaml.php | 44 +++++++++++ Constraints/YamlValidator.php | 63 ++++++++++++++++ Tests/Constraints/YamlTest.php | 57 +++++++++++++++ Tests/Constraints/YamlValidatorTest.php | 97 +++++++++++++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 Constraints/Yaml.php create mode 100644 Constraints/YamlValidator.php create mode 100644 Tests/Constraints/YamlTest.php create mode 100644 Tests/Constraints/YamlValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e64932a4..93b2d58b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Make `PasswordStrengthValidator::estimateStrength()` public + * Add the `Yaml` constraint for validating YAML content 7.1 --- diff --git a/Constraints/Yaml.php b/Constraints/Yaml.php new file mode 100644 index 000000000..3024855cc --- /dev/null +++ b/Constraints/Yaml.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Attribute\HasNamedArguments; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\LogicException; +use Symfony\Component\Yaml\Parser; + +/** + * @author Kev + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class Yaml extends Constraint +{ + public const INVALID_YAML_ERROR = '63313a31-837c-42bb-99eb-542c76aacc48'; + + protected const ERROR_NAMES = [ + self::INVALID_YAML_ERROR => 'INVALID_YAML_ERROR', + ]; + + #[HasNamedArguments] + public function __construct( + public string $message = 'This value is not valid YAML.', + public int $flags = 0, + ?array $groups = null, + mixed $payload = null, + ) { + if (!class_exists(Parser::class)) { + throw new LogicException('The Yaml component is required to use the Yaml constraint. Try running "composer require symfony/yaml".'); + } + + parent::__construct(null, $groups, $payload); + } +} diff --git a/Constraints/YamlValidator.php b/Constraints/YamlValidator.php new file mode 100644 index 000000000..2675ed9aa --- /dev/null +++ b/Constraints/YamlValidator.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser; + +/** + * @author Kev + */ +class YamlValidator extends ConstraintValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof Yaml) { + throw new UnexpectedTypeException($constraint, Yaml::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!\is_scalar($value) && !$value instanceof \Stringable) { + throw new UnexpectedValueException($value, 'string'); + } + + $value = (string) $value; + + /** @see \Symfony\Component\Yaml\Command\LintCommand::validate() */ + $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { + if (\E_USER_DEPRECATED === $level) { + throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1); + } + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; + }); + + try { + (new Parser())->parse($value, $constraint->flags); + } catch (ParseException $e) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ error }}', $e->getMessage()) + ->setParameter('{{ line }}', $e->getParsedLine()) + ->setCode(Yaml::INVALID_YAML_ERROR) + ->addViolation(); + } finally { + restore_error_handler(); + } + } +} diff --git a/Tests/Constraints/YamlTest.php b/Tests/Constraints/YamlTest.php new file mode 100644 index 000000000..c9529fc12 --- /dev/null +++ b/Tests/Constraints/YamlTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Yaml; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; +use Symfony\Component\Yaml\Yaml as YamlParser; + +/** + * @author Kev + */ +class YamlTest extends TestCase +{ + public function testAttributes() + { + $metadata = new ClassMetadata(YamlDummy::class); + $loader = new AttributeLoader(); + self::assertTrue($loader->loadClassMetadata($metadata)); + + [$bConstraint] = $metadata->properties['b']->getConstraints(); + self::assertSame('myMessage', $bConstraint->message); + self::assertSame(['Default', 'YamlDummy'], $bConstraint->groups); + + [$cConstraint] = $metadata->properties['c']->getConstraints(); + self::assertSame(['my_group'], $cConstraint->groups); + self::assertSame('some attached data', $cConstraint->payload); + + [$cConstraint] = $metadata->properties['d']->getConstraints(); + self::assertSame(YamlParser::PARSE_CONSTANT | YamlParser::PARSE_CUSTOM_TAGS, $cConstraint->flags); + } +} + +class YamlDummy +{ + #[Yaml] + private $a; + + #[Yaml(message: 'myMessage')] + private $b; + + #[Yaml(groups: ['my_group'], payload: 'some attached data')] + private $c; + + #[Yaml(flags: YamlParser::PARSE_CONSTANT | YamlParser::PARSE_CUSTOM_TAGS)] + private $d; +} diff --git a/Tests/Constraints/YamlValidatorTest.php b/Tests/Constraints/YamlValidatorTest.php new file mode 100644 index 000000000..a31892667 --- /dev/null +++ b/Tests/Constraints/YamlValidatorTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\Yaml; +use Symfony\Component\Validator\Constraints\YamlValidator; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Yaml\Yaml as YamlParser; + +/** + * @author Kev + */ +class YamlValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): YamlValidator + { + return new YamlValidator(); + } + + /** + * @dataProvider getValidValues + */ + public function testYamlIsValid($value) + { + $this->validator->validate($value, new Yaml()); + + $this->assertNoViolation(); + } + + public function testYamlWithFlags() + { + $this->validator->validate('date: 2023-01-01', new Yaml(flags: YamlParser::PARSE_DATETIME)); + $this->assertNoViolation(); + } + + /** + * @dataProvider getInvalidValues + */ + public function testInvalidValues($value, $message, $line) + { + $constraint = new Yaml( + message: 'myMessageTest', + ); + + $this->validator->validate($value, $constraint); + + $this->buildViolation('myMessageTest') + ->setParameter('{{ error }}', $message) + ->setParameter('{{ line }}', $line) + ->setCode(Yaml::INVALID_YAML_ERROR) + ->assertRaised(); + } + + public function testInvalidFlags() + { + $value = 'tags: [!tagged app.myclass]'; + $this->validator->validate($value, new Yaml()); + $this->buildViolation('This value is not valid YAML.') + ->setParameter('{{ error }}', 'Tags support is not enabled. Enable the "Yaml::PARSE_CUSTOM_TAGS" flag to use "!tagged" at line 1 (near "tags: [!tagged app.myclass]").') + ->setParameter('{{ line }}', 1) + ->setCode(Yaml::INVALID_YAML_ERROR) + ->assertRaised(); + } + + public static function getValidValues() + { + return [ + ['planet_diameters: {earth: 12742, mars: 6779, saturn: 116460, mercury: 4879}'], + ["key:\n value"], + [null], + [''], + ['"null"'], + ['null'], + ['"string"'], + ['1'], + ['true'], + [1], + ]; + } + + public static function getInvalidValues(): array + { + return [ + ['{:INVALID]', 'Malformed unquoted YAML string at line 1 (near "{:INVALID]").', 1], + ["key:\nvalue", 'Unable to parse at line 2 (near "value").', 2], + ]; + } +} From 8610994156c0cca8207c13caaba6444188d6ecc2 Mon Sep 17 00:00:00 2001 From: Anthony Tenneriello Date: Wed, 12 Jun 2024 16:09:23 +0200 Subject: [PATCH 056/149] [Validator] fix IBAN validator fails if IBAN contains non-breaking space --- Constraints/IbanValidator.php | 4 ++-- Tests/Constraints/IbanValidatorTest.php | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Constraints/IbanValidator.php b/Constraints/IbanValidator.php index 3ed971cb4..b7deeca15 100644 --- a/Constraints/IbanValidator.php +++ b/Constraints/IbanValidator.php @@ -179,8 +179,8 @@ public function validate(mixed $value, Constraint $constraint): void $value = (string) $value; - // Remove spaces and convert to uppercase - $canonicalized = str_replace(' ', '', strtoupper($value)); + // Remove spaces (regular, non-breaking, and narrow non-breaking) and convert to uppercase + $canonicalized = str_replace([' ', "\xc2\xa0", "\xe2\x80\xaf"], '', strtoupper($value)); // The IBAN must contain only digits and characters... if (!ctype_alnum($canonicalized)) { diff --git a/Tests/Constraints/IbanValidatorTest.php b/Tests/Constraints/IbanValidatorTest.php index af3cf98ac..834eb3f5d 100644 --- a/Tests/Constraints/IbanValidatorTest.php +++ b/Tests/Constraints/IbanValidatorTest.php @@ -77,6 +77,8 @@ public static function getValidIbans() ['FO97 5432 0388 8999 44'], // Faroe Islands ['FI21 1234 5600 0007 85'], // Finland ['FR14 2004 1010 0505 0001 3M02 606'], // France + ["FR14\xc2\xa02004\xc2\xa01010\xc2\xa00505\xc2\xa00001\xc2\xa03M02\xc2\xa0606"], // France with non-breaking spaces + ["FR14\xe2\x80\xaf2004\xe2\x80\xaf1010\xe2\x80\xaf0505\xe2\x80\xaf0001\xe2\x80\xaf3M02\xe2\x80\xaf606"], // France with narrow non-breaking spaces ['GE29 NB00 0000 0101 9049 17'], // Georgia ['DE89 3704 0044 0532 0130 00'], // Germany ['GI75 NWBK 0000 0000 7099 453'], // Gibraltar From d02196baf87f6eb05f53b6c2a91de8c19b3e0578 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2024 21:28:08 +0200 Subject: [PATCH 057/149] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b2d58b2..32ac5c864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.2 --- + * `IbanValidator` accepts IBANs containing non-breaking and narrow non-breaking spaces * Make `PasswordStrengthValidator::estimateStrength()` public * Add the `Yaml` constraint for validating YAML content From 7ce93b319f287e6d58087d1971475e79063b9065 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 18 Jun 2024 10:16:21 +0200 Subject: [PATCH 058/149] Add more precise types for the Yaml flags --- Constraints/Yaml.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Constraints/Yaml.php b/Constraints/Yaml.php index 3024855cc..99f2092d1 100644 --- a/Constraints/Yaml.php +++ b/Constraints/Yaml.php @@ -28,6 +28,10 @@ class Yaml extends Constraint self::INVALID_YAML_ERROR => 'INVALID_YAML_ERROR', ]; + /** + * @param int-mask-of<\Symfony\Component\Yaml\Yaml::PARSE_*> $flags + * @param string[]|null $groups + */ #[HasNamedArguments] public function __construct( public string $message = 'This value is not valid YAML.', From d1d95a3b3a0ee1e515762e2d83e69e44f2178ae8 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 18 Jun 2024 10:53:07 +0300 Subject: [PATCH 059/149] [Validator] Add `errorPath` to Unique constraint --- CHANGELOG.md | 1 + Constraints/Unique.php | 3 + Constraints/UniqueValidator.php | 3 +- Tests/Constraints/UniqueValidatorTest.php | 114 +++++++++++++++++++--- 4 files changed, 109 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32ac5c864..337534645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * `IbanValidator` accepts IBANs containing non-breaking and narrow non-breaking spaces * Make `PasswordStrengthValidator::estimateStrength()` public * Add the `Yaml` constraint for validating YAML content + * Add `errorPath` to Unique constraint 7.1 --- diff --git a/Constraints/Unique.php b/Constraints/Unique.php index c759fac63..7e4d6f626 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -25,6 +25,7 @@ class Unique extends Constraint public const IS_NOT_UNIQUE = '7911c98d-b845-4da0-94b7-a8dac36bc55a'; public array|string $fields = []; + public ?string $errorPath = null; protected const ERROR_NAMES = [ self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE', @@ -46,12 +47,14 @@ public function __construct( ?array $groups = null, mixed $payload = null, array|string|null $fields = null, + ?string $errorPath = null, ) { parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; $this->normalizer = $normalizer ?? $this->normalizer; $this->fields = $fields ?? $this->fields; + $this->errorPath = $errorPath ?? $this->errorPath; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); diff --git a/Constraints/UniqueValidator.php b/Constraints/UniqueValidator.php index e62c94179..e188e3f25 100644 --- a/Constraints/UniqueValidator.php +++ b/Constraints/UniqueValidator.php @@ -39,7 +39,7 @@ public function validate(mixed $value, Constraint $constraint): void $collectionElements = []; $normalizer = $this->getNormalizer($constraint); - foreach ($value as $element) { + foreach ($value as $index => $element) { $element = $normalizer($element); if ($fields && !$element = $this->reduceElementKeys($fields, $element)) { @@ -48,6 +48,7 @@ public function validate(mixed $value, Constraint $constraint): void if (\in_array($element, $collectionElements, true)) { $this->context->buildViolation($constraint->message) + ->atPath("[$index]".(null !== $constraint->errorPath ? ".{$constraint->errorPath}" : '')) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Unique::IS_NOT_UNIQUE) ->addViolation(); diff --git a/Tests/Constraints/UniqueValidatorTest.php b/Tests/Constraints/UniqueValidatorTest.php index 3c2dd9f21..0382eb5b6 100644 --- a/Tests/Constraints/UniqueValidatorTest.php +++ b/Tests/Constraints/UniqueValidatorTest.php @@ -61,7 +61,7 @@ public static function getValidValues() /** * @dataProvider getInvalidValues */ - public function testInvalidValues($value) + public function testInvalidValues($value, string $expectedErrorPath) { $constraint = new Unique([ 'message' => 'myMessage', @@ -71,6 +71,7 @@ public function testInvalidValues($value) $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath($expectedErrorPath) ->assertRaised(); } @@ -79,12 +80,12 @@ public static function getInvalidValues() $object = new \stdClass(); return [ - yield 'not unique booleans' => [[true, true]], - yield 'not unique integers' => [[1, 2, 3, 3]], - yield 'not unique floats' => [[0.1, 0.2, 0.1]], - yield 'not unique string' => [['a', 'b', 'a']], - yield 'not unique arrays' => [[[1, 1], [2, 3], [1, 1]]], - yield 'not unique objects' => [[$object, $object]], + yield 'not unique booleans' => [[true, true], 'property.path[1]'], + yield 'not unique integers' => [[1, 2, 3, 3], 'property.path[3]'], + yield 'not unique floats' => [[0.1, 0.2, 0.1], 'property.path[2]'], + yield 'not unique string' => [['a', 'b', 'a'], 'property.path[2]'], + yield 'not unique arrays' => [[[1, 1], [2, 3], [1, 1]], 'property.path[2]'], + yield 'not unique objects' => [[$object, $object], 'property.path[1]'], ]; } @@ -96,6 +97,7 @@ public function testInvalidValueNamed() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[3]') ->assertRaised(); } @@ -152,6 +154,7 @@ public function testExpectsNonUniqueObjects($callback) $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[2]') ->assertRaised(); } @@ -176,6 +179,7 @@ public function testExpectsInvalidNonStrictComparison() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[1]') ->assertRaised(); } @@ -202,6 +206,7 @@ public function testExpectsInvalidCaseInsensitiveComparison() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[1]') ->assertRaised(); } @@ -246,7 +251,7 @@ public static function getInvalidFieldNames(): array /** * @dataProvider getInvalidCollectionValues */ - public function testInvalidCollectionValues(array $value, array $fields) + public function testInvalidCollectionValues(array $value, array $fields, string $expectedErrorPath) { $this->validator->validate($value, new Unique([ 'message' => 'myMessage', @@ -255,6 +260,7 @@ public function testInvalidCollectionValues(array $value, array $fields) $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath($expectedErrorPath) ->assertRaised(); } @@ -264,23 +270,25 @@ public static function getInvalidCollectionValues(): array 'unique string' => [[ ['lang' => 'eng', 'translation' => 'hi'], ['lang' => 'eng', 'translation' => 'hello'], - ], ['lang']], + ], ['lang'], 'property.path[1]'], 'unique floats' => [[ ['latitude' => 51.509865, 'longitude' => -0.118092, 'poi' => 'capital'], ['latitude' => 52.520008, 'longitude' => 13.404954], ['latitude' => 51.509865, 'longitude' => -0.118092], - ], ['latitude', 'longitude']], + ], ['latitude', 'longitude'], 'property.path[2]'], 'unique int' => [[ ['id' => 1, 'email' => 'bar@email.com'], ['id' => 1, 'email' => 'foo@email.com'], - ], ['id']], + ], ['id'], 'property.path[1]'], 'unique null' => [ [null, null], [], + 'property.path[1]', ], 'unique field null' => [ [['nullField' => null], ['nullField' => null]], ['nullField'], + 'property.path[1]', ], ]; } @@ -308,6 +316,90 @@ public function testArrayOfObjectsUnique() $this->assertNoViolation(); } + public function testErrorPath() + { + $array = [ + new DummyClassOne(), + new DummyClassOne(), + new DummyClassOne(), + ]; + + $array[0]->code = 'a1'; + $array[1]->code = 'a2'; + $array[2]->code = 'a1'; + + $this->validator->validate( + $array, + new Unique( + normalizer: [self::class, 'normalizeDummyClassOne'], + fields: 'code', + errorPath: 'code', + ) + ); + + $this->buildViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', 'array') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[2].code') + ->assertRaised(); + } + + public function testErrorPathWithIteratorAggregate() + { + $array = new \ArrayObject([ + new DummyClassOne(), + new DummyClassOne(), + new DummyClassOne(), + ]); + + $array[0]->code = 'a1'; + $array[1]->code = 'a2'; + $array[2]->code = 'a1'; + + $this->validator->validate( + $array, + new Unique( + normalizer: [self::class, 'normalizeDummyClassOne'], + fields: 'code', + errorPath: 'code', + ) + ); + + $this->buildViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', 'object') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[2].code') + ->assertRaised(); + } + + public function testErrorPathWithNonList() + { + $array = [ + 'a' => new DummyClassOne(), + 'b' => new DummyClassOne(), + 'c' => new DummyClassOne(), + ]; + + $array['a']->code = 'a1'; + $array['b']->code = 'a2'; + $array['c']->code = 'a1'; + + $this->validator->validate( + $array, + new Unique( + normalizer: [self::class, 'normalizeDummyClassOne'], + fields: 'code', + errorPath: 'code', + ) + ); + + $this->buildViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', 'array') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[c].code') + ->assertRaised(); + } + public static function normalizeDummyClassOne(DummyClassOne $obj): array { return [ From 2050a6a1f5b51adb6154a78db934767f609a8817 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 060/149] Prefix all sprintf() calls --- Command/DebugCommand.php | 4 ++-- Constraint.php | 16 ++++++++-------- ConstraintViolationList.php | 2 +- Constraints/AbstractComparison.php | 6 +++--- Constraints/AbstractComparisonValidator.php | 4 ++-- Constraints/Bic.php | 2 +- Constraints/BicValidator.php | 2 +- Constraints/CallbackValidator.php | 2 +- Constraints/Cascade.php | 2 +- Constraints/Charset.php | 2 +- Constraints/Cidr.php | 6 +++--- Constraints/Composite.php | 6 +++--- Constraints/Compound.php | 2 +- Constraints/Count.php | 2 +- Constraints/CssColor.php | 4 ++-- Constraints/DisableAutoMapping.php | 2 +- Constraints/DivisibleByValidator.php | 4 ++-- Constraints/Email.php | 4 ++-- Constraints/EmailValidator.php | 4 ++-- Constraints/EnableAutoMapping.php | 2 +- Constraints/Expression.php | 2 +- Constraints/ExpressionLanguageProvider.php | 2 +- Constraints/File.php | 2 +- Constraints/ImageValidator.php | 16 ++++++++-------- Constraints/Ip.php | 4 ++-- Constraints/Length.php | 6 +++--- Constraints/MacAddress.php | 2 +- Constraints/NotBlank.php | 2 +- Constraints/NotCompromisedPasswordValidator.php | 4 ++-- Constraints/PasswordStrength.php | 2 +- Constraints/Range.php | 10 +++++----- Constraints/RangeValidator.php | 6 +++--- Constraints/Regex.php | 2 +- Constraints/Traverse.php | 2 +- Constraints/Unique.php | 2 +- Constraints/Url.php | 2 +- Constraints/UrlValidator.php | 2 +- Constraints/Uuid.php | 2 +- Constraints/When.php | 2 +- Constraints/WhenValidator.php | 2 +- Constraints/ZeroComparisonConstraintTrait.php | 4 ++-- ContainerConstraintValidatorFactory.php | 2 +- .../AddAutoMappingConfigurationPass.php | 2 +- Exception/UnexpectedTypeException.php | 2 +- Mapping/ClassMetadata.php | 8 ++++---- Mapping/Factory/LazyLoadingMetadataFactory.php | 4 ++-- Mapping/GenericMetadata.php | 2 +- Mapping/GetterMetadata.php | 4 ++-- Mapping/Loader/AbstractLoader.php | 2 +- Mapping/Loader/AttributeLoader.php | 2 +- Mapping/Loader/FileLoader.php | 6 +++--- Mapping/Loader/LoaderChain.php | 2 +- Mapping/Loader/StaticMethodLoader.php | 2 +- Mapping/Loader/YamlFileLoader.php | 4 ++-- Mapping/MemberMetadata.php | 2 +- Mapping/PropertyMetadata.php | 4 ++-- Test/ConstraintValidatorTestCase.php | 6 +++--- .../AbstractComparisonValidatorTestCase.php | 6 +++--- Tests/Constraints/AtLeastOneOfValidatorTest.php | 6 +++--- Tests/Constraints/BicValidatorTest.php | 2 +- Tests/Constraints/CidrTest.php | 4 ++-- Tests/Constraints/DisableAutoMappingTest.php | 2 +- Tests/Constraints/DivisibleByValidatorTest.php | 2 +- Tests/Constraints/EmailValidatorTest.php | 2 +- Tests/Constraints/EnableAutoMappingTest.php | 2 +- Tests/Constraints/HostnameValidatorTest.php | 4 ++-- Tests/Constraints/ImageValidatorTest.php | 4 ++-- Tests/Constraints/LengthTest.php | 2 +- Tests/Constraints/TimezoneValidatorTest.php | 8 ++++---- Tests/Constraints/UniqueValidatorTest.php | 2 +- Tests/Resources/TranslationFilesTest.php | 4 ++-- Validator/RecursiveContextualValidator.php | 14 +++++++------- 72 files changed, 137 insertions(+), 137 deletions(-) diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index acff8cbab..70b508d09 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -74,7 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } catch (DirectoryNotFoundException) { $io = new SymfonyStyle($input, $output); - $io->error(sprintf('Neither class nor path were found with "%s" argument.', $input->getArgument('class'))); + $io->error(\sprintf('Neither class nor path were found with "%s" argument.', $input->getArgument('class'))); return 1; } @@ -85,7 +85,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function dumpValidatorsForClass(InputInterface $input, OutputInterface $output, string $class): void { $io = new SymfonyStyle($input, $output); - $title = sprintf('%s', $class); + $title = \sprintf('%s', $class); $rows = []; $dump = new Dumper($output); diff --git a/Constraint.php b/Constraint.php index 7b65406d2..e04dc81e1 100644 --- a/Constraint.php +++ b/Constraint.php @@ -74,7 +74,7 @@ public static function getErrorName(string $errorCode): string return static::ERROR_NAMES[$errorCode]; } - throw new InvalidArgumentException(sprintf('The error code "%s" does not exist for constraint of type "%s".', $errorCode, static::class)); + throw new InvalidArgumentException(\sprintf('The error code "%s" does not exist for constraint of type "%s".', $errorCode, static::class)); } /** @@ -134,7 +134,7 @@ protected function normalizeOptions(mixed $options): array if (\is_array($options) && isset($options['value']) && !property_exists($this, 'value')) { if (null === $defaultOption) { - throw new ConstraintDefinitionException(sprintf('No default option is configured for constraint "%s".', static::class)); + throw new ConstraintDefinitionException(\sprintf('No default option is configured for constraint "%s".', static::class)); } $options[$defaultOption] = $options['value']; @@ -155,7 +155,7 @@ protected function normalizeOptions(mixed $options): array } } elseif (null !== $options && !(\is_array($options) && 0 === \count($options))) { if (null === $defaultOption) { - throw new ConstraintDefinitionException(sprintf('No default option is configured for constraint "%s".', static::class)); + throw new ConstraintDefinitionException(\sprintf('No default option is configured for constraint "%s".', static::class)); } if (\array_key_exists($defaultOption, $knownOptions)) { @@ -167,11 +167,11 @@ protected function normalizeOptions(mixed $options): array } if (\count($invalidOptions) > 0) { - throw new InvalidOptionsException(sprintf('The options "%s" do not exist in constraint "%s".', implode('", "', $invalidOptions), static::class), $invalidOptions); + throw new InvalidOptionsException(\sprintf('The options "%s" do not exist in constraint "%s".', implode('", "', $invalidOptions), static::class), $invalidOptions); } if (\count($missingOptions) > 0) { - throw new MissingOptionsException(sprintf('The options "%s" must be set for constraint "%s".', implode('", "', array_keys($missingOptions)), static::class), array_keys($missingOptions)); + throw new MissingOptionsException(\sprintf('The options "%s" must be set for constraint "%s".', implode('", "', array_keys($missingOptions)), static::class), array_keys($missingOptions)); } return $normalizedOptions; @@ -194,7 +194,7 @@ public function __set(string $option, mixed $value): void return; } - throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint "%s".', $option, static::class), [$option]); + throw new InvalidOptionsException(\sprintf('The option "%s" does not exist in constraint "%s".', $option, static::class), [$option]); } /** @@ -214,7 +214,7 @@ public function __get(string $option): mixed return $this->groups; } - throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint "%s".', $option, static::class), [$option]); + throw new InvalidOptionsException(\sprintf('The option "%s" does not exist in constraint "%s".', $option, static::class), [$option]); } public function __isset(string $option): bool @@ -228,7 +228,7 @@ public function __isset(string $option): bool public function addImplicitGroupName(string $group): void { if (null === $this->groups && \array_key_exists('groups', (array) $this)) { - throw new \LogicException(sprintf('"%s::$groups" is set to null. Did you forget to call "%s::__construct()"?', static::class, self::class)); + throw new \LogicException(\sprintf('"%s::$groups" is set to null. Did you forget to call "%s::__construct()"?', static::class, self::class)); } if (\in_array(self::DEFAULT_GROUP, $this->groups) && !\in_array($group, $this->groups, true)) { diff --git a/ConstraintViolationList.php b/ConstraintViolationList.php index 96a93d583..a67b53389 100644 --- a/ConstraintViolationList.php +++ b/ConstraintViolationList.php @@ -73,7 +73,7 @@ public function addAll(ConstraintViolationListInterface $otherList): void public function get(int $offset): ConstraintViolationInterface { if (!isset($this->violations[$offset])) { - throw new OutOfBoundsException(sprintf('The offset "%s" does not exist.', $offset)); + throw new OutOfBoundsException(\sprintf('The offset "%s" does not exist.', $offset)); } return $this->violations[$offset]; diff --git a/Constraints/AbstractComparison.php b/Constraints/AbstractComparison.php index 5eb8c4696..4d34b1401 100644 --- a/Constraints/AbstractComparison.php +++ b/Constraints/AbstractComparison.php @@ -42,15 +42,15 @@ public function __construct(mixed $value = null, ?string $propertyPath = null, ? $this->propertyPath = $propertyPath ?? $this->propertyPath; if (null === $this->value && null === $this->propertyPath) { - throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires either the "value" or "propertyPath" option to be set.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires either the "value" or "propertyPath" option to be set.', static::class)); } if (null !== $this->value && null !== $this->propertyPath) { - throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires only one of the "value" or "propertyPath" options to be set, not both.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires only one of the "value" or "propertyPath" options to be set, not both.', static::class)); } if (null !== $this->propertyPath && !class_exists(PropertyAccess::class)) { - throw new LogicException(sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "propertyPath" option. Try running "composer require symfony/property-access".', static::class)); + throw new LogicException(\sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "propertyPath" option. Try running "composer require symfony/property-access".', static::class)); } } diff --git a/Constraints/AbstractComparisonValidator.php b/Constraints/AbstractComparisonValidator.php index 941b1bef6..94c789ae6 100644 --- a/Constraints/AbstractComparisonValidator.php +++ b/Constraints/AbstractComparisonValidator.php @@ -52,7 +52,7 @@ public function validate(mixed $value, Constraint $constraint): void try { $comparedValue = $this->getPropertyAccessor()->getValue($object, $path); } catch (NoSuchPropertyException $e) { - throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e); + throw new ConstraintDefinitionException(\sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e); } } else { $comparedValue = $constraint->value; @@ -65,7 +65,7 @@ public function validate(mixed $value, Constraint $constraint): void try { $comparedValue = new $value($comparedValue); } catch (\Exception) { - throw new ConstraintDefinitionException(sprintf('The compared value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $comparedValue, get_debug_type($value), get_debug_type($constraint))); + throw new ConstraintDefinitionException(\sprintf('The compared value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $comparedValue, get_debug_type($value), get_debug_type($constraint))); } } diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 22640bedf..c0974c918 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -100,7 +100,7 @@ public function __construct( } 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. Try running "composer require symfony/property-access".', self::class)); + throw new LogicException(\sprintf('The "symfony/property-access" component is required to use the "%s" constraint with the "ibanPropertyPath" option. Try running "composer require symfony/property-access".', self::class)); } } } diff --git a/Constraints/BicValidator.php b/Constraints/BicValidator.php index d0d1b5084..2e299e8f6 100644 --- a/Constraints/BicValidator.php +++ b/Constraints/BicValidator.php @@ -129,7 +129,7 @@ public function validate(mixed $value, Constraint $constraint): void try { $iban = $this->getPropertyAccessor()->getValue($object, $path); } catch (NoSuchPropertyException $e) { - throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e); + throw new ConstraintDefinitionException(\sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e); } } if (!$iban) { diff --git a/Constraints/CallbackValidator.php b/Constraints/CallbackValidator.php index 417586c15..d2ae8e32d 100644 --- a/Constraints/CallbackValidator.php +++ b/Constraints/CallbackValidator.php @@ -43,7 +43,7 @@ public function validate(mixed $object, Constraint $constraint): void $method($object, $this->context, $constraint->payload); } elseif (null !== $object) { if (!method_exists($object, $method)) { - throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist in class "%s".', $method, get_debug_type($object))); + throw new ConstraintDefinitionException(\sprintf('Method "%s" targeted by Callback constraint does not exist in class "%s".', $method, get_debug_type($object))); } $reflMethod = new \ReflectionMethod($object, $method); diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index a1e7ccfa5..9575f37d6 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -37,7 +37,7 @@ public function __construct(array|string|null $exclude = null, ?array $options = } if (\is_array($options) && \array_key_exists('groups', $options)) { - throw new ConstraintDefinitionException(sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); + throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } parent::__construct($options); diff --git a/Constraints/Charset.php b/Constraints/Charset.php index 8b1d482ca..aa22c503a 100644 --- a/Constraints/Charset.php +++ b/Constraints/Charset.php @@ -37,7 +37,7 @@ public function __construct( parent::__construct(null, $groups, $payload); if ([] === $this->encodings) { - throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires at least one encoding.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires at least one encoding.', static::class)); } } } diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index 98617c557..349c29b66 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -87,7 +87,7 @@ public function __construct( $this->version = $version ?? $options['version'] ?? $this->version; if (!\array_key_exists($this->version, self::NET_MAXES)) { - throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s".', implode('", "', array_keys(self::NET_MAXES)))); + throw new ConstraintDefinitionException(\sprintf('The option "version" must be one of "%s".', implode('", "', array_keys(self::NET_MAXES)))); } $this->netmaskMin = $netmaskMin ?? $options['netmaskMin'] ?? $this->netmaskMin; @@ -98,11 +98,11 @@ public function __construct( unset($options['netmaskMin'], $options['netmaskMax'], $options['version']); if ($this->netmaskMin < 0 || $this->netmaskMax > self::NET_MAXES[$this->version] || $this->netmaskMin > $this->netmaskMax) { - throw new ConstraintDefinitionException(sprintf('The netmask range must be between 0 and %d.', self::NET_MAXES[$this->version])); + throw new ConstraintDefinitionException(\sprintf('The netmask range must be between 0 and %d.', self::NET_MAXES[$this->version])); } if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Composite.php b/Constraints/Composite.php index b62aca91e..8cd0edde7 100644 --- a/Constraints/Composite.php +++ b/Constraints/Composite.php @@ -69,11 +69,11 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed $constraint = $constraint::class; } - throw new ConstraintDefinitionException(sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, static::class)); + throw new ConstraintDefinitionException(\sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, static::class)); } if ($constraint instanceof Valid) { - throw new ConstraintDefinitionException(sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', static::class)); } } @@ -98,7 +98,7 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed $excessGroups = array_diff($constraint->groups, $this->groups); if (\count($excessGroups) > 0) { - throw new ConstraintDefinitionException(sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), static::class)); + throw new ConstraintDefinitionException(\sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), static::class)); } } else { $constraint->groups = $this->groups; diff --git a/Constraints/Compound.php b/Constraints/Compound.php index 4e7f7cd60..2862bca3f 100644 --- a/Constraints/Compound.php +++ b/Constraints/Compound.php @@ -27,7 +27,7 @@ abstract class Compound extends Composite public function __construct(mixed $options = null) { if (isset($options[$this->getCompositeOption()])) { - throw new ConstraintDefinitionException(sprintf('You can\'t redefine the "%s" option. Use the "%s::getConstraints()" method instead.', $this->getCompositeOption(), __CLASS__)); + throw new ConstraintDefinitionException(\sprintf('You can\'t redefine the "%s" option. Use the "%s::getConstraints()" method instead.', $this->getCompositeOption(), __CLASS__)); } $this->constraints = $this->getConstraints($this->normalizeOptions($options)); diff --git a/Constraints/Count.php b/Constraints/Count.php index 3e1bf1e6b..a63d077d5 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -88,7 +88,7 @@ public function __construct( $this->divisibleByMessage = $divisibleByMessage ?? $this->divisibleByMessage; if (null === $this->min && null === $this->max && null === $this->divisibleBy) { - throw new MissingOptionsException(sprintf('Either option "min", "max" or "divisibleBy" must be given for constraint "%s".', __CLASS__), ['min', 'max', 'divisibleBy']); + throw new MissingOptionsException(\sprintf('Either option "min", "max" or "divisibleBy" must be given for constraint "%s".', __CLASS__), ['min', 'max', 'divisibleBy']); } } } diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 5e67d157b..538362a98 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -76,13 +76,13 @@ public function __construct(array|string $formats = [], ?string $message = null, $options = array_merge($formats, $options ?? []); } elseif (\is_array($formats)) { if ([] === array_intersect(self::$validationModes, $formats)) { - throw new InvalidArgumentException(sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString)); + throw new InvalidArgumentException(\sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString)); } $options['value'] = $formats; } elseif (\is_string($formats)) { if (!\in_array($formats, self::$validationModes, true)) { - throw new InvalidArgumentException(sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString)); + throw new InvalidArgumentException(\sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString)); } $options['value'] = [$formats]; diff --git a/Constraints/DisableAutoMapping.php b/Constraints/DisableAutoMapping.php index 5dedddaa4..2b762059f 100644 --- a/Constraints/DisableAutoMapping.php +++ b/Constraints/DisableAutoMapping.php @@ -31,7 +31,7 @@ class DisableAutoMapping extends Constraint 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__)); + throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } parent::__construct($options); diff --git a/Constraints/DivisibleByValidator.php b/Constraints/DivisibleByValidator.php index 03c0a95b0..d86875884 100644 --- a/Constraints/DivisibleByValidator.php +++ b/Constraints/DivisibleByValidator.php @@ -43,10 +43,10 @@ protected function compareValues(mixed $value1, mixed $value2): bool $quotient = $value1 / $value2; $rounded = round($quotient); - return sprintf('%.12e', $quotient) === sprintf('%.12e', $rounded); + return \sprintf('%.12e', $quotient) === \sprintf('%.12e', $rounded); } - return sprintf('%.12e', $value2) === sprintf('%.12e', $remainder); + return \sprintf('%.12e', $value2) === \sprintf('%.12e', $remainder); } protected function getErrorCode(): ?string diff --git a/Constraints/Email.php b/Constraints/Email.php index bba04efb3..7b9b9ba2b 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -73,11 +73,11 @@ public function __construct( $this->normalizer = $normalizer ?? $this->normalizer; if (self::VALIDATION_MODE_STRICT === $this->mode && !class_exists(StrictEmailValidator::class)) { - throw new LogicException(sprintf('The "egulias/email-validator" component is required to use the "%s" constraint in strict mode. Try running "composer require egulias/email-validator".', __CLASS__)); + throw new LogicException(\sprintf('The "egulias/email-validator" component is required to use the "%s" constraint in strict mode. Try running "composer require egulias/email-validator".', __CLASS__)); } if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } } diff --git a/Constraints/EmailValidator.php b/Constraints/EmailValidator.php index 9b751811d..b6732775d 100644 --- a/Constraints/EmailValidator.php +++ b/Constraints/EmailValidator.php @@ -70,14 +70,14 @@ public function validate(mixed $value, Constraint $constraint): void if (null === $constraint->mode) { if (Email::VALIDATION_MODE_STRICT === $this->defaultMode && !class_exists(EguliasEmailValidator::class)) { - throw new LogicException(sprintf('The "egulias/email-validator" component is required to make the "%s" constraint default to strict mode. Try running "composer require egulias/email-validator".', Email::class)); + throw new LogicException(\sprintf('The "egulias/email-validator" component is required to make the "%s" constraint default to strict mode. Try running "composer require egulias/email-validator".', Email::class)); } $constraint->mode = $this->defaultMode; } if (!\in_array($constraint->mode, Email::VALIDATION_MODES, true)) { - throw new InvalidArgumentException(sprintf('The "%s::$mode" parameter value is not valid.', get_debug_type($constraint))); + throw new InvalidArgumentException(\sprintf('The "%s::$mode" parameter value is not valid.', get_debug_type($constraint))); } if (Email::VALIDATION_MODE_STRICT === $constraint->mode) { diff --git a/Constraints/EnableAutoMapping.php b/Constraints/EnableAutoMapping.php index 56327e3a2..a4808d08c 100644 --- a/Constraints/EnableAutoMapping.php +++ b/Constraints/EnableAutoMapping.php @@ -31,7 +31,7 @@ class EnableAutoMapping extends Constraint 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__)); + throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } parent::__construct($options); diff --git a/Constraints/Expression.php b/Constraints/Expression.php index aa4ee7785..6f624807c 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -55,7 +55,7 @@ public function __construct( ?bool $negate = null, ) { if (!class_exists(ExpressionLanguage::class)) { - throw new LogicException(sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); + throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); } if (\is_array($expression)) { diff --git a/Constraints/ExpressionLanguageProvider.php b/Constraints/ExpressionLanguageProvider.php index b491649a1..0a3727503 100644 --- a/Constraints/ExpressionLanguageProvider.php +++ b/Constraints/ExpressionLanguageProvider.php @@ -20,7 +20,7 @@ public function getFunctions(): array { return [ new ExpressionFunction('is_valid', function (...$arguments) { - return sprintf( + return \sprintf( '0 === $context->getValidator()->inContext($context)->validate(%s)->getViolations()->count()', implode(', ', $arguments) ); diff --git a/Constraints/File.php b/Constraints/File.php index 86b3b2802..089253bc9 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -190,7 +190,7 @@ private function normalizeBinaryFormat(int|string $maxSize): void $this->maxSize = $matches[1] * $factors[$unit = strtolower($matches[2])]; $this->binaryFormat ??= 2 === \strlen($unit); } else { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size.', $maxSize)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum size.', $maxSize)); } } } diff --git a/Constraints/ImageValidator.php b/Constraints/ImageValidator.php index eaf13fcbc..a715471d9 100644 --- a/Constraints/ImageValidator.php +++ b/Constraints/ImageValidator.php @@ -65,7 +65,7 @@ public function validate(mixed $value, Constraint $constraint): void if ($constraint->minWidth) { if (!ctype_digit((string) $constraint->minWidth)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum width.', $constraint->minWidth)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum width.', $constraint->minWidth)); } if ($width < $constraint->minWidth) { @@ -81,7 +81,7 @@ public function validate(mixed $value, Constraint $constraint): void if ($constraint->maxWidth) { if (!ctype_digit((string) $constraint->maxWidth)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum width.', $constraint->maxWidth)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum width.', $constraint->maxWidth)); } if ($width > $constraint->maxWidth) { @@ -97,7 +97,7 @@ public function validate(mixed $value, Constraint $constraint): void if ($constraint->minHeight) { if (!ctype_digit((string) $constraint->minHeight)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum height.', $constraint->minHeight)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum height.', $constraint->minHeight)); } if ($height < $constraint->minHeight) { @@ -113,7 +113,7 @@ public function validate(mixed $value, Constraint $constraint): void if ($constraint->maxHeight) { if (!ctype_digit((string) $constraint->maxHeight)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum height.', $constraint->maxHeight)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum height.', $constraint->maxHeight)); } if ($height > $constraint->maxHeight) { @@ -129,7 +129,7 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->minPixels) { if (!ctype_digit((string) $constraint->minPixels)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum amount of pixels.', $constraint->minPixels)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum amount of pixels.', $constraint->minPixels)); } if ($pixels < $constraint->minPixels) { @@ -145,7 +145,7 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->maxPixels) { if (!ctype_digit((string) $constraint->maxPixels)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum amount of pixels.', $constraint->maxPixels)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum amount of pixels.', $constraint->maxPixels)); } if ($pixels > $constraint->maxPixels) { @@ -163,7 +163,7 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->minRatio) { if (!is_numeric((string) $constraint->minRatio)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum ratio.', $constraint->minRatio)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum ratio.', $constraint->minRatio)); } if ($ratio < round($constraint->minRatio, 2)) { @@ -177,7 +177,7 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->maxRatio) { if (!is_numeric((string) $constraint->maxRatio)) { - throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum ratio.', $constraint->maxRatio)); + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum ratio.', $constraint->maxRatio)); } if ($ratio > round($constraint->maxRatio, 2)) { diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 4a8b98021..97743030d 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -126,11 +126,11 @@ public function __construct( $this->normalizer = $normalizer ?? $this->normalizer; if (!\in_array($this->version, static::VERSIONS, true)) { - throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s".', implode('", "', static::VERSIONS))); + throw new ConstraintDefinitionException(\sprintf('The option "version" must be one of "%s".', implode('", "', static::VERSIONS))); } if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } } diff --git a/Constraints/Length.php b/Constraints/Length.php index ebbe4750f..233857760 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -109,15 +109,15 @@ public function __construct( $this->charsetMessage = $charsetMessage ?? $this->charsetMessage; if (null === $this->min && null === $this->max) { - throw new MissingOptionsException(sprintf('Either option "min" or "max" must be given for constraint "%s".', __CLASS__), ['min', 'max']); + throw new MissingOptionsException(\sprintf('Either option "min" or "max" must be given for constraint "%s".', __CLASS__), ['min', 'max']); } if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } if (!\in_array($this->countUnit, self::VALID_COUNT_UNITS, true)) { - throw new InvalidArgumentException(sprintf('The "countUnit" option must be one of the "%s"::COUNT_* constants ("%s" given).', __CLASS__, $this->countUnit)); + throw new InvalidArgumentException(\sprintf('The "countUnit" option must be one of the "%s"::COUNT_* constants ("%s" given).', __CLASS__, $this->countUnit)); } } } diff --git a/Constraints/MacAddress.php b/Constraints/MacAddress.php index 12c41a247..566495b38 100644 --- a/Constraints/MacAddress.php +++ b/Constraints/MacAddress.php @@ -77,7 +77,7 @@ public function __construct( parent::__construct(null, $groups, $payload); if (!\in_array($this->type, self::TYPES, true)) { - throw new ConstraintDefinitionException(sprintf('The option "type" must be one of "%s".', implode('", "', self::TYPES))); + throw new ConstraintDefinitionException(\sprintf('The option "type" must be one of "%s".', implode('", "', self::TYPES))); } $this->normalizer = null !== $normalizer ? $normalizer(...) : null; diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index 96b49a302..db3602615 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -48,7 +48,7 @@ public function __construct(?array $options = null, ?string $message = null, ?bo $this->normalizer = $normalizer ?? $this->normalizer; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } } diff --git a/Constraints/NotCompromisedPasswordValidator.php b/Constraints/NotCompromisedPasswordValidator.php index b16e97ce5..3b1d25b4c 100644 --- a/Constraints/NotCompromisedPasswordValidator.php +++ b/Constraints/NotCompromisedPasswordValidator.php @@ -40,7 +40,7 @@ class NotCompromisedPasswordValidator extends ConstraintValidator public function __construct(?HttpClientInterface $httpClient = null, string $charset = 'UTF-8', bool $enabled = true, ?string $endpoint = null) { if (null === $httpClient && !class_exists(HttpClient::class)) { - throw new LogicException(sprintf('The "%s" class requires the "HttpClient" component. Try running "composer require symfony/http-client".', self::class)); + throw new LogicException(\sprintf('The "%s" class requires the "HttpClient" component. Try running "composer require symfony/http-client".', self::class)); } $this->httpClient = $httpClient ?? HttpClient::create(); @@ -77,7 +77,7 @@ public function validate(mixed $value, Constraint $constraint): void $hash = strtoupper(sha1($value)); $hashPrefix = substr($hash, 0, 5); - $url = sprintf($this->endpoint, $hashPrefix); + $url = \sprintf($this->endpoint, $hashPrefix); try { $result = $this->httpClient->request('GET', $url, ['headers' => ['Add-Padding' => 'true']])->getContent(); diff --git a/Constraints/PasswordStrength.php b/Constraints/PasswordStrength.php index 1827c5fb4..42a93c530 100644 --- a/Constraints/PasswordStrength.php +++ b/Constraints/PasswordStrength.php @@ -53,7 +53,7 @@ public function __construct(?array $options = null, ?int $minScore = null, ?arra $this->message = $message ?? $this->message; if ($this->minScore < 1 || 4 < $this->minScore) { - throw new ConstraintDefinitionException(sprintf('The parameter "minScore" of the "%s" constraint must be an integer between 1 and 4.', self::class)); + throw new ConstraintDefinitionException(\sprintf('The parameter "minScore" of the "%s" constraint must be an integer between 1 and 4.', self::class)); } } } diff --git a/Constraints/Range.php b/Constraints/Range.php index a8c7271c4..ab274704c 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -84,23 +84,23 @@ public function __construct( $this->maxPropertyPath = $maxPropertyPath ?? $this->maxPropertyPath; if (null === $this->min && null === $this->minPropertyPath && null === $this->max && null === $this->maxPropertyPath) { - throw new MissingOptionsException(sprintf('Either option "min", "minPropertyPath", "max" or "maxPropertyPath" must be given for constraint "%s".', __CLASS__), ['min', 'minPropertyPath', 'max', 'maxPropertyPath']); + throw new MissingOptionsException(\sprintf('Either option "min", "minPropertyPath", "max" or "maxPropertyPath" must be given for constraint "%s".', __CLASS__), ['min', 'minPropertyPath', 'max', 'maxPropertyPath']); } if (null !== $this->min && null !== $this->minPropertyPath) { - throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires only one of the "min" or "minPropertyPath" options to be set, not both.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires only one of the "min" or "minPropertyPath" options to be set, not both.', static::class)); } if (null !== $this->max && null !== $this->maxPropertyPath) { - throw new ConstraintDefinitionException(sprintf('The "%s" constraint requires only one of the "max" or "maxPropertyPath" options to be set, not both.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires only one of the "max" or "maxPropertyPath" options to be set, not both.', static::class)); } if ((null !== $this->minPropertyPath || null !== $this->maxPropertyPath) && !class_exists(PropertyAccess::class)) { - throw new LogicException(sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "minPropertyPath" or "maxPropertyPath" option. Try running "composer require symfony/property-access".', static::class)); + throw new LogicException(\sprintf('The "%s" constraint requires the Symfony PropertyAccess component to use the "minPropertyPath" or "maxPropertyPath" option. Try running "composer require symfony/property-access".', static::class)); } if (null !== $this->min && null !== $this->max && ($minMessage || $maxMessage || isset($options['minMessage']) || isset($options['maxMessage']))) { - throw new ConstraintDefinitionException(sprintf('The "%s" constraint can not use "minMessage" and "maxMessage" when the "min" and "max" options are both set. Use "notInRangeMessage" instead.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint can not use "minMessage" and "maxMessage" when the "min" and "max" options are both set. Use "notInRangeMessage" instead.', static::class)); } } } diff --git a/Constraints/RangeValidator.php b/Constraints/RangeValidator.php index 717c5d7f3..1f359c528 100644 --- a/Constraints/RangeValidator.php +++ b/Constraints/RangeValidator.php @@ -69,7 +69,7 @@ public function validate(mixed $value, Constraint $constraint): void try { $min = new $value($min); } catch (\Exception) { - throw new ConstraintDefinitionException(sprintf('The min value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $min, get_debug_type($value), get_debug_type($constraint))); + throw new ConstraintDefinitionException(\sprintf('The min value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $min, get_debug_type($value), get_debug_type($constraint))); } } @@ -77,7 +77,7 @@ public function validate(mixed $value, Constraint $constraint): void try { $max = new $value($max); } catch (\Exception) { - throw new ConstraintDefinitionException(sprintf('The max value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $max, get_debug_type($value), get_debug_type($constraint))); + throw new ConstraintDefinitionException(\sprintf('The max value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $max, get_debug_type($value), get_debug_type($constraint))); } } } @@ -158,7 +158,7 @@ private function getLimit(?string $propertyPath, mixed $default, Constraint $con try { return $this->getPropertyAccessor()->getValue($object, $propertyPath); } catch (NoSuchPropertyException $e) { - throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: ', $propertyPath, get_debug_type($constraint)).$e->getMessage(), 0, $e); + throw new ConstraintDefinitionException(\sprintf('Invalid property path "%s" provided to "%s" constraint: ', $propertyPath, get_debug_type($constraint)).$e->getMessage(), 0, $e); } } diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 1ce86eec3..7cd8301f7 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -66,7 +66,7 @@ public function __construct( $this->normalizer = $normalizer ?? $this->normalizer; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index 8f999adee..80c7b2d31 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -30,7 +30,7 @@ class Traverse extends Constraint public function __construct(bool|array|null $traverse = null) { if (\is_array($traverse) && \array_key_exists('groups', $traverse)) { - throw new ConstraintDefinitionException(sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); + throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } parent::__construct($traverse); diff --git a/Constraints/Unique.php b/Constraints/Unique.php index 7e4d6f626..6e68e2c3a 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -57,7 +57,7 @@ public function __construct( $this->errorPath = $errorPath ?? $this->errorPath; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } } diff --git a/Constraints/Url.php b/Constraints/Url.php index 9481f9395..5dce9106b 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -68,7 +68,7 @@ public function __construct( $this->requireTld = $requireTld ?? $this->requireTld; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } } diff --git a/Constraints/UrlValidator.php b/Constraints/UrlValidator.php index fb342a6fa..70c562731 100644 --- a/Constraints/UrlValidator.php +++ b/Constraints/UrlValidator.php @@ -69,7 +69,7 @@ public function validate(mixed $value, Constraint $constraint): void } $pattern = $constraint->relativeProtocol ? str_replace('(%s):', '(?:(%s):)?', static::PATTERN) : static::PATTERN; - $pattern = sprintf($pattern, implode('|', $constraint->protocols)); + $pattern = \sprintf($pattern, implode('|', $constraint->protocols)); if (!preg_match($pattern, $value)) { $this->context->buildViolation($constraint->message) diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index 1b028f1a9..f7b517720 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -117,7 +117,7 @@ public function __construct( $this->normalizer = $normalizer ?? $this->normalizer; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { - throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); + throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); } } } diff --git a/Constraints/When.php b/Constraints/When.php index 52121c41c..10b5aa3c7 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -38,7 +38,7 @@ class When extends Composite public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, array $options = []) { if (!class_exists(ExpressionLanguage::class)) { - throw new LogicException(sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); + throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); } if (\is_array($expression)) { diff --git a/Constraints/WhenValidator.php b/Constraints/WhenValidator.php index 00d1d9c91..c02a450e8 100644 --- a/Constraints/WhenValidator.php +++ b/Constraints/WhenValidator.php @@ -43,7 +43,7 @@ public function validate(mixed $value, Constraint $constraint): void private function getExpressionLanguage(): ExpressionLanguage { if (!class_exists(ExpressionLanguage::class)) { - throw new LogicException(sprintf('The "symfony/expression-language" component is required to use the "%s" validator. Try running "composer require symfony/expression-language".', __CLASS__)); + throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" validator. Try running "composer require symfony/expression-language".', __CLASS__)); } return $this->expressionLanguage ??= new ExpressionLanguage(); diff --git a/Constraints/ZeroComparisonConstraintTrait.php b/Constraints/ZeroComparisonConstraintTrait.php index 4c816474f..78fab1f54 100644 --- a/Constraints/ZeroComparisonConstraintTrait.php +++ b/Constraints/ZeroComparisonConstraintTrait.php @@ -26,11 +26,11 @@ public function __construct(?array $options = null, ?string $message = null, ?ar $options ??= []; if (isset($options['propertyPath'])) { - throw new ConstraintDefinitionException(sprintf('The "propertyPath" option of the "%s" constraint cannot be set.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "propertyPath" option of the "%s" constraint cannot be set.', static::class)); } if (isset($options['value'])) { - throw new ConstraintDefinitionException(sprintf('The "value" option of the "%s" constraint cannot be set.', static::class)); + throw new ConstraintDefinitionException(\sprintf('The "value" option of the "%s" constraint cannot be set.', static::class)); } parent::__construct(0, null, $message, $groups, $payload, $options); diff --git a/ContainerConstraintValidatorFactory.php b/ContainerConstraintValidatorFactory.php index b4e5690ee..49e8d5aa3 100644 --- a/ContainerConstraintValidatorFactory.php +++ b/ContainerConstraintValidatorFactory.php @@ -43,7 +43,7 @@ public function getInstance(Constraint $constraint): ConstraintValidatorInterfac $this->validators[$name] = $this->container->get($name); } else { if (!class_exists($name)) { - throw new ValidatorException(sprintf('Constraint validator "%s" does not exist or is not enabled. Check the "validatedBy" method in your constraint class "%s".', $name, get_debug_type($constraint))); + throw new ValidatorException(\sprintf('Constraint validator "%s" does not exist or is not enabled. Check the "validatedBy" method in your constraint class "%s".', $name, get_debug_type($constraint))); } $this->validators[$name] = new $name(); diff --git a/DependencyInjection/AddAutoMappingConfigurationPass.php b/DependencyInjection/AddAutoMappingConfigurationPass.php index db33aa5a7..0a6250696 100644 --- a/DependencyInjection/AddAutoMappingConfigurationPass.php +++ b/DependencyInjection/AddAutoMappingConfigurationPass.php @@ -79,6 +79,6 @@ private function getRegexp(array $patterns): ?string $regexps[] = '^'.$regex; } - return sprintf('{%s}', implode('|', $regexps)); + return \sprintf('{%s}', implode('|', $regexps)); } } diff --git a/Exception/UnexpectedTypeException.php b/Exception/UnexpectedTypeException.php index efd52d80c..24ae4b3ac 100644 --- a/Exception/UnexpectedTypeException.php +++ b/Exception/UnexpectedTypeException.php @@ -15,6 +15,6 @@ class UnexpectedTypeException extends ValidatorException { public function __construct(mixed $value, string $expectedType) { - parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, get_debug_type($value))); + parent::__construct(\sprintf('Expected argument of type "%s", "%s" given', $expectedType, get_debug_type($value))); } } diff --git a/Mapping/ClassMetadata.php b/Mapping/ClassMetadata.php index 1d11cc2c2..d812255c4 100644 --- a/Mapping/ClassMetadata.php +++ b/Mapping/ClassMetadata.php @@ -396,11 +396,11 @@ public function setGroupSequence(array|GroupSequence $groupSequence): static } if (\in_array(Constraint::DEFAULT_GROUP, $groupSequence->groups, true)) { - throw new GroupDefinitionException(sprintf('The group "%s" is not allowed in group sequences.', Constraint::DEFAULT_GROUP)); + throw new GroupDefinitionException(\sprintf('The group "%s" is not allowed in group sequences.', Constraint::DEFAULT_GROUP)); } if (!\in_array($this->getDefaultGroup(), $groupSequence->groups, true)) { - throw new GroupDefinitionException(sprintf('The group "%s" is missing in the group sequence.', $this->getDefaultGroup())); + throw new GroupDefinitionException(\sprintf('The group "%s" is missing in the group sequence.', $this->getDefaultGroup())); } $this->groupSequence = $groupSequence; @@ -438,7 +438,7 @@ public function setGroupSequenceProvider(bool $active): void } if (null === $this->groupProvider && !$this->getReflectionClass()->implementsInterface(GroupSequenceProviderInterface::class)) { - throw new GroupDefinitionException(sprintf('Class "%s" must implement GroupSequenceProviderInterface.', $this->name)); + throw new GroupDefinitionException(\sprintf('Class "%s" must implement GroupSequenceProviderInterface.', $this->name)); } $this->groupSequenceProvider = $active; @@ -474,7 +474,7 @@ private function addPropertyMetadata(PropertyMetadataInterface $metadata): void private function checkConstraint(Constraint $constraint): void { if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets(), true)) { - throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', get_debug_type($constraint))); + throw new ConstraintDefinitionException(\sprintf('The constraint "%s" cannot be put on classes.', get_debug_type($constraint))); } if ($constraint instanceof Composite) { diff --git a/Mapping/Factory/LazyLoadingMetadataFactory.php b/Mapping/Factory/LazyLoadingMetadataFactory.php index 26cf61f49..1404df963 100644 --- a/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -68,7 +68,7 @@ public function __construct( public function getMetadataFor(mixed $value): MetadataInterface { if (!\is_object($value) && !\is_string($value)) { - throw new NoSuchMetadataException(sprintf('Cannot create metadata for non-objects. Got: "%s".', get_debug_type($value))); + throw new NoSuchMetadataException(\sprintf('Cannot create metadata for non-objects. Got: "%s".', get_debug_type($value))); } $class = ltrim(\is_object($value) ? $value::class : $value, '\\'); @@ -78,7 +78,7 @@ public function getMetadataFor(mixed $value): MetadataInterface } if (!class_exists($class) && !interface_exists($class, false)) { - throw new NoSuchMetadataException(sprintf('The class or interface "%s" does not exist.', $class)); + throw new NoSuchMetadataException(\sprintf('The class or interface "%s" does not exist.', $class)); } $cacheItem = $this->cache?->getItem($this->escapeClassName($class)); diff --git a/Mapping/GenericMetadata.php b/Mapping/GenericMetadata.php index 899cf8933..43992255c 100644 --- a/Mapping/GenericMetadata.php +++ b/Mapping/GenericMetadata.php @@ -133,7 +133,7 @@ public function __clone() public function addConstraint(Constraint $constraint): static { if ($constraint instanceof Traverse || $constraint instanceof Cascade) { - throw new ConstraintDefinitionException(sprintf('The constraint "%s" can only be put on classes. Please use "Symfony\Component\Validator\Constraints\Valid" instead.', get_debug_type($constraint))); + throw new ConstraintDefinitionException(\sprintf('The constraint "%s" can only be put on classes. Please use "Symfony\Component\Validator\Constraints\Valid" instead.', get_debug_type($constraint))); } if ($constraint instanceof Valid && null === $constraint->groups) { diff --git a/Mapping/GetterMetadata.php b/Mapping/GetterMetadata.php index 8b8d2e15f..a4e2352ec 100644 --- a/Mapping/GetterMetadata.php +++ b/Mapping/GetterMetadata.php @@ -53,10 +53,10 @@ public function __construct(string $class, string $property, ?string $method = n } elseif (method_exists($class, $hasMethod)) { $method = $hasMethod; } else { - throw new ValidatorException(sprintf('Neither of these methods exist in class "%s": "%s", "%s", "%s".', $class, $getMethod, $isMethod, $hasMethod)); + throw new ValidatorException(\sprintf('Neither of these methods exist in class "%s": "%s", "%s", "%s".', $class, $getMethod, $isMethod, $hasMethod)); } } elseif (!method_exists($class, $method)) { - throw new ValidatorException(sprintf('The "%s()" method does not exist in class "%s".', $method, $class)); + throw new ValidatorException(\sprintf('The "%s()" method does not exist in class "%s".', $method, $class)); } parent::__construct($class, $method, $property); diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index bcfdce2bd..a74b53348 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -76,7 +76,7 @@ protected function newConstraint(string $name, mixed $options = null): Constrain [$prefix, $className] = explode(':', $name, 2); if (!isset($this->namespaces[$prefix])) { - throw new MappingException(sprintf('Undefined namespace prefix "%s".', $prefix)); + throw new MappingException(\sprintf('Undefined namespace prefix "%s".', $prefix)); } $className = $this->namespaces[$prefix].$className; diff --git a/Mapping/Loader/AttributeLoader.php b/Mapping/Loader/AttributeLoader.php index 9674122b6..12a6a342b 100644 --- a/Mapping/Loader/AttributeLoader.php +++ b/Mapping/Loader/AttributeLoader.php @@ -68,7 +68,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool if (preg_match('/^(get|is|has)(.+)$/i', $method->name, $matches)) { $metadata->addGetterMethodConstraint(lcfirst($matches[2]), $matches[0], $constraint); } else { - throw new MappingException(sprintf('The constraint on "%s::%s()" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name)); + throw new MappingException(\sprintf('The constraint on "%s::%s()" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name)); } } diff --git a/Mapping/Loader/FileLoader.php b/Mapping/Loader/FileLoader.php index 03571fa77..ea46ffc8c 100644 --- a/Mapping/Loader/FileLoader.php +++ b/Mapping/Loader/FileLoader.php @@ -34,15 +34,15 @@ public function __construct( protected string $file, ) { if (!is_file($file)) { - throw new MappingException(sprintf('The mapping file "%s" does not exist.', $file)); + throw new MappingException(\sprintf('The mapping file "%s" does not exist.', $file)); } if (!is_readable($file)) { - throw new MappingException(sprintf('The mapping file "%s" is not readable.', $file)); + throw new MappingException(\sprintf('The mapping file "%s" is not readable.', $file)); } if (!stream_is_local($this->file)) { - throw new MappingException(sprintf('The mapping file "%s" is not a local file.', $file)); + throw new MappingException(\sprintf('The mapping file "%s" is not a local file.', $file)); } } } diff --git a/Mapping/Loader/LoaderChain.php b/Mapping/Loader/LoaderChain.php index ef08ad0c5..88510a262 100644 --- a/Mapping/Loader/LoaderChain.php +++ b/Mapping/Loader/LoaderChain.php @@ -35,7 +35,7 @@ public function __construct( ) { foreach ($loaders as $loader) { if (!$loader instanceof LoaderInterface) { - throw new MappingException(sprintf('Class "%s" is expected to implement LoaderInterface.', get_debug_type($loader))); + throw new MappingException(\sprintf('Class "%s" is expected to implement LoaderInterface.', get_debug_type($loader))); } } } diff --git a/Mapping/Loader/StaticMethodLoader.php b/Mapping/Loader/StaticMethodLoader.php index 06d174732..c230c36d3 100644 --- a/Mapping/Loader/StaticMethodLoader.php +++ b/Mapping/Loader/StaticMethodLoader.php @@ -44,7 +44,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool } if (!$reflMethod->isStatic()) { - throw new MappingException(sprintf('The method "%s::%s()" should be static.', $reflClass->name, $this->methodName)); + throw new MappingException(\sprintf('The method "%s::%s()" should be static.', $reflClass->name, $this->methodName)); } if ($reflMethod->getDeclaringClass()->name != $reflClass->name) { diff --git a/Mapping/Loader/YamlFileLoader.php b/Mapping/Loader/YamlFileLoader.php index b117d7932..78d1b0ef9 100644 --- a/Mapping/Loader/YamlFileLoader.php +++ b/Mapping/Loader/YamlFileLoader.php @@ -116,7 +116,7 @@ private function parseFile(string $path): array try { $classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e); + throw new \InvalidArgumentException(\sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e); } // empty file @@ -126,7 +126,7 @@ private function parseFile(string $path): array // not an array if (!\is_array($classes)) { - throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $this->file)); + throw new \InvalidArgumentException(\sprintf('The file "%s" must contain a YAML array.', $this->file)); } return $classes; diff --git a/Mapping/MemberMetadata.php b/Mapping/MemberMetadata.php index ecfcad988..6df8add12 100644 --- a/Mapping/MemberMetadata.php +++ b/Mapping/MemberMetadata.php @@ -148,7 +148,7 @@ abstract protected function newReflectionMember(object|string $objectOrClassName private function checkConstraint(Constraint $constraint): void { if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets(), true)) { - throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', get_debug_type($constraint))); + throw new ConstraintDefinitionException(\sprintf('The constraint "%s" cannot be put on properties or getters.', get_debug_type($constraint))); } if ($constraint instanceof Composite) { diff --git a/Mapping/PropertyMetadata.php b/Mapping/PropertyMetadata.php index 49620bfdf..89a25a5ef 100644 --- a/Mapping/PropertyMetadata.php +++ b/Mapping/PropertyMetadata.php @@ -37,7 +37,7 @@ class PropertyMetadata extends MemberMetadata public function __construct(string $class, string $name) { if (!property_exists($class, $name)) { - throw new ValidatorException(sprintf('Property "%s" does not exist in class "%s".', $name, $class)); + throw new ValidatorException(\sprintf('Property "%s" does not exist in class "%s".', $name, $class)); } parent::__construct($class, $name, $name); @@ -75,7 +75,7 @@ protected function newReflectionMember(object|string $objectOrClassName): \Refle $objectOrClassName = get_parent_class($objectOrClassName); if (false === $objectOrClassName) { - throw new ValidatorException(sprintf('Property "%s" does not exist in class "%s".', $this->getName(), $originalClass)); + throw new ValidatorException(\sprintf('Property "%s" does not exist in class "%s".', $this->getName(), $originalClass)); } } diff --git a/Test/ConstraintValidatorTestCase.php b/Test/ConstraintValidatorTestCase.php index 803df588d..ecb91e2b3 100644 --- a/Test/ConstraintValidatorTestCase.php +++ b/Test/ConstraintValidatorTestCase.php @@ -280,7 +280,7 @@ protected function expectViolationsAt(int $i, mixed $value, Constraint $constrai protected function assertNoViolation() { - $this->assertSame(0, $violationsCount = \count($this->context->getViolations()), sprintf('0 violation expected. Got %u.', $violationsCount)); + $this->assertSame(0, $violationsCount = \count($this->context->getViolations()), \sprintf('0 violation expected. Got %u.', $violationsCount)); } protected function buildViolation(string|\Stringable $message): ConstraintViolationAssertion @@ -421,7 +421,7 @@ public function assertRaised(): void $violations = iterator_to_array($this->context->getViolations()); - Assert::assertSame($expectedCount = \count($expected), $violationsCount = \count($violations), sprintf('%u violation(s) expected. Got %u.', $expectedCount, $violationsCount)); + Assert::assertSame($expectedCount = \count($expected), $violationsCount = \count($violations), \sprintf('%u violation(s) expected. Got %u.', $expectedCount, $violationsCount)); reset($violations); @@ -489,7 +489,7 @@ public function doAtPath(string $path): static Assert::assertFalse($this->expectNoValidate, 'No validation calls have been expected.'); if (!isset($this->expectedAtPath[++$this->atPathCalls])) { - throw new ExpectationFailedException(sprintf('Validation for property path "%s" was not expected.', $path)); + throw new ExpectationFailedException(\sprintf('Validation for property path "%s" was not expected.', $path)); } $expectedPath = $this->expectedAtPath[$this->atPathCalls]; diff --git a/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/Tests/Constraints/AbstractComparisonValidatorTestCase.php index 8ac430bb0..fc7332b94 100644 --- a/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -155,7 +155,7 @@ public function testInvalidValuePath() $constraint = $this->createConstraint(['propertyPath' => 'foo']); $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage(sprintf('Invalid property path "foo" provided to "%s" constraint', $constraint::class)); + $this->expectExceptionMessage(\sprintf('Invalid property path "foo" provided to "%s" constraint', $constraint::class)); $object = new ComparisonTest_Class(5); @@ -232,8 +232,8 @@ public static function throwsOnInvalidStringDatesProvider(): array ]); return [ - [$constraint, sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraint::class), new \DateTimeImmutable()], - [$constraint, sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraint::class), new \DateTime()], + [$constraint, \sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraint::class), new \DateTimeImmutable()], + [$constraint, \sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraint::class), new \DateTime()], ]; } diff --git a/Tests/Constraints/AtLeastOneOfValidatorTest.php b/Tests/Constraints/AtLeastOneOfValidatorTest.php index 9e2da70f7..7cf1dcb76 100644 --- a/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -107,12 +107,12 @@ public function testInvalidCombinationsWithDefaultMessage($value, $constraints) $i = 0; foreach ($constraints as $constraint) { - $message[] = sprintf(' [%d] %s', ++$i, $validator->validate($value, $constraint)->get(0)->getMessage()); + $message[] = \sprintf(' [%d] %s', ++$i, $validator->validate($value, $constraint)->get(0)->getMessage()); } $violations = $validator->validate($value, $atLeastOneOf); - $this->assertCount(1, $violations, sprintf('1 violation expected. Got %u.', \count($violations))); + $this->assertCount(1, $violations, \sprintf('1 violation expected. Got %u.', \count($violations))); $this->assertEquals(new ConstraintViolation(implode('', $message), implode('', $message), [], $value, '', $value, null, AtLeastOneOf::AT_LEAST_ONE_OF_ERROR, $atLeastOneOf), $violations->get(0)); } @@ -125,7 +125,7 @@ public function testInvalidCombinationsWithCustomMessage($value, $constraints) $violations = Validation::createValidator()->validate($value, $atLeastOneOf); - $this->assertCount(1, $violations, sprintf('1 violation expected. Got %u.', \count($violations))); + $this->assertCount(1, $violations, \sprintf('1 violation expected. Got %u.', \count($violations))); $this->assertEquals(new ConstraintViolation('foo', 'foo', [], $value, '', $value, null, AtLeastOneOf::AT_LEAST_ONE_OF_ERROR, $atLeastOneOf), $violations->get(0)); } diff --git a/Tests/Constraints/BicValidatorTest.php b/Tests/Constraints/BicValidatorTest.php index b71c7dac5..bb1de6846 100644 --- a/Tests/Constraints/BicValidatorTest.php +++ b/Tests/Constraints/BicValidatorTest.php @@ -164,7 +164,7 @@ public function testInvalidValuePath() $constraint = new Bic(['ibanPropertyPath' => 'foo']); $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage(sprintf('Invalid property path "foo" provided to "%s" constraint', $constraint::class)); + $this->expectExceptionMessage(\sprintf('Invalid property path "foo" provided to "%s" constraint', $constraint::class)); $object = new BicComparisonTestClass(5); diff --git a/Tests/Constraints/CidrTest.php b/Tests/Constraints/CidrTest.php index c2536a7b5..142783ec0 100644 --- a/Tests/Constraints/CidrTest.php +++ b/Tests/Constraints/CidrTest.php @@ -60,7 +60,7 @@ public function testWithInvalidVersion() ]; self::expectException(ConstraintDefinitionException::class); - self::expectExceptionMessage(sprintf('The option "version" must be one of "%s".', implode('", "', $availableVersions))); + self::expectExceptionMessage(\sprintf('The option "version" must be one of "%s".', implode('", "', $availableVersions))); new Cidr(['version' => '8']); } @@ -89,7 +89,7 @@ public function testWithInvalidMinMaxValues(string $ipVersion, int $netmaskMin, $expectedMax = Ip::V4 == $ipVersion ? 32 : 128; self::expectException(ConstraintDefinitionException::class); - self::expectExceptionMessage(sprintf('The netmask range must be between 0 and %d.', $expectedMax)); + self::expectExceptionMessage(\sprintf('The netmask range must be between 0 and %d.', $expectedMax)); new Cidr([ 'version' => $ipVersion, diff --git a/Tests/Constraints/DisableAutoMappingTest.php b/Tests/Constraints/DisableAutoMappingTest.php index e47bc6aeb..709334e36 100644 --- a/Tests/Constraints/DisableAutoMappingTest.php +++ b/Tests/Constraints/DisableAutoMappingTest.php @@ -26,7 +26,7 @@ class DisableAutoMappingTest extends TestCase public function testGroups() { $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage(sprintf('The option "groups" is not supported by the constraint "%s".', DisableAutoMapping::class)); + $this->expectExceptionMessage(\sprintf('The option "groups" is not supported by the constraint "%s".', DisableAutoMapping::class)); new DisableAutoMapping(['groups' => 'foo']); } diff --git a/Tests/Constraints/DivisibleByValidatorTest.php b/Tests/Constraints/DivisibleByValidatorTest.php index 30950d94b..37ae2087e 100644 --- a/Tests/Constraints/DivisibleByValidatorTest.php +++ b/Tests/Constraints/DivisibleByValidatorTest.php @@ -88,7 +88,7 @@ public static function provideInvalidComparisons(): array public function testThrowsOnNonNumericValues(string $expectedGivenType, $value, $comparedValue) { $this->expectException(UnexpectedValueException::class); - $this->expectExceptionMessage(sprintf('Expected argument of type "numeric", "%s" given', $expectedGivenType)); + $this->expectExceptionMessage(\sprintf('Expected argument of type "numeric", "%s" given', $expectedGivenType)); $this->validator->validate($value, $this->createConstraint([ 'value' => $comparedValue, diff --git a/Tests/Constraints/EmailValidatorTest.php b/Tests/Constraints/EmailValidatorTest.php index ab424a82d..4dd6442b2 100644 --- a/Tests/Constraints/EmailValidatorTest.php +++ b/Tests/Constraints/EmailValidatorTest.php @@ -180,7 +180,7 @@ public static function getInvalidHtml5Emails() [' example@example.com '], [' example @example .com '], ['example@-example.com'], - [sprintf('example@%s.com', str_repeat('a', 64))], + [\sprintf('example@%s.com', str_repeat('a', 64))], ]; } diff --git a/Tests/Constraints/EnableAutoMappingTest.php b/Tests/Constraints/EnableAutoMappingTest.php index 2a5a944d3..66ab42cdf 100644 --- a/Tests/Constraints/EnableAutoMappingTest.php +++ b/Tests/Constraints/EnableAutoMappingTest.php @@ -26,7 +26,7 @@ class EnableAutoMappingTest extends TestCase public function testGroups() { $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage(sprintf('The option "groups" is not supported by the constraint "%s".', EnableAutoMapping::class)); + $this->expectExceptionMessage(\sprintf('The option "groups" is not supported by the constraint "%s".', EnableAutoMapping::class)); new EnableAutoMapping(['groups' => 'foo']); } diff --git a/Tests/Constraints/HostnameValidatorTest.php b/Tests/Constraints/HostnameValidatorTest.php index 0af802b63..f0b03e019 100644 --- a/Tests/Constraints/HostnameValidatorTest.php +++ b/Tests/Constraints/HostnameValidatorTest.php @@ -72,7 +72,7 @@ public static function getValidMultilevelDomains() ['xn--diseolatinoamericano-66b.com'], ['xn--ggle-0nda.com'], ['www.xn--simulateur-prt-2kb.fr'], - [sprintf('%s.com', str_repeat('a', 20))], + [\sprintf('%s.com', str_repeat('a', 20))], ]; } @@ -114,7 +114,7 @@ public static function getInvalidDomains() ['qq--.com'], ['-example.com'], ['example-.com'], - [sprintf('%s.com', str_repeat('a', 300))], + [\sprintf('%s.com', str_repeat('a', 300))], ]; } diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index 6523173a1..908517081 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -448,7 +448,7 @@ public function testInvalidMimeType() $this->assertSame('image/*', $constraint->mimeTypes); $this->buildViolation('This file is not a valid image.') - ->setParameter('{{ file }}', sprintf('"%s"', $this->notAnImage)) + ->setParameter('{{ file }}', \sprintf('"%s"', $this->notAnImage)) ->setParameter('{{ type }}', '"text/plain"') ->setParameter('{{ types }}', '"image/*"') ->setParameter('{{ name }}', '"ccc.txt"') @@ -475,7 +475,7 @@ public function testInvalidMimeTypeWithNarrowedSet(Image $constraint) $this->validator->validate($this->image, $constraint); $this->buildViolation('The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.') - ->setParameter('{{ file }}', sprintf('"%s"', $this->image)) + ->setParameter('{{ file }}', \sprintf('"%s"', $this->image)) ->setParameter('{{ type }}', '"image/gif"') ->setParameter('{{ types }}', '"image/jpeg", "image/png"') ->setParameter('{{ name }}', '"test.gif"') diff --git a/Tests/Constraints/LengthTest.php b/Tests/Constraints/LengthTest.php index 03bd37674..6af8a7bc1 100644 --- a/Tests/Constraints/LengthTest.php +++ b/Tests/Constraints/LengthTest.php @@ -58,7 +58,7 @@ public function testNonDefaultCountUnitCanBeSet() public function testInvalidCountUnitThrowsException() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage(sprintf('The "countUnit" option must be one of the "%s"::COUNT_* constants ("%s" given).', Length::class, 'nonExistentCountUnit')); + $this->expectExceptionMessage(\sprintf('The "countUnit" option must be one of the "%s"::COUNT_* constants ("%s" given).', Length::class, 'nonExistentCountUnit')); new Length(['min' => 0, 'max' => 10, 'countUnit' => 'nonExistentCountUnit']); } diff --git a/Tests/Constraints/TimezoneValidatorTest.php b/Tests/Constraints/TimezoneValidatorTest.php index 527b2a664..e72aca91e 100644 --- a/Tests/Constraints/TimezoneValidatorTest.php +++ b/Tests/Constraints/TimezoneValidatorTest.php @@ -136,7 +136,7 @@ public function testInvalidTimezoneWithoutZone(string $timezone) $this->validator->validate($timezone, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', sprintf('"%s"', $timezone)) + ->setParameter('{{ value }}', \sprintf('"%s"', $timezone)) ->setCode(Timezone::TIMEZONE_IDENTIFIER_ERROR) ->assertRaised(); } @@ -162,7 +162,7 @@ public function testInvalidGroupedTimezones(string $timezone, int $zone) $this->validator->validate($timezone, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', sprintf('"%s"', $timezone)) + ->setParameter('{{ value }}', \sprintf('"%s"', $timezone)) ->setCode(Timezone::TIMEZONE_IDENTIFIER_IN_ZONE_ERROR) ->assertRaised(); } @@ -243,7 +243,7 @@ public function testInvalidGroupedTimezonesByCountry(string $timezone, string $c $this->validator->validate($timezone, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', sprintf('"%s"', $timezone)) + ->setParameter('{{ value }}', \sprintf('"%s"', $timezone)) ->setCode(Timezone::TIMEZONE_IDENTIFIER_IN_COUNTRY_ERROR) ->assertRaised(); } @@ -297,7 +297,7 @@ public function testDeprecatedTimezonesAreInvalidWithoutBC(string $timezone) $this->validator->validate($timezone, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', sprintf('"%s"', $timezone)) + ->setParameter('{{ value }}', \sprintf('"%s"', $timezone)) ->setCode(Timezone::TIMEZONE_IDENTIFIER_ERROR) ->assertRaised(); } diff --git a/Tests/Constraints/UniqueValidatorTest.php b/Tests/Constraints/UniqueValidatorTest.php index 0382eb5b6..e7261cff8 100644 --- a/Tests/Constraints/UniqueValidatorTest.php +++ b/Tests/Constraints/UniqueValidatorTest.php @@ -234,7 +234,7 @@ public function testCollectionFieldsAreOptional() public function testCollectionFieldNamesMustBeString(string $type, mixed $field) { $this->expectException(UnexpectedTypeException::class); - $this->expectExceptionMessage(sprintf('Expected argument of type "string", "%s" given', $type)); + $this->expectExceptionMessage(\sprintf('Expected argument of type "string", "%s" given', $type)); $this->validator->validate([['value' => 5], ['id' => 1, 'value' => 6]], new Unique(fields: [$field])); } diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index 19c88f620..326ed4fc4 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -26,7 +26,7 @@ public function testTranslationFileIsValid($filePath) $errors = XliffUtils::validateSchema($document); - $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } /** @@ -39,7 +39,7 @@ public function testTranslationFileIsValidWithoutEntityLoader($filePath) $errors = XliffUtils::validateSchema($document); - $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } public static function provideTranslationFiles() diff --git a/Validator/RecursiveContextualValidator.php b/Validator/RecursiveContextualValidator.php index 2949f3ed6..c8a26d0b8 100644 --- a/Validator/RecursiveContextualValidator.php +++ b/Validator/RecursiveContextualValidator.php @@ -153,7 +153,7 @@ public function validate(mixed $value, Constraint|array|null $constraints = null return $this; } - throw new RuntimeException(sprintf('Cannot validate values of type "%s" automatically. Please provide a constraint.', get_debug_type($value))); + throw new RuntimeException(\sprintf('Cannot validate values of type "%s" automatically. Please provide a constraint.', get_debug_type($value))); } public function validateProperty(object $object, string $propertyName, string|GroupSequence|array|null $groups = null): static @@ -161,7 +161,7 @@ public function validateProperty(object $object, string $propertyName, string|Gr $classMetadata = $this->metadataFactory->getMetadataFor($object); if (!$classMetadata instanceof ClassMetadataInterface) { - throw new ValidatorException(sprintf('The metadata factory should return instances of "\Symfony\Component\Validator\Mapping\ClassMetadataInterface", got: "%s".', get_debug_type($classMetadata))); + throw new ValidatorException(\sprintf('The metadata factory should return instances of "\Symfony\Component\Validator\Mapping\ClassMetadataInterface", got: "%s".', get_debug_type($classMetadata))); } $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); @@ -202,7 +202,7 @@ public function validatePropertyValue(object|string $objectOrClass, string $prop $classMetadata = $this->metadataFactory->getMetadataFor($objectOrClass); if (!$classMetadata instanceof ClassMetadataInterface) { - throw new ValidatorException(sprintf('The metadata factory should return instances of "\Symfony\Component\Validator\Mapping\ClassMetadataInterface", got: "%s".', get_debug_type($classMetadata))); + throw new ValidatorException(\sprintf('The metadata factory should return instances of "\Symfony\Component\Validator\Mapping\ClassMetadataInterface", got: "%s".', get_debug_type($classMetadata))); } $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); @@ -290,7 +290,7 @@ private function validateObject(object $object, string $propertyPath, array $gro $classMetadata = $this->metadataFactory->getMetadataFor($object); if (!$classMetadata instanceof ClassMetadataInterface) { - throw new UnsupportedMetadataException(sprintf('The metadata factory should return instances of "Symfony\Component\Validator\Mapping\ClassMetadataInterface", got: "%s".', get_debug_type($classMetadata))); + throw new UnsupportedMetadataException(\sprintf('The metadata factory should return instances of "Symfony\Component\Validator\Mapping\ClassMetadataInterface", got: "%s".', get_debug_type($classMetadata))); } $this->validateClassNode( @@ -493,7 +493,7 @@ private function validateClassNode(object $object, ?string $cacheKey, ClassMetad // returns two metadata objects, not just one foreach ($metadata->getPropertyMetadata($propertyName) as $propertyMetadata) { if (!$propertyMetadata instanceof PropertyMetadataInterface) { - throw new UnsupportedMetadataException(sprintf('The property metadata instances should implement "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', get_debug_type($propertyMetadata))); + throw new UnsupportedMetadataException(\sprintf('The property metadata instances should implement "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', get_debug_type($propertyMetadata))); } if ($propertyMetadata instanceof GetterMetadata) { @@ -534,7 +534,7 @@ private function validateClassNode(object $object, ?string $cacheKey, ClassMetad // If TRAVERSE, fail if we have no Traversable if (!$object instanceof \Traversable) { - throw new ConstraintDefinitionException(sprintf('Traversal was enabled for "%s", but this class does not implement "\Traversable".', get_debug_type($object))); + throw new ConstraintDefinitionException(\sprintf('Traversal was enabled for "%s", but this class does not implement "\Traversable".', get_debug_type($object))); } $this->validateEachObjectIn( @@ -640,7 +640,7 @@ private function validateGenericNode(mixed $value, ?object $object, ?string $cac } if (!\is_object($value)) { - throw new NoSuchMetadataException(sprintf('Cannot create metadata for non-objects. Got: "%s".', \gettype($value))); + throw new NoSuchMetadataException(\sprintf('Cannot create metadata for non-objects. Got: "%s".', \gettype($value))); } $this->validateObject( From 4f3a6bf9b2a237f93d8b38abaef07e6f2263e37a Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 16 Jun 2024 17:17:26 +0200 Subject: [PATCH 061/149] chore: CS fixes --- ...aterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php index e21f6b8e9..4a2584be6 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php @@ -69,7 +69,7 @@ public function testThrowsConstraintExceptionIfValue() */ public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { - $this->markTestSkipped('Value option always set for PositiveOrZero constraint'); + $this->markTestSkipped('Value option always set for PositiveOrZero constraint'); } public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() From 3a8f055aef74818dcefaf3024be44cf1bdb629c6 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 18 Jun 2024 10:22:24 +0200 Subject: [PATCH 062/149] [Validator] Add the `format` option to the `Uuid` constraint to allow accepting different ULID formats --- CHANGELOG.md | 1 + Constraints/Ulid.php | 12 +++++++ Constraints/UlidValidator.php | 40 ++++++++++++++------- Tests/Constraints/UlidTest.php | 9 +++++ Tests/Constraints/UlidValidatorTest.php | 47 +++++++++++++++++++++++-- 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 337534645..b5461ca6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Make `PasswordStrengthValidator::estimateStrength()` public * Add the `Yaml` constraint for validating YAML content * Add `errorPath` to Unique constraint + * Add the `format` option to the `Ulid` constraint to allow accepting different ULID formats 7.1 --- diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index a5e2a47b9..caf92b022 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * Validates that a value is a valid Universally Unique Lexicographically Sortable Identifier (ULID). @@ -35,20 +36,31 @@ class Ulid extends Constraint self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', ]; + public const FORMAT_BASE_32 = 'base32'; + public const FORMAT_BASE_58 = 'base58'; + public string $message = 'This is not a valid ULID.'; + public string $format = self::FORMAT_BASE_32; /** * @param array|null $options * @param string[]|null $groups + * @param self::FORMAT_*|null $format */ public function __construct( ?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null, + ?string $format = null, ) { parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; + $this->format = $format ?? $this->format; + + if (!\in_array($this->format, [self::FORMAT_BASE_32, self::FORMAT_BASE_58], true)) { + throw new ConstraintDefinitionException(sprintf('The "%s" validation format is not supported.', $format)); + } } } diff --git a/Constraints/UlidValidator.php b/Constraints/UlidValidator.php index 91942721b..e4133d2dc 100644 --- a/Constraints/UlidValidator.php +++ b/Constraints/UlidValidator.php @@ -40,31 +40,47 @@ public function validate(mixed $value, Constraint $constraint): void $value = (string) $value; - if (26 !== \strlen($value)) { + [$requiredLength, $requiredCharset] = match ($constraint->format) { + Ulid::FORMAT_BASE_32 => [26, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz'], + Ulid::FORMAT_BASE_58 => [22, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'], + }; + + if ($requiredLength !== \strlen($value)) { $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(26 > \strlen($value) ? Ulid::TOO_SHORT_ERROR : Ulid::TOO_LONG_ERROR) + ->setParameters([ + '{{ value }}' => $this->formatValue($value), + '{{ format }}' => $constraint->format, + ]) + ->setCode($requiredLength > \strlen($value) ? Ulid::TOO_SHORT_ERROR : Ulid::TOO_LONG_ERROR) ->addViolation(); return; } - if (\strlen($value) !== strspn($value, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz')) { + if (\strlen($value) !== strspn($value, $requiredCharset)) { $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameters([ + '{{ value }}' => $this->formatValue($value), + '{{ format }}' => $constraint->format, + ]) ->setCode(Ulid::INVALID_CHARACTERS_ERROR) ->addViolation(); return; } - // Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ' - // Cf https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings - if ($value[0] > '7') { - $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Ulid::TOO_LARGE_ERROR) - ->addViolation(); + if (Ulid::FORMAT_BASE_32 === $constraint->format) { + // Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ' + // Cf https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings + if ($value[0] > '7') { + $this->context->buildViolation($constraint->message) + ->setParameters([ + '{{ value }}' => $this->formatValue($value), + '{{ format }}' => $constraint->format, + ]) + ->setCode(Ulid::TOO_LARGE_ERROR) + ->addViolation(); + } } } } diff --git a/Tests/Constraints/UlidTest.php b/Tests/Constraints/UlidTest.php index 14046e37a..bb12ef0e9 100644 --- a/Tests/Constraints/UlidTest.php +++ b/Tests/Constraints/UlidTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Ulid; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; @@ -32,6 +33,14 @@ public function testAttributes() self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } + + public function testUnexpectedValidationFormat() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "invalid" validation format is not supported.'); + + new Ulid(format: 'invalid'); + } } class UlidDummy diff --git a/Tests/Constraints/UlidValidatorTest.php b/Tests/Constraints/UlidValidatorTest.php index abe5490d1..7d2be16e2 100644 --- a/Tests/Constraints/UlidValidatorTest.php +++ b/Tests/Constraints/UlidValidatorTest.php @@ -53,6 +53,13 @@ public function testValidUlid() $this->assertNoViolation(); } + public function testValidUlidAsBase58() + { + $this->validator->validate('1CCD2w4mK2m455S2BAXFht', new Ulid(format: Ulid::FORMAT_BASE_58)); + + $this->assertNoViolation(); + } + /** * @dataProvider getInvalidUlids */ @@ -65,12 +72,15 @@ public function testInvalidUlid(string $ulid, string $code) $this->validator->validate($ulid, $constraint); $this->buildViolation('testMessage') - ->setParameter('{{ value }}', '"'.$ulid.'"') + ->setParameters([ + '{{ value }}' => '"'.$ulid.'"', + '{{ format }}' => Ulid::FORMAT_BASE_32, + ]) ->setCode($code) ->assertRaised(); } - public static function getInvalidUlids() + public static function getInvalidUlids(): array { return [ ['01ARZ3NDEKTSV4RRFFQ69G5FA', Ulid::TOO_SHORT_ERROR], @@ -81,6 +91,34 @@ public static function getInvalidUlids() ]; } + /** + * @dataProvider getInvalidBase58Ulids + */ + public function testInvalidBase58Ulid(string $ulid, string $code) + { + $constraint = new Ulid(message: 'testMessage', format: Ulid::FORMAT_BASE_58); + + $this->validator->validate($ulid, $constraint); + + $this->buildViolation('testMessage') + ->setParameters([ + '{{ value }}' => '"'.$ulid.'"', + '{{ format }}' => Ulid::FORMAT_BASE_58, + ]) + ->setCode($code) + ->assertRaised(); + } + + public static function getInvalidBase58Ulids(): array + { + return [ + ['1CCD2w4mK2m455S2BAXFh', Ulid::TOO_SHORT_ERROR], + ['1CCD2w4mK2m455S2BAXFhttt', Ulid::TOO_LONG_ERROR], + ['1CCD2w4mK2m455S2BAXFhO', Ulid::INVALID_CHARACTERS_ERROR], + ['not-even-ulid-like', Ulid::TOO_SHORT_ERROR], + ]; + } + public function testInvalidUlidNamed() { $constraint = new Ulid(message: 'testMessage'); @@ -88,7 +126,10 @@ public function testInvalidUlidNamed() $this->validator->validate('01ARZ3NDEKTSV4RRFFQ69G5FA', $constraint); $this->buildViolation('testMessage') - ->setParameter('{{ value }}', '"01ARZ3NDEKTSV4RRFFQ69G5FA"') + ->setParameters([ + '{{ value }}' => '"01ARZ3NDEKTSV4RRFFQ69G5FA"', + '{{ format }}' => Ulid::FORMAT_BASE_32, + ]) ->setCode(Ulid::TOO_SHORT_ERROR) ->assertRaised(); } From 5cb1747ffb4dd91ff1439c125b11b2ecad13e61a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 063/149] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index c34694db5..0775b0bd7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,4 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore /Resources/bin/sync-iban-formats.php export-ignore From aa3e93a680a07031e04bb8971c103dc4bd37e7ac Mon Sep 17 00:00:00 2001 From: Maximilian Zumbansen Date: Mon, 8 Jul 2024 15:09:05 +0200 Subject: [PATCH 064/149] [Validator] add setGroupProvider to AttributeLoader --- Mapping/Loader/AttributeLoader.php | 1 + Tests/Mapping/Loader/AttributeLoaderTest.php | 21 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Mapping/Loader/AttributeLoader.php b/Mapping/Loader/AttributeLoader.php index 9674122b6..62e9243a2 100644 --- a/Mapping/Loader/AttributeLoader.php +++ b/Mapping/Loader/AttributeLoader.php @@ -37,6 +37,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool if ($constraint instanceof GroupSequence) { $metadata->setGroupSequence($constraint->groups); } elseif ($constraint instanceof GroupSequenceProvider) { + $metadata->setGroupProvider($constraint->provider); $metadata->setGroupSequenceProvider(true); } elseif ($constraint instanceof Constraint) { $metadata->addConstraint($constraint); diff --git a/Tests/Mapping/Loader/AttributeLoaderTest.php b/Tests/Mapping/Loader/AttributeLoaderTest.php index f9cb0da9b..ca025431c 100644 --- a/Tests/Mapping/Loader/AttributeLoaderTest.php +++ b/Tests/Mapping/Loader/AttributeLoaderTest.php @@ -210,6 +210,22 @@ public function testLoadGroupSequenceProviderAttribute() $this->assertEquals($expected, $metadata); } + public function testLoadExternalGroupSequenceProvider() + { + $loader = $this->createAttributeLoader(); + $namespace = $this->getFixtureAttributeNamespace(); + + $metadata = new ClassMetadata($namespace.'\GroupProviderDto'); + $loader->loadClassMetadata($metadata); + + $expected = new ClassMetadata($namespace.'\GroupProviderDto'); + $expected->setGroupProvider('Symfony\Component\Validator\Tests\Dummy\DummyGroupProvider'); + $expected->setGroupSequenceProvider(true); + $expected->getReflectionClass(); + + $this->assertEquals($expected, $metadata); + } + protected function createAttributeLoader(): AttributeLoader { return new AttributeLoader(); @@ -219,4 +235,9 @@ protected function getFixtureNamespace(): string { return 'Symfony\Component\Validator\Tests\Fixtures\NestedAttribute'; } + + protected function getFixtureAttributeNamespace(): string + { + return 'Symfony\Component\Validator\Tests\Fixtures\Attribute'; + } } From e96202bd4ed7116f365fdfdfd725f7158d3d5267 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 12 Jul 2024 16:39:00 +0200 Subject: [PATCH 065/149] [Validator] Add the `WordCount` constraint --- CHANGELOG.md | 1 + Constraints/WordCount.php | 65 ++++++++++ Constraints/WordCountValidator.php | 65 ++++++++++ Tests/Constraints/WordCountTest.php | 125 +++++++++++++++++++ Tests/Constraints/WordCountValidatorTest.php | 97 ++++++++++++++ 5 files changed, 353 insertions(+) create mode 100644 Constraints/WordCount.php create mode 100644 Constraints/WordCountValidator.php create mode 100644 Tests/Constraints/WordCountTest.php create mode 100644 Tests/Constraints/WordCountValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b5461ca6a..46709ac0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Add the `Yaml` constraint for validating YAML content * Add `errorPath` to Unique constraint * Add the `format` option to the `Ulid` constraint to allow accepting different ULID formats + * Add the `WordCount` constraint 7.1 --- diff --git a/Constraints/WordCount.php b/Constraints/WordCount.php new file mode 100644 index 000000000..6a85318b1 --- /dev/null +++ b/Constraints/WordCount.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Attribute\HasNamedArguments; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\MissingOptionsException; + +/** + * @author Alexandre Daubois + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class WordCount extends Constraint +{ + public const TOO_SHORT_ERROR = 'cc4925df-b5a6-42dd-87f3-21919f349bf3'; + public const TOO_LONG_ERROR = 'a951a642-f662-4fad-8761-79250eef74cb'; + + protected const ERROR_NAMES = [ + self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', + self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', + ]; + + #[HasNamedArguments] + public function __construct( + public ?int $min = null, + public ?int $max = null, + public ?string $locale = null, + public string $minMessage = 'This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words.', + public string $maxMessage = 'This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less.', + ?array $groups = null, + mixed $payload = null, + ) { + if (!class_exists(\IntlBreakIterator::class)) { + throw new \RuntimeException(\sprintf('The "%s" constraint requires the "intl" PHP extension.', __CLASS__)); + } + + if (null === $min && null === $max) { + throw new MissingOptionsException(\sprintf('Either option "min" or "max" must be given for constraint "%s".', __CLASS__), ['min', 'max']); + } + + if (null !== $min && $min < 0) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min word count to be a positive integer or 0 if set.', __CLASS__)); + } + + if (null !== $max && $max <= 0) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the max word count to be a positive integer if set.', __CLASS__)); + } + + if (null !== $min && null !== $max && $min > $max) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min word count to be less than or equal to the max word count.', __CLASS__)); + } + + parent::__construct(null, $groups, $payload); + } +} diff --git a/Constraints/WordCountValidator.php b/Constraints/WordCountValidator.php new file mode 100644 index 000000000..ee090de26 --- /dev/null +++ b/Constraints/WordCountValidator.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; + +/** + * @author Alexandre Daubois + */ +final class WordCountValidator extends ConstraintValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!class_exists(\IntlBreakIterator::class)) { + throw new \RuntimeException(\sprintf('The "%s" constraint requires the "intl" PHP extension.', __CLASS__)); + } + + if (!$constraint instanceof WordCount) { + throw new UnexpectedTypeException($constraint, WordCount::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!\is_string($value) && !$value instanceof \Stringable) { + throw new UnexpectedValueException($value, 'string'); + } + + $iterator = \IntlBreakIterator::createWordInstance($constraint->locale); + $iterator->setText($value); + $words = iterator_to_array($iterator->getPartsIterator()); + + // erase "blank words" and don't count them as words + $wordsCount = \count(array_filter(array_map(trim(...), $words))); + + if (null !== $constraint->min && $wordsCount < $constraint->min) { + $this->context->buildViolation($constraint->minMessage) + ->setParameter('{{ count }}', $wordsCount) + ->setParameter('{{ min }}', $constraint->min) + ->setPlural($constraint->min) + ->setInvalidValue($value) + ->addViolation(); + } elseif (null !== $constraint->max && $wordsCount > $constraint->max) { + $this->context->buildViolation($constraint->maxMessage) + ->setParameter('{{ count }}', $wordsCount) + ->setParameter('{{ max }}', $constraint->max) + ->setPlural($constraint->max) + ->setInvalidValue($value) + ->addViolation(); + } + } +} diff --git a/Tests/Constraints/WordCountTest.php b/Tests/Constraints/WordCountTest.php new file mode 100644 index 000000000..7ec911371 --- /dev/null +++ b/Tests/Constraints/WordCountTest.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\WordCount; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\MissingOptionsException; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; + +/** + * @requires extension intl + */ +class WordCountTest extends TestCase +{ + public function testLocaleIsSet() + { + $wordCount = new WordCount(min: 1, locale: 'en'); + + $this->assertSame('en', $wordCount->locale); + } + + public function testOnlyMinIsSet() + { + $wordCount = new WordCount(1); + + $this->assertSame(1, $wordCount->min); + $this->assertNull($wordCount->max); + $this->assertNull($wordCount->locale); + } + + public function testOnlyMaxIsSet() + { + $wordCount = new WordCount(max: 1); + + $this->assertNull($wordCount->min); + $this->assertSame(1, $wordCount->max); + $this->assertNull($wordCount->locale); + } + + public function testMinMustBeNatural() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the min word count to be a positive integer or 0 if set.'); + + new WordCount(-1); + } + + public function testMaxMustBePositive() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the max word count to be a positive integer if set.'); + + new WordCount(max: 0); + } + + public function testNothingIsSet() + { + $this->expectException(MissingOptionsException::class); + $this->expectExceptionMessage('Either option "min" or "max" must be given for constraint "Symfony\Component\Validator\Constraints\WordCount".'); + + new WordCount(); + } + + public function testMaxIsLessThanMin() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the min word count to be less than or equal to the max word count.'); + + new WordCount(2, 1); + } + + public function testMinAndMaxAreEquals() + { + $wordCount = new WordCount(1, 1); + + $this->assertSame(1, $wordCount->min); + $this->assertSame(1, $wordCount->max); + $this->assertNull($wordCount->locale); + } + + public function testAttributes() + { + $metadata = new ClassMetadata(WordCountDummy::class); + $loader = new AttributeLoader(); + $this->assertTrue($loader->loadClassMetadata($metadata)); + + [$aConstraint] = $metadata->properties['a']->getConstraints(); + $this->assertSame(1, $aConstraint->min); + $this->assertSame(null, $aConstraint->max); + $this->assertNull($aConstraint->locale); + + [$bConstraint] = $metadata->properties['b']->getConstraints(); + $this->assertSame(2, $bConstraint->min); + $this->assertSame(5, $bConstraint->max); + $this->assertNull($bConstraint->locale); + + [$cConstraint] = $metadata->properties['c']->getConstraints(); + $this->assertSame(3, $cConstraint->min); + $this->assertNull($cConstraint->max); + $this->assertSame('en', $cConstraint->locale); + } +} + +class WordCountDummy +{ + #[WordCount(min: 1)] + private string $a; + + #[WordCount(min: 2, max: 5)] + private string $b; + + #[WordCount(min: 3, locale: 'en')] + private string $c; +} diff --git a/Tests/Constraints/WordCountValidatorTest.php b/Tests/Constraints/WordCountValidatorTest.php new file mode 100644 index 000000000..65f34947b --- /dev/null +++ b/Tests/Constraints/WordCountValidatorTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\WordCount; +use Symfony\Component\Validator\Constraints\WordCountValidator; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; + +/** + * @requires extension intl + */ +class WordCountValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): WordCountValidator + { + return new WordCountValidator(); + } + + /** + * @dataProvider provideValidValues + */ + public function testValidWordCount(string|\Stringable|null $value, int $expectedWordCount) + { + $this->validator->validate($value, new WordCount(min: $expectedWordCount, max: $expectedWordCount)); + + $this->assertNoViolation(); + } + + public function testTooShort() + { + $constraint = new WordCount(min: 4, minMessage: 'myMessage'); + $this->validator->validate('my ascii string', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ count }}', 3) + ->setParameter('{{ min }}', 4) + ->setPlural(4) + ->setInvalidValue('my ascii string') + ->assertRaised(); + } + + public function testTooLong() + { + $constraint = new WordCount(max: 3, maxMessage: 'myMessage'); + $this->validator->validate('my beautiful ascii string', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ count }}', 4) + ->setParameter('{{ max }}', 3) + ->setPlural(3) + ->setInvalidValue('my beautiful ascii string') + ->assertRaised(); + } + + /** + * @dataProvider provideInvalidTypes + */ + public function testNonStringValues(mixed $value) + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessageMatches('/Expected argument of type "string", ".*" given/'); + + $this->validator->validate($value, new WordCount(min: 1)); + } + + public static function provideValidValues() + { + yield ['my ascii string', 3]; + yield [" with a\nnewline", 3]; + yield ["皆さん、こんにちは。", 4]; + yield ["你好,世界!这是一个测试。", 9]; + yield [new StringableValue('my ûtf 8'), 3]; + yield [null, 1]; // null should always pass and eventually be handled by NotNullValidator + yield ['', 1]; // empty string should always pass and eventually be handled by NotBlankValidator + } + + public static function provideInvalidTypes() + { + yield [true]; + yield [false]; + yield [1]; + yield [1.1]; + yield [[]]; + yield [new \stdClass()]; + } +} From a0f41acbeb320f1344978fda40a13494f9df2fbc Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 17 Jul 2024 19:44:04 +0200 Subject: [PATCH 066/149] [Validator] Fix constraints on `WordCount` properties --- Constraints/WordCount.php | 4 ++-- Tests/Constraints/WordCountTest.php | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Constraints/WordCount.php b/Constraints/WordCount.php index 6a85318b1..3c3c82f84 100644 --- a/Constraints/WordCount.php +++ b/Constraints/WordCount.php @@ -48,8 +48,8 @@ public function __construct( throw new MissingOptionsException(\sprintf('Either option "min" or "max" must be given for constraint "%s".', __CLASS__), ['min', 'max']); } - if (null !== $min && $min < 0) { - throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min word count to be a positive integer or 0 if set.', __CLASS__)); + if (null !== $min && $min <= 0) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min word count to be a positive integer if set.', __CLASS__)); } if (null !== $max && $max <= 0) { diff --git a/Tests/Constraints/WordCountTest.php b/Tests/Constraints/WordCountTest.php index 7ec911371..60263174f 100644 --- a/Tests/Constraints/WordCountTest.php +++ b/Tests/Constraints/WordCountTest.php @@ -48,15 +48,31 @@ public function testOnlyMaxIsSet() $this->assertNull($wordCount->locale); } - public function testMinMustBeNatural() + public function testMinIsNegative() { $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the min word count to be a positive integer or 0 if set.'); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the min word count to be a positive integer if set.'); new WordCount(-1); } - public function testMaxMustBePositive() + public function testMinIsZero() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the min word count to be a positive integer if set.'); + + new WordCount(0); + } + + public function testMaxIsNegative() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the max word count to be a positive integer if set.'); + + new WordCount(max: -1); + } + + public function testMaxIsZero() { $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\WordCount" constraint requires the max word count to be a positive integer if set.'); From 429664f956dc0b20ff7cc70618836947e6782b6a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 18 Jul 2024 10:11:58 +0200 Subject: [PATCH 067/149] [Validator] Unwrap useless cast --- Constraints/ChoiceValidator.php | 4 ++-- Constraints/CountValidator.php | 4 ++-- Constraints/LengthValidator.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Constraints/ChoiceValidator.php b/Constraints/ChoiceValidator.php index 4c70fceb2..7dd992f9f 100644 --- a/Constraints/ChoiceValidator.php +++ b/Constraints/ChoiceValidator.php @@ -80,7 +80,7 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->min && $count < $constraint->min) { $this->context->buildViolation($constraint->minMessage) ->setParameter('{{ limit }}', $constraint->min) - ->setPlural((int) $constraint->min) + ->setPlural($constraint->min) ->setCode(Choice::TOO_FEW_ERROR) ->addViolation(); @@ -90,7 +90,7 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->max && $count > $constraint->max) { $this->context->buildViolation($constraint->maxMessage) ->setParameter('{{ limit }}', $constraint->max) - ->setPlural((int) $constraint->max) + ->setPlural($constraint->max) ->setCode(Choice::TOO_MANY_ERROR) ->addViolation(); diff --git a/Constraints/CountValidator.php b/Constraints/CountValidator.php index 6aa425bdf..04f539333 100644 --- a/Constraints/CountValidator.php +++ b/Constraints/CountValidator.php @@ -44,7 +44,7 @@ public function validate(mixed $value, Constraint $constraint): void ->setParameter('{{ count }}', $count) ->setParameter('{{ limit }}', $constraint->max) ->setInvalidValue($value) - ->setPlural((int) $constraint->max) + ->setPlural($constraint->max) ->setCode($exactlyOptionEnabled ? Count::NOT_EQUAL_COUNT_ERROR : Count::TOO_MANY_ERROR) ->addViolation(); @@ -58,7 +58,7 @@ public function validate(mixed $value, Constraint $constraint): void ->setParameter('{{ count }}', $count) ->setParameter('{{ limit }}', $constraint->min) ->setInvalidValue($value) - ->setPlural((int) $constraint->min) + ->setPlural($constraint->min) ->setCode($exactlyOptionEnabled ? Count::NOT_EQUAL_COUNT_ERROR : Count::TOO_FEW_ERROR) ->addViolation(); diff --git a/Constraints/LengthValidator.php b/Constraints/LengthValidator.php index ea35125ad..985660bc2 100644 --- a/Constraints/LengthValidator.php +++ b/Constraints/LengthValidator.php @@ -76,7 +76,7 @@ public function validate(mixed $value, Constraint $constraint): void ->setParameter('{{ limit }}', $constraint->max) ->setParameter('{{ value_length }}', $length) ->setInvalidValue($value) - ->setPlural((int) $constraint->max) + ->setPlural($constraint->max) ->setCode($exactlyOptionEnabled ? Length::NOT_EQUAL_LENGTH_ERROR : Length::TOO_LONG_ERROR) ->addViolation(); @@ -91,7 +91,7 @@ public function validate(mixed $value, Constraint $constraint): void ->setParameter('{{ limit }}', $constraint->min) ->setParameter('{{ value_length }}', $length) ->setInvalidValue($value) - ->setPlural((int) $constraint->min) + ->setPlural($constraint->min) ->setCode($exactlyOptionEnabled ? Length::NOT_EQUAL_LENGTH_ERROR : Length::TOO_SHORT_ERROR) ->addViolation(); } From 82e4baf2998361ee02efac4186e71fbbc827deff Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 18 Jul 2024 10:20:59 +0200 Subject: [PATCH 068/149] [Validator] Use CPP in `ConstraintViolation` --- ConstraintViolation.php | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/ConstraintViolation.php b/ConstraintViolation.php index 5129ccf95..380ba4c84 100644 --- a/ConstraintViolation.php +++ b/ConstraintViolation.php @@ -18,17 +18,6 @@ */ class ConstraintViolation implements ConstraintViolationInterface { - private string|\Stringable $message; - private ?string $messageTemplate; - private array $parameters; - private ?int $plural; - private mixed $root; - private ?string $propertyPath; - private mixed $invalidValue; - private ?Constraint $constraint; - private ?string $code; - private mixed $cause; - /** * Creates a new constraint violation. * @@ -49,18 +38,18 @@ class ConstraintViolation implements ConstraintViolationInterface * caused the violation * @param mixed $cause The cause of the violation */ - public function __construct(string|\Stringable $message, ?string $messageTemplate, array $parameters, mixed $root, ?string $propertyPath, mixed $invalidValue, ?int $plural = null, ?string $code = null, ?Constraint $constraint = null, mixed $cause = null) - { - $this->message = $message; - $this->messageTemplate = $messageTemplate; - $this->parameters = $parameters; - $this->plural = $plural; - $this->root = $root; - $this->propertyPath = $propertyPath; - $this->invalidValue = $invalidValue; - $this->constraint = $constraint; - $this->code = $code; - $this->cause = $cause; + public function __construct( + private string|\Stringable $message, + private ?string $messageTemplate, + private array $parameters, + private mixed $root, + private ?string $propertyPath, + private mixed $invalidValue, + private ?int $plural = null, + private ?string $code = null, + private ?Constraint $constraint = null, + private mixed $cause = null, + ) { } public function __toString(): string From 6e0c2a2b5857f34f43f501939773578ebab9808d Mon Sep 17 00:00:00 2001 From: "andreybolonin1989@gmail.com" Date: Sun, 21 Jul 2024 02:51:07 +0300 Subject: [PATCH 069/149] [Validator] Use CPP in AbstractComparisonValidator, BicValidator, RangeValidator --- Constraints/AbstractComparisonValidator.php | 5 +---- Constraints/BicValidator.php | 5 +---- Constraints/RangeValidator.php | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Constraints/AbstractComparisonValidator.php b/Constraints/AbstractComparisonValidator.php index 94c789ae6..e2081a1b4 100644 --- a/Constraints/AbstractComparisonValidator.php +++ b/Constraints/AbstractComparisonValidator.php @@ -27,11 +27,8 @@ */ abstract class AbstractComparisonValidator extends ConstraintValidator { - private ?PropertyAccessorInterface $propertyAccessor; - - public function __construct(?PropertyAccessorInterface $propertyAccessor = null) + public function __construct(private ?PropertyAccessorInterface $propertyAccessor = null) { - $this->propertyAccessor = $propertyAccessor; } public function validate(mixed $value, Constraint $constraint): void diff --git a/Constraints/BicValidator.php b/Constraints/BicValidator.php index 2e299e8f6..c8bd1e472 100644 --- a/Constraints/BicValidator.php +++ b/Constraints/BicValidator.php @@ -56,11 +56,8 @@ class BicValidator extends ConstraintValidator 'EA' => 'ES', // Ceuta and Melilla ]; - private ?PropertyAccessor $propertyAccessor; - - public function __construct(?PropertyAccessor $propertyAccessor = null) + public function __construct(private ?PropertyAccessor $propertyAccessor = null) { - $this->propertyAccessor = $propertyAccessor; } public function validate(mixed $value, Constraint $constraint): void diff --git a/Constraints/RangeValidator.php b/Constraints/RangeValidator.php index 1f359c528..602cb032f 100644 --- a/Constraints/RangeValidator.php +++ b/Constraints/RangeValidator.php @@ -24,11 +24,8 @@ */ class RangeValidator extends ConstraintValidator { - private ?PropertyAccessorInterface $propertyAccessor; - - public function __construct(?PropertyAccessorInterface $propertyAccessor = null) + public function __construct(private ?PropertyAccessorInterface $propertyAccessor = null) { - $this->propertyAccessor = $propertyAccessor; } public function validate(mixed $value, Constraint $constraint): void From 35ef1932ba4af00de51b9c9dcf3051d604cdcb10 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 22 Jul 2024 09:20:55 +0200 Subject: [PATCH 070/149] Replace `TestCase::assertTrue(true)` with `TestCase::expectNotToPerformAssertions()` --- Tests/Constraints/NotCompromisedPasswordValidatorTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php index 253529444..3ff24eb94 100644 --- a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php +++ b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php @@ -213,8 +213,9 @@ public function testApiError() */ public function testApiErrorSkipped(NotCompromisedPassword $constraint) { + $this->expectNotToPerformAssertions(); + $this->validator->validate(self::PASSWORD_TRIGGERING_AN_ERROR, $constraint); - $this->assertTrue(true); // No exception have been thrown } public static function provideErrorSkippingConstraints(): iterable From 0c468dc03bcd149522772aae6ee4969e01621adb Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 22 Jul 2024 10:27:43 +0200 Subject: [PATCH 071/149] Use CPP where possible --- Constraints/ExpressionSyntaxValidator.php | 8 +++--- Test/ConstraintValidatorTestCase.php | 30 +++++++++-------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/Constraints/ExpressionSyntaxValidator.php b/Constraints/ExpressionSyntaxValidator.php index f377406a2..ff63652c4 100644 --- a/Constraints/ExpressionSyntaxValidator.php +++ b/Constraints/ExpressionSyntaxValidator.php @@ -23,11 +23,9 @@ */ class ExpressionSyntaxValidator extends ConstraintValidator { - private ?ExpressionLanguage $expressionLanguage; - - public function __construct(?ExpressionLanguage $expressionLanguage = null) - { - $this->expressionLanguage = $expressionLanguage; + public function __construct( + private ?ExpressionLanguage $expressionLanguage = null, + ) { } public function validate(mixed $expression, Constraint $constraint): void diff --git a/Test/ConstraintValidatorTestCase.php b/Test/ConstraintValidatorTestCase.php index ecb91e2b3..1f97d7f50 100644 --- a/Test/ConstraintValidatorTestCase.php +++ b/Test/ConstraintValidatorTestCase.php @@ -296,31 +296,24 @@ abstract protected function createValidator(): ConstraintValidatorInterface; final class ConstraintViolationAssertion { - private ExecutionContextInterface $context; - - /** - * @var ConstraintViolationAssertion[] - */ - private array $assertions; - - private string $message; private array $parameters = []; private mixed $invalidValue = 'InvalidValue'; private string $propertyPath = 'property.path'; private ?int $plural = null; private ?string $code = null; - private ?Constraint $constraint; private mixed $cause = null; /** + * @param ConstraintViolationAssertion[] $assertions + * * @internal */ - public function __construct(ExecutionContextInterface $context, string $message, ?Constraint $constraint = null, array $assertions = []) - { - $this->context = $context; - $this->message = $message; - $this->constraint = $constraint; - $this->assertions = $assertions; + public function __construct( + private ExecutionContextInterface $context, + private string $message, + private ?Constraint $constraint = null, + private array $assertions = [], + ) { } /** @@ -453,16 +446,15 @@ private function getViolation(): ConstraintViolation */ class AssertingContextualValidator implements ContextualValidatorInterface { - private ExecutionContextInterface $context; private bool $expectNoValidate = false; private int $atPathCalls = -1; private array $expectedAtPath = []; private int $validateCalls = -1; private array $expectedValidate = []; - public function __construct(ExecutionContextInterface $context) - { - $this->context = $context; + public function __construct( + private ExecutionContextInterface $context, + ) { } public function __destruct() From 5dbccf1867a686beb3f6d6f9eb899d6cd7b1ae04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oriol=20Vi=C3=B1als?= Date: Wed, 24 Jul 2024 16:18:16 +0200 Subject: [PATCH 072/149] [Validator] Add Catalan and Spanish translation for `WordCount` constraint" --- Resources/translations/validators.ca.xlf | 4 ++-- Resources/translations/validators.es.xlf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/translations/validators.ca.xlf b/Resources/translations/validators.ca.xlf index d0fd3a8aa..60f747f62 100644 --- a/Resources/translations/validators.ca.xlf +++ b/Resources/translations/validators.ca.xlf @@ -444,11 +444,11 @@ This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. - This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. + Aquest valor és massa curt. Ha de contenir almenys una paraula.|Aquest valor és massa curt. Ha de contenir almenys {{ min }} paraules. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. - This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + Aquest valor és massa llarg. Ha de contenir una paraula.|Aquest valor és massa llarg. Ha de contenir {{ max }} paraules o menys. diff --git a/Resources/translations/validators.es.xlf b/Resources/translations/validators.es.xlf index 66ce4b60c..f9b327722 100644 --- a/Resources/translations/validators.es.xlf +++ b/Resources/translations/validators.es.xlf @@ -444,11 +444,11 @@ This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. - This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. + Este valor es demasiado corto. Debe contener al menos una palabra.|Este valor es demasiado corto. Debe contener al menos {{ min }} palabras. This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. - This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less. + Este valor es demasiado largo. Debe contener una palabra.|Este valor es demasiado largo. Debe contener {{ max }} palabras o menos. From 0a47a7b327d95c5ffe56c9e68db4f8930ac4245e Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 31 Jul 2024 16:13:26 +0200 Subject: [PATCH 073/149] Remove unused code and unnecessary `else` branches --- Constraints/ChoiceValidator.php | 2 -- Constraints/EmailValidator.php | 6 ------ Constraints/Regex.php | 4 +--- Constraints/UrlValidator.php | 2 -- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Constraints/ChoiceValidator.php b/Constraints/ChoiceValidator.php index 7dd992f9f..02b6697a6 100644 --- a/Constraints/ChoiceValidator.php +++ b/Constraints/ChoiceValidator.php @@ -93,8 +93,6 @@ public function validate(mixed $value, Constraint $constraint): void ->setPlural($constraint->max) ->setCode(Choice::TOO_MANY_ERROR) ->addViolation(); - - return; } } elseif ($constraint->match xor \in_array($value, $choices, true)) { $this->context->buildViolation($constraint->message) diff --git a/Constraints/EmailValidator.php b/Constraints/EmailValidator.php index b6732775d..8ede9a72e 100644 --- a/Constraints/EmailValidator.php +++ b/Constraints/EmailValidator.php @@ -88,23 +88,17 @@ public function validate(mixed $value, Constraint $constraint): void ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Email::INVALID_FORMAT_ERROR) ->addViolation(); - - return; } elseif (!interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, false, true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Email::INVALID_FORMAT_ERROR) ->addViolation(); - - return; } } elseif (!preg_match(self::EMAIL_PATTERNS[$constraint->mode], $value)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Email::INVALID_FORMAT_ERROR) ->addViolation(); - - return; } } } diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 7cd8301f7..4a2a90610 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -120,8 +120,6 @@ public function getHtmlPattern(): ?string $pattern = '^' === $pattern[0] ? substr($pattern, 1) : '.*'.$pattern; // Trim trailing $, otherwise append .* - $pattern = '$' === $pattern[\strlen($pattern) - 1] ? substr($pattern, 0, -1) : $pattern.'.*'; - - return $pattern; + return '$' === $pattern[\strlen($pattern) - 1] ? substr($pattern, 0, -1) : $pattern.'.*'; } } diff --git a/Constraints/UrlValidator.php b/Constraints/UrlValidator.php index 70c562731..0eeb7f5dd 100644 --- a/Constraints/UrlValidator.php +++ b/Constraints/UrlValidator.php @@ -88,8 +88,6 @@ public function validate(mixed $value, Constraint $constraint): void ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Url::MISSING_TLD_ERROR) ->addViolation(); - - return; } } } From 94e7465b1271ba024bd96a424da037e3390184a5 Mon Sep 17 00:00:00 2001 From: Sylvain Just Date: Wed, 31 Jul 2024 13:32:38 +0200 Subject: [PATCH 074/149] [Validator] add tldMessage in URL constructor according to the following documentation https://symfony.com/doc/current/reference/constraints/Url.html#tldmessage --- Constraints/Url.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Constraints/Url.php b/Constraints/Url.php index 9481f9395..24921af1a 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -54,6 +54,7 @@ public function __construct( ?array $groups = null, mixed $payload = null, ?bool $requireTld = null, + ?string $tldMessage = null, ) { parent::__construct($options, $groups, $payload); @@ -66,6 +67,7 @@ public function __construct( $this->relativeProtocol = $relativeProtocol ?? $this->relativeProtocol; $this->normalizer = $normalizer ?? $this->normalizer; $this->requireTld = $requireTld ?? $this->requireTld; + $this->tldMessage = $tldMessage ?? $this->tldMessage; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); From b5e77ded6827f86251c7c37d757ea597acee7ca5 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 5 Aug 2024 09:12:25 +0200 Subject: [PATCH 075/149] Fix multiple CS errors --- Constraints/Ulid.php | 2 +- ...ssThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php | 1 - .../LessThanValidatorWithNegativeConstraintTest.php | 1 - Tests/Constraints/WordCountTest.php | 2 +- Tests/Constraints/WordCountValidatorTest.php | 4 ++-- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index caf92b022..d4d0a0a1d 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -60,7 +60,7 @@ public function __construct( $this->format = $format ?? $this->format; if (!\in_array($this->format, [self::FORMAT_BASE_32, self::FORMAT_BASE_58], true)) { - throw new ConstraintDefinitionException(sprintf('The "%s" validation format is not supported.', $format)); + throw new ConstraintDefinitionException(\sprintf('The "%s" validation format is not supported.', $format)); } } } diff --git a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php index 1d93fef22..2f6d5d624 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Validator\Tests\Constraints; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\AbstractComparison; use Symfony\Component\Validator\Constraints\NegativeOrZero; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; diff --git a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php index 0179c2611..c52d8bc2d 100644 --- a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php +++ b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Validator\Tests\Constraints; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\AbstractComparison; use Symfony\Component\Validator\Constraints\Negative; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; diff --git a/Tests/Constraints/WordCountTest.php b/Tests/Constraints/WordCountTest.php index 60263174f..50ed8081d 100644 --- a/Tests/Constraints/WordCountTest.php +++ b/Tests/Constraints/WordCountTest.php @@ -113,7 +113,7 @@ public function testAttributes() [$aConstraint] = $metadata->properties['a']->getConstraints(); $this->assertSame(1, $aConstraint->min); - $this->assertSame(null, $aConstraint->max); + $this->assertNull($aConstraint->max); $this->assertNull($aConstraint->locale); [$bConstraint] = $metadata->properties['b']->getConstraints(); diff --git a/Tests/Constraints/WordCountValidatorTest.php b/Tests/Constraints/WordCountValidatorTest.php index 65f34947b..3e3b760c4 100644 --- a/Tests/Constraints/WordCountValidatorTest.php +++ b/Tests/Constraints/WordCountValidatorTest.php @@ -78,8 +78,8 @@ public static function provideValidValues() { yield ['my ascii string', 3]; yield [" with a\nnewline", 3]; - yield ["皆さん、こんにちは。", 4]; - yield ["你好,世界!这是一个测试。", 9]; + yield ['皆さん、こんにちは。', 4]; + yield ['你好,世界!这是一个测试。', 9]; yield [new StringableValue('my ûtf 8'), 3]; yield [null, 1]; // null should always pass and eventually be handled by NotNullValidator yield ['', 1]; // empty string should always pass and eventually be handled by NotBlankValidator From 831f2590214a7920c7bed09ccb70d05765a210d1 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Thu, 1 Aug 2024 17:21:17 +0200 Subject: [PATCH 076/149] Code style change in `@PER-CS2.0` affecting `@Symfony` (parentheses for anonymous classes) --- Tests/Constraints/AtLeastOneOfValidatorTest.php | 6 +++--- Tests/Constraints/RegexValidatorTest.php | 4 ++-- Tests/Mapping/Loader/PropertyInfoLoaderTest.php | 6 +++--- ValidatorBuilder.php | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/Constraints/AtLeastOneOfValidatorTest.php b/Tests/Constraints/AtLeastOneOfValidatorTest.php index 7cf1dcb76..2364631d4 100644 --- a/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -189,7 +189,7 @@ public function testGroupsArePropagatedToNestedConstraints() public function testContextIsPropagatedToNestedConstraints() { $validator = Validation::createValidatorBuilder() - ->setMetadataFactory(new class() implements MetadataFactoryInterface { + ->setMetadataFactory(new class implements MetadataFactoryInterface { public function getMetadataFor($classOrObject): MetadataInterface { return (new ClassMetadata(ExpressionConstraintNested::class)) @@ -215,7 +215,7 @@ public function hasMetadataFor($classOrObject): bool public function testEmbeddedMessageTakenFromFailingConstraint() { $validator = Validation::createValidatorBuilder() - ->setMetadataFactory(new class() implements MetadataFactoryInterface { + ->setMetadataFactory(new class implements MetadataFactoryInterface { public function getMetadataFor($classOrObject): MetadataInterface { return (new ClassMetadata(Data::class)) @@ -265,7 +265,7 @@ public function testNestedConstraintsAreNotExecutedWhenGroupDoesNotMatch() public function testTranslatorIsCalledOnConstraintBaseMessageAndViolations() { - $translator = new class() implements TranslatorInterface, LocaleAwareInterface { + $translator = new class implements TranslatorInterface, LocaleAwareInterface { use TranslatorTrait; public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string diff --git a/Tests/Constraints/RegexValidatorTest.php b/Tests/Constraints/RegexValidatorTest.php index 360fb6259..82739f0e3 100644 --- a/Tests/Constraints/RegexValidatorTest.php +++ b/Tests/Constraints/RegexValidatorTest.php @@ -83,7 +83,7 @@ public static function getValidValues() ['0'], ['090909'], [90909], - [new class() { + [new class { public function __toString(): string { return '090909'; @@ -144,7 +144,7 @@ public static function getInvalidValues() return [ ['abcd'], ['090foo'], - [new class() { + [new class { public function __toString(): string { return 'abcd'; diff --git a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php index f4bb4fbd4..fde1d7d57 100644 --- a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php +++ b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php @@ -58,7 +58,7 @@ public function testLoadClassMetadata() ]) ; - $propertyTypeExtractor = new class() implements PropertyTypeExtractorInterface { + $propertyTypeExtractor = new class implements PropertyTypeExtractorInterface { private int $i = 0; private int $j = 0; private array $types; @@ -234,7 +234,7 @@ public function testClassValidator(bool $expected, ?string $classValidatorRegexp ->willReturn(['string']) ; - $propertyTypeExtractor = new class() implements PropertyTypeExtractorInterface { + $propertyTypeExtractor = new class implements PropertyTypeExtractorInterface { public function getType(string $class, string $property, array $context = []): ?Type { return Type::string(); @@ -273,7 +273,7 @@ public function testClassNoAutoMapping(?PropertyTypeExtractorInterface $property ->willReturn(['string', 'autoMappingExplicitlyEnabled']) ; - $propertyTypeExtractor = new class() implements PropertyTypeExtractorInterface { + $propertyTypeExtractor = new class implements PropertyTypeExtractorInterface { public function getType(string $class, string $property, array $context = []): ?Type { return Type::string(); diff --git a/ValidatorBuilder.php b/ValidatorBuilder.php index 00e7d5ce7..83e543b8f 100644 --- a/ValidatorBuilder.php +++ b/ValidatorBuilder.php @@ -352,7 +352,7 @@ public function getValidator(): ValidatorInterface $translator = $this->translator; if (null === $translator) { - $translator = new class() implements TranslatorInterface, LocaleAwareInterface { + $translator = new class implements TranslatorInterface, LocaleAwareInterface { use TranslatorTrait; }; // Force the locale to be 'en' when no translator is provided rather than relying on the Intl default locale From 8b73bba95fd574d4ca45a352ef485e31638b27f2 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 7 Aug 2024 10:39:45 +0200 Subject: [PATCH 077/149] [Validator] Add support for RFC4122 format in the `Ulid` constraint --- Constraints/Ulid.php | 9 ++++-- Constraints/UlidValidator.php | 11 ++++++++ Tests/Constraints/UlidTest.php | 3 +- Tests/Constraints/UlidValidatorTest.php | 37 +++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index d4d0a0a1d..b73757c13 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -26,18 +26,21 @@ class Ulid extends Constraint { public const TOO_SHORT_ERROR = '7b44804e-37d5-4df4-9bdd-b738d4a45bb4'; public const TOO_LONG_ERROR = '9608249f-6da1-4d53-889e-9864b58c4d37'; - public const INVALID_CHARACTERS_ERROR = 'e4155739-5135-4258-9c81-ae7b44b5311e'; public const TOO_LARGE_ERROR = 'df8cfb9a-ce6d-4a69-ae5a-eea7ab6f278b'; + public const INVALID_CHARACTERS_ERROR = 'e4155739-5135-4258-9c81-ae7b44b5311e'; + public const INVALID_FORMAT_ERROR = '34d5cdd7-5aac-4ba0-b9a2-b45e0bab3e2e'; protected const ERROR_NAMES = [ self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', - self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', ]; public const FORMAT_BASE_32 = 'base32'; public const FORMAT_BASE_58 = 'base58'; + public const FORMAT_RFC_4122 = 'rfc4122'; public string $message = 'This is not a valid ULID.'; public string $format = self::FORMAT_BASE_32; @@ -59,7 +62,7 @@ public function __construct( $this->message = $message ?? $this->message; $this->format = $format ?? $this->format; - if (!\in_array($this->format, [self::FORMAT_BASE_32, self::FORMAT_BASE_58], true)) { + if (!\in_array($this->format, [self::FORMAT_BASE_32, self::FORMAT_BASE_58, self::FORMAT_RFC_4122], true)) { throw new ConstraintDefinitionException(\sprintf('The "%s" validation format is not supported.', $format)); } } diff --git a/Constraints/UlidValidator.php b/Constraints/UlidValidator.php index e4133d2dc..ae49ad34b 100644 --- a/Constraints/UlidValidator.php +++ b/Constraints/UlidValidator.php @@ -43,6 +43,7 @@ public function validate(mixed $value, Constraint $constraint): void [$requiredLength, $requiredCharset] = match ($constraint->format) { Ulid::FORMAT_BASE_32 => [26, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz'], Ulid::FORMAT_BASE_58 => [22, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'], + Ulid::FORMAT_RFC_4122 => [36, '0123456789ABCDEFabcdef-'], }; if ($requiredLength !== \strlen($value)) { @@ -81,6 +82,16 @@ public function validate(mixed $value, Constraint $constraint): void ->setCode(Ulid::TOO_LARGE_ERROR) ->addViolation(); } + } elseif (Ulid::FORMAT_RFC_4122 === $constraint->format) { + if (!preg_match('/^[^-]{8}-[^-]{4}-[^-]{4}-[^-]{4}-[^-]{12}$/', $value)) { + $this->context->buildViolation($constraint->message) + ->setParameters([ + '{{ value }}' => $this->formatValue($value), + '{{ format }}' => $constraint->format, + ]) + ->setCode(Ulid::INVALID_FORMAT_ERROR) + ->addViolation(); + } } } } diff --git a/Tests/Constraints/UlidTest.php b/Tests/Constraints/UlidTest.php index bb12ef0e9..86cc32266 100644 --- a/Tests/Constraints/UlidTest.php +++ b/Tests/Constraints/UlidTest.php @@ -28,6 +28,7 @@ public function testAttributes() [$bConstraint] = $metadata->properties['b']->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'UlidDummy'], $bConstraint->groups); + self::assertSame(Ulid::FORMAT_BASE_58, $bConstraint->format); [$cConstraint] = $metadata->properties['c']->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); @@ -48,7 +49,7 @@ class UlidDummy #[Ulid] private $a; - #[Ulid(message: 'myMessage')] + #[Ulid(message: 'myMessage', format: Ulid::FORMAT_BASE_58)] private $b; #[Ulid(groups: ['my_group'], payload: 'some attached data')] diff --git a/Tests/Constraints/UlidValidatorTest.php b/Tests/Constraints/UlidValidatorTest.php index 7d2be16e2..abacdfdc5 100644 --- a/Tests/Constraints/UlidValidatorTest.php +++ b/Tests/Constraints/UlidValidatorTest.php @@ -60,6 +60,13 @@ public function testValidUlidAsBase58() $this->assertNoViolation(); } + public function testValidUlidAsRfc4122() + { + $this->validator->validate('01912bf3-feff-fa6c-00f2-90d2f2e00564', new Ulid(format: Ulid::FORMAT_RFC_4122)); + + $this->assertNoViolation(); + } + /** * @dataProvider getInvalidUlids */ @@ -119,6 +126,36 @@ public static function getInvalidBase58Ulids(): array ]; } + /** + * @dataProvider getInvalidRfc4122Ulids + */ + public function testInvalidInvalid4122Ulid(string $ulid, string $code) + { + $constraint = new Ulid(message: 'testMessage', format: Ulid::FORMAT_RFC_4122); + + $this->validator->validate($ulid, $constraint); + + $this->buildViolation('testMessage') + ->setParameters([ + '{{ value }}' => '"'.$ulid.'"', + '{{ format }}' => Ulid::FORMAT_RFC_4122, + ]) + ->setCode($code) + ->assertRaised(); + } + + public static function getInvalidRfc4122Ulids(): array + { + return [ + ['01912bf3-f5b7-e55d', Ulid::TOO_SHORT_ERROR], + ['01912bf3-f5b7-e55d-d21f-5ef032cd8e29999999', Ulid::TOO_LONG_ERROR], + ['01912bf3-f5b7-e55d-d21f-5ef032cd8eZZ', Ulid::INVALID_CHARACTERS_ERROR], + ['not-even-ulid-like', Ulid::TOO_SHORT_ERROR], + ['01912bf30feff0fa6c000f2090d2f2e00564', Ulid::INVALID_FORMAT_ERROR], + ['019-2bf3-feff-fa6c-00f2-90d2f2e00564', Ulid::INVALID_FORMAT_ERROR], + ]; + } + public function testInvalidUlidNamed() { $constraint = new Ulid(message: 'testMessage'); From d441373782e4a83ffd2771ff19c7a282b3cf99dc Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 29 Jul 2024 09:33:48 +0200 Subject: [PATCH 078/149] Remove useless code --- Constraint.php | 2 +- Test/ConstraintValidatorTestCase.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Constraint.php b/Constraint.php index e04dc81e1..98bd4b116 100644 --- a/Constraint.php +++ b/Constraint.php @@ -129,7 +129,7 @@ protected function normalizeOptions(mixed $options): array $normalizedOptions = []; $defaultOption = $this->getDefaultOption(); $invalidOptions = []; - $missingOptions = array_flip((array) $this->getRequiredOptions()); + $missingOptions = array_flip($this->getRequiredOptions()); $knownOptions = get_class_vars(static::class); if (\is_array($options) && isset($options['value']) && !property_exists($this, 'value')) { diff --git a/Test/ConstraintValidatorTestCase.php b/Test/ConstraintValidatorTestCase.php index 1f97d7f50..71d87b753 100644 --- a/Test/ConstraintValidatorTestCase.php +++ b/Test/ConstraintValidatorTestCase.php @@ -235,7 +235,7 @@ protected function expectValidateValue(int $i, mixed $value, array $constraints { $contextualValidator = $this->context->getValidator()->inContext($this->context); $contextualValidator->expectValidation($i, null, $value, $group, function ($passedConstraints) use ($constraints) { - if (\is_array($constraints) && !\is_array($passedConstraints)) { + if (!\is_array($passedConstraints)) { $passedConstraints = [$passedConstraints]; } @@ -247,7 +247,7 @@ protected function expectFailingValueValidation(int $i, mixed $value, array $con { $contextualValidator = $this->context->getValidator()->inContext($this->context); $contextualValidator->expectValidation($i, null, $value, $group, function ($passedConstraints) use ($constraints) { - if (\is_array($constraints) && !\is_array($passedConstraints)) { + if (!\is_array($passedConstraints)) { $passedConstraints = [$passedConstraints]; } From 6c9baedc81f8b61aadadaa2413e4409f7e47b45d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 2 Aug 2024 10:08:15 +0200 Subject: [PATCH 079/149] [Validator] Add `Week` constraint --- CHANGELOG.md | 1 + Constraints/Week.php | 66 +++++++++++ Constraints/WeekValidator.php | 82 ++++++++++++++ Tests/Constraints/WeekTest.php | 101 +++++++++++++++++ Tests/Constraints/WeekValidatorTest.php | 142 ++++++++++++++++++++++++ 5 files changed, 392 insertions(+) create mode 100644 Constraints/Week.php create mode 100644 Constraints/WeekValidator.php create mode 100644 Tests/Constraints/WeekTest.php create mode 100644 Tests/Constraints/WeekValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 46709ac0a..d8ae5e6cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Add `errorPath` to Unique constraint * Add the `format` option to the `Ulid` constraint to allow accepting different ULID formats * Add the `WordCount` constraint + * Add the `Week` constraint 7.1 --- diff --git a/Constraints/Week.php b/Constraints/Week.php new file mode 100644 index 000000000..46b886975 --- /dev/null +++ b/Constraints/Week.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Attribute\HasNamedArguments; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; + +/** + * @author Alexandre Daubois + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Week extends Constraint +{ + public const INVALID_FORMAT_ERROR = '19012dd1-01c8-4ce8-959f-72ad22684f5f'; + public const INVALID_WEEK_NUMBER_ERROR = 'd67ebfc9-45fe-4e4c-a038-5eaa56895ea3'; + public const TOO_LOW_ERROR = '9b506423-77a3-4749-aa34-c822a08be978'; + public const TOO_HIGH_ERROR = '85156377-d1e6-42cd-8f6e-dc43c2ecb72b'; + + protected const ERROR_NAMES = [ + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', + self::INVALID_WEEK_NUMBER_ERROR => 'INVALID_WEEK_NUMBER_ERROR', + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', + ]; + + #[HasNamedArguments] + public function __construct( + public ?string $min = null, + public ?string $max = null, + public string $invalidFormatMessage = 'This value does not represent a valid week in the ISO 8601 format.', + public string $invalidWeekNumberMessage = 'The week "{{ value }}" is not a valid week.', + public string $tooLowMessage = 'The value should not be before week "{{ min }}".', + public string $tooHighMessage = 'The value should not be after week "{{ max }}".', + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct(null, $groups, $payload); + + if (null !== $min && !preg_match('/^\d{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$/', $min)) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min week to be in the ISO 8601 format if set.', __CLASS__)); + } + + if (null !== $max && !preg_match('/^\d{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$/', $max)) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the max week to be in the ISO 8601 format if set.', __CLASS__)); + } + + if (null !== $min && null !== $max) { + [$minYear, $minWeekNumber] = \explode('-W', $min, 2); + [$maxYear, $maxWeekNumber] = \explode('-W', $max, 2); + + if ($minYear > $maxYear || ($minYear === $maxYear && $minWeekNumber > $maxWeekNumber)) { + throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min week to be less than or equal to the max week.', __CLASS__)); + } + } + } +} diff --git a/Constraints/WeekValidator.php b/Constraints/WeekValidator.php new file mode 100644 index 000000000..83052c1a9 --- /dev/null +++ b/Constraints/WeekValidator.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; + +/** + * @author Alexandre Daubois + */ +final class WeekValidator extends ConstraintValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof Week) { + throw new UnexpectedTypeException($constraint, Week::class); + } + + if (null === $value) { + return; + } + + if (!\is_string($value) && !$value instanceof \Stringable) { + throw new UnexpectedValueException($value, 'string'); + } + + if (!preg_match('/^\d{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$/D', $value)) { + $this->context->buildViolation($constraint->invalidFormatMessage) + ->setCode(Week::INVALID_FORMAT_ERROR) + ->addViolation(); + + return; + } + + [$year, $weekNumber] = \explode('-W', $value, 2); + $weeksInYear = (int) \date('W', \mktime(0, 0, 0, 12, 28, $year)); + + if ($weekNumber > $weeksInYear) { + $this->context->buildViolation($constraint->invalidWeekNumberMessage) + ->setCode(Week::INVALID_WEEK_NUMBER_ERROR) + ->setParameter('{{ value }}', $value) + ->addViolation(); + + return; + } + + if ($constraint->min) { + [$minYear, $minWeekNumber] = \explode('-W', $constraint->min, 2); + if ($year < $minYear || ($year === $minYear && $weekNumber < $minWeekNumber)) { + $this->context->buildViolation($constraint->tooLowMessage) + ->setCode(Week::TOO_LOW_ERROR) + ->setInvalidValue($value) + ->setParameter('{{ min }}', $constraint->min) + ->addViolation(); + + return; + } + } + + if ($constraint->max) { + [$maxYear, $maxWeekNumber] = \explode('-W', $constraint->max, 2); + if ($year > $maxYear || ($year === $maxYear && $weekNumber > $maxWeekNumber)) { + $this->context->buildViolation($constraint->tooHighMessage) + ->setCode(Week::TOO_HIGH_ERROR) + ->setInvalidValue($value) + ->setParameter('{{ max }}', $constraint->max) + ->addViolation(); + } + } + } +} diff --git a/Tests/Constraints/WeekTest.php b/Tests/Constraints/WeekTest.php new file mode 100644 index 000000000..0fc9aac62 --- /dev/null +++ b/Tests/Constraints/WeekTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Week; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; + +class WeekTest extends TestCase +{ + public function testWithoutArgument() + { + $week = new Week(); + + $this->assertNull($week->min); + $this->assertNull($week->max); + } + + public function testConstructor() + { + $week = new Week(min: '2010-W01', max: '2010-W02'); + + $this->assertSame('2010-W01', $week->min); + $this->assertSame('2010-W02', $week->max); + } + + public function testMinYearIsAfterMaxYear() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Week" constraint requires the min week to be less than or equal to the max week.'); + + new Week(min: '2011-W01', max: '2010-W02'); + } + + public function testMinWeekIsAfterMaxWeek() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Week" constraint requires the min week to be less than or equal to the max week.'); + + new Week(min: '2010-W02', max: '2010-W01'); + } + + public function testMinAndMaxWeeksAreTheSame() + { + $week = new Week(min: '2010-W01', max: '2010-W01'); + + $this->assertSame('2010-W01', $week->min); + $this->assertSame('2010-W01', $week->max); + } + + public function testMinIsBadlyFormatted() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Week" constraint requires the min week to be in the ISO 8601 format if set.'); + + new Week(min: '2010-01'); + } + + public function testMaxIsBadlyFormatted() + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage('The "Symfony\Component\Validator\Constraints\Week" constraint requires the max week to be in the ISO 8601 format if set.'); + + new Week(max: '2010-01'); + } + + public function testAttributes() + { + $metadata = new ClassMetadata(WeekDummy::class); + $loader = new AttributeLoader(); + $this->assertTrue($loader->loadClassMetadata($metadata)); + + [$aConstraint] = $metadata->properties['a']->getConstraints(); + $this->assertNull($aConstraint->min); + $this->assertNull($aConstraint->max); + + [$bConstraint] = $metadata->properties['b']->getConstraints(); + $this->assertSame('2010-W01', $bConstraint->min); + $this->assertSame('2010-W02', $bConstraint->max); + } +} + +class WeekDummy +{ + #[Week] + private string $a; + + #[Week(min: '2010-W01', max: '2010-W02')] + private string $b; +} diff --git a/Tests/Constraints/WeekValidatorTest.php b/Tests/Constraints/WeekValidatorTest.php new file mode 100644 index 000000000..08bc3b29b --- /dev/null +++ b/Tests/Constraints/WeekValidatorTest.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\Week; +use Symfony\Component\Validator\Constraints\WeekValidator; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; + +class WeekValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): WeekValidator + { + return new WeekValidator(); + } + + /** + * @dataProvider provideWeekNumber + */ + public function testWeekIsValidWeekNumber(string|\Stringable $value, bool $expectedViolation) + { + $constraint = new Week(); + $this->validator->validate($value, $constraint); + + if ($expectedViolation) { + $this->buildViolation('The week "{{ value }}" is not a valid week.') + ->setCode(Week::INVALID_WEEK_NUMBER_ERROR) + ->setParameter('{{ value }}', $value) + ->assertRaised(); + + return; + } + + $this->assertNoViolation(); + } + + public static function provideWeekNumber() + { + yield ['2015-W53', false]; // 2015 has 53 weeks + yield ['2020-W53', false]; // 2020 also has 53 weeks + yield ['2024-W53', true]; // 2024 has 52 weeks + yield [new StringableValue('2024-W53'), true]; + } + + public function testBounds() + { + $constraint = new Week(min: '2015-W10', max: '2016-W25'); + + $this->validator->validate('2015-W10', $constraint); + $this->assertNoViolation(); + + $this->validator->validate('2016-W25', $constraint); + $this->assertNoViolation(); + } + + public function testTooLow() + { + $constraint = new Week(min: '2015-W10'); + + $this->validator->validate('2015-W08', $constraint); + $this->buildViolation('The value should not be before week "{{ min }}".') + ->setInvalidValue('2015-W08') + ->setParameter('{{ min }}', '2015-W10') + ->setCode(Week::TOO_LOW_ERROR) + ->assertRaised(); + } + + public function testTooHigh() + { + $constraint = new Week(max: '2016-W25'); + + $this->validator->validate('2016-W30', $constraint); + $this->buildViolation('The value should not be after week "{{ max }}".') + ->setInvalidValue('2016-W30') + ->setParameter('{{ max }}', '2016-W25') + ->setCode(Week::TOO_HIGH_ERROR) + ->assertRaised(); + } + + public function testWithNewLine() + { + $this->validator->validate("2015-W10\n", new Week()); + + $this->buildViolation('This value does not represent a valid week in the ISO 8601 format.') + ->setCode(Week::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider provideInvalidValues + */ + public function testInvalidValues(string $value) + { + $this->validator->validate($value, new Week()); + + $this->buildViolation('This value does not represent a valid week in the ISO 8601 format.') + ->setCode(Week::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + + /** + * @dataProvider provideInvalidTypes + */ + public function testNonStringValues(mixed $value) + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessageMatches('/Expected argument of type "string", ".*" given/'); + + $this->validator->validate($value, new Week()); + } + + public static function provideInvalidValues() + { + yield ['1970-01']; + yield ['1970-W00']; + yield ['1970-W54']; + yield ['1970-W100']; + yield ['1970-W01-01']; + yield ['-W01']; + yield ['24-W01']; + } + + public static function provideInvalidTypes() + { + yield [true]; + yield [false]; + yield [1]; + yield [1.1]; + yield [[]]; + yield [new \stdClass()]; + } +} From 8014091140950845c7a7d915dfb610a8ca8dbdab Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 13 Aug 2024 11:24:23 +0200 Subject: [PATCH 080/149] change default Week constraint messages --- Constraints/Week.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Constraints/Week.php b/Constraints/Week.php index 46b886975..182f8c055 100644 --- a/Constraints/Week.php +++ b/Constraints/Week.php @@ -38,9 +38,9 @@ public function __construct( public ?string $min = null, public ?string $max = null, public string $invalidFormatMessage = 'This value does not represent a valid week in the ISO 8601 format.', - public string $invalidWeekNumberMessage = 'The week "{{ value }}" is not a valid week.', - public string $tooLowMessage = 'The value should not be before week "{{ min }}".', - public string $tooHighMessage = 'The value should not be after week "{{ max }}".', + public string $invalidWeekNumberMessage = 'This value is not a valid week.', + public string $tooLowMessage = 'This value should not be before week "{{ min }}".', + public string $tooHighMessage = 'This value should not be after week "{{ max }}".', ?array $groups = null, mixed $payload = null, ) { @@ -55,8 +55,8 @@ public function __construct( } if (null !== $min && null !== $max) { - [$minYear, $minWeekNumber] = \explode('-W', $min, 2); - [$maxYear, $maxWeekNumber] = \explode('-W', $max, 2); + [$minYear, $minWeekNumber] = explode('-W', $min, 2); + [$maxYear, $maxWeekNumber] = explode('-W', $max, 2); if ($minYear > $maxYear || ($minYear === $maxYear && $minWeekNumber > $maxWeekNumber)) { throw new ConstraintDefinitionException(\sprintf('The "%s" constraint requires the min week to be less than or equal to the max week.', __CLASS__)); From 03939fb3c3b9a5e70e2c4a519e5e090f2b424c01 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 13 Aug 2024 12:44:29 +0200 Subject: [PATCH 081/149] [Validator] Fix tests --- Tests/Constraints/WeekValidatorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Constraints/WeekValidatorTest.php b/Tests/Constraints/WeekValidatorTest.php index 08bc3b29b..a4f12a8b4 100644 --- a/Tests/Constraints/WeekValidatorTest.php +++ b/Tests/Constraints/WeekValidatorTest.php @@ -33,7 +33,7 @@ public function testWeekIsValidWeekNumber(string|\Stringable $value, bool $expec $this->validator->validate($value, $constraint); if ($expectedViolation) { - $this->buildViolation('The week "{{ value }}" is not a valid week.') + $this->buildViolation('This value is not a valid week.') ->setCode(Week::INVALID_WEEK_NUMBER_ERROR) ->setParameter('{{ value }}', $value) ->assertRaised(); @@ -68,7 +68,7 @@ public function testTooLow() $constraint = new Week(min: '2015-W10'); $this->validator->validate('2015-W08', $constraint); - $this->buildViolation('The value should not be before week "{{ min }}".') + $this->buildViolation('This value should not be before week "{{ min }}".') ->setInvalidValue('2015-W08') ->setParameter('{{ min }}', '2015-W10') ->setCode(Week::TOO_LOW_ERROR) From dcbef130b268e70e29f41762b719ed63dd6011ec Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 13 Aug 2024 12:46:13 +0200 Subject: [PATCH 082/149] [Validator] Pass required `requireTld` option to `Url` in tests --- Tests/Constraints/UrlValidatorTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/Constraints/UrlValidatorTest.php b/Tests/Constraints/UrlValidatorTest.php index 7bc8f140f..5fdbb28b6 100644 --- a/Tests/Constraints/UrlValidatorTest.php +++ b/Tests/Constraints/UrlValidatorTest.php @@ -65,7 +65,7 @@ public function testValidUrls($url) */ public function testValidUrlsWithNewLine($url) { - $this->validator->validate($url."\n", new Url()); + $this->validator->validate($url."\n", new Url(requireTld: false)); $this->buildViolation('This value is not a valid URL.') ->setParameter('{{ value }}', '"'.$url."\n".'"') @@ -108,9 +108,7 @@ public function testValidRelativeUrl($url) */ public function testValidRelativeUrlWithNewLine(string $url) { - $constraint = new Url([ - 'relativeProtocol' => true, - ]); + $constraint = new Url(relativeProtocol: true, requireTld: false); $this->validator->validate($url."\n", $constraint); From a979a7e120354f580bf9f95989428ef5c3fc51cb Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 13 Aug 2024 13:57:32 +0200 Subject: [PATCH 083/149] fix test --- Tests/Constraints/WeekValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Constraints/WeekValidatorTest.php b/Tests/Constraints/WeekValidatorTest.php index a4f12a8b4..0a5f0936c 100644 --- a/Tests/Constraints/WeekValidatorTest.php +++ b/Tests/Constraints/WeekValidatorTest.php @@ -80,7 +80,7 @@ public function testTooHigh() $constraint = new Week(max: '2016-W25'); $this->validator->validate('2016-W30', $constraint); - $this->buildViolation('The value should not be after week "{{ max }}".') + $this->buildViolation('This value should not be after week "{{ max }}".') ->setInvalidValue('2016-W30') ->setParameter('{{ max }}', '2016-W25') ->setCode(Week::TOO_HIGH_ERROR) From 2cb05691c44e40415a8277337fbbef251d314a6c Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 21 Aug 2024 11:47:59 +0200 Subject: [PATCH 084/149] [Validator] Add more precise PHPDoc --- Constraints/CardScheme.php | 6 +++--- Constraints/Cascade.php | 4 ++-- Constraints/Choice.php | 4 ++-- Constraints/Count.php | 12 ++++++------ Constraints/CssColor.php | 6 +++--- Constraints/DateTime.php | 6 +++--- Constraints/Expression.php | 2 +- Constraints/ExpressionSyntax.php | 2 +- Constraints/File.php | 4 ++-- Constraints/Image.php | 22 +++++++++++----------- Constraints/Length.php | 16 ++++++++-------- Constraints/NotCompromisedPassword.php | 2 +- Constraints/Range.php | 16 ++++++++-------- Constraints/Week.php | 4 ++++ Constraints/WordCount.php | 4 ++++ 15 files changed, 59 insertions(+), 51 deletions(-) diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 965ec1c01..86085ee2e 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -47,9 +47,9 @@ class CardScheme extends Constraint public array|string|null $schemes = null; /** - * @param string|string[]|array|null $schemes Name(s) of the number scheme(s) used to validate the credit card number - * @param string[]|null $groups - * @param array $options + * @param non-empty-string|non-empty-string[]|array|null $schemes Name(s) of the number scheme(s) used to validate the credit card number + * @param string[]|null $groups + * @param array $options */ public function __construct(array|string|null $schemes, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) { diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index 9575f37d6..8879ca657 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -25,8 +25,8 @@ class Cascade extends Constraint public array $exclude = []; /** - * @param string[]|string|array|null $exclude Properties excluded from validation - * @param array|null $options + * @param non-empty-string[]|non-empty-string|array|null $exclude Properties excluded from validation + * @param array|null $options */ public function __construct(array|string|null $exclude = null, ?array $options = null) { diff --git a/Constraints/Choice.php b/Constraints/Choice.php index 743abaf97..18570c5c9 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -54,8 +54,8 @@ public function getDefaultOption(): ?string * @param callable|string|null $callback Callback method to use instead of the choice option to get the choices * @param bool|null $multiple Whether to expect the value to be an array of valid choices (defaults to false) * @param bool|null $strict This option defaults to true and should not be used - * @param int|null $min Minimum of valid choices if multiple values are expected - * @param int|null $max Maximum of valid choices if multiple values are expected + * @param int<0, max>|null $min Minimum of valid choices if multiple values are expected + * @param positive-int|null $max Maximum of valid choices if multiple values are expected * @param string[]|null $groups * @param bool|null $match Whether to validate the values are part of the choices or not (defaults to true) */ diff --git a/Constraints/Count.php b/Constraints/Count.php index a63d077d5..175e07ebb 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -43,12 +43,12 @@ class Count extends Constraint public ?int $divisibleBy = null; /** - * @param int|array|null $exactly The exact expected number of elements - * @param int|null $min Minimum expected number of elements - * @param int|null $max Maximum expected number of elements - * @param int|null $divisibleBy The number the collection count should be divisible by - * @param string[]|null $groups - * @param array $options + * @param int<0, max>|array|null $exactly The exact expected number of elements + * @param int<0, max>|null $min Minimum expected number of elements + * @param positive-int|null $max Maximum expected number of elements + * @param positive-int|null $divisibleBy The number the collection count should be divisible by + * @param string[]|null $groups + * @param array $options */ public function __construct( int|array|null $exactly = null, diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 538362a98..4f61df18f 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -62,9 +62,9 @@ class CssColor extends Constraint public array|string $formats; /** - * @param string[]|string|array $formats The types of CSS colors allowed ({@see https://symfony.com/doc/current/reference/constraints/CssColor.html#formats}) - * @param string[]|null $groups - * @param array|null $options + * @param non-empty-string[]|non-empty-string|array $formats The types of CSS colors allowed ({@see https://symfony.com/doc/current/reference/constraints/CssColor.html#formats}) + * @param string[]|null $groups + * @param array|null $options */ public function __construct(array|string $formats = [], ?string $message = null, ?array $groups = null, $payload = null, ?array $options = null) { diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index da2a2d6ba..5b3fd1b0b 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -37,9 +37,9 @@ class DateTime extends Constraint public string $message = 'This value is not a valid datetime.'; /** - * @param string|array|null $format The datetime format to match (defaults to 'Y-m-d H:i:s') - * @param string[]|null $groups - * @param array $options + * @param non-empty-string|array|null $format The datetime format to match (defaults to 'Y-m-d H:i:s') + * @param string[]|null $groups + * @param array $options */ public function __construct(string|array|null $format = null, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) { diff --git a/Constraints/Expression.php b/Constraints/Expression.php index 6f624807c..a9423a08b 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -43,7 +43,7 @@ class Expression extends Constraint * @param array|null $values The values of the custom variables used in the expression (defaults to an empty array) * @param string[]|null $groups * @param array $options - * @param bool|null $negate Whether to fail is the expression evaluates to true (defaults to false) + * @param bool|null $negate Whether to fail if the expression evaluates to true (defaults to false) */ public function __construct( string|ExpressionObject|array|null $expression, diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index a03053f02..8f4f59834 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -33,7 +33,7 @@ class ExpressionSyntax extends Constraint /** * @param array|null $options - * @param string|null $service The service used to validate the constraint instead of the default one + * @param non-empty-string|null $service The service used to validate the constraint instead of the default one * @param string[]|null $allowedVariables Restrict the available variables in the expression to these values (defaults to null that allows any variable) * @param string[]|null $groups */ diff --git a/Constraints/File.php b/Constraints/File.php index 089253bc9..8948b9ea6 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -73,10 +73,10 @@ class File extends Constraint /** * @param array|null $options - * @param int|string|null $maxSize The max size of the underlying file + * @param positive-int|string|null $maxSize The max size of the underlying file * @param bool|null $binaryFormat Pass true to use binary-prefixed units (KiB, MiB, etc.) or false to use SI-prefixed units (kB, MB) in displayed messages. Pass null to guess the format from the maxSize option. (defaults to null) * @param string[]|string|null $mimeTypes Acceptable media type(s). Prefer the extensions option that also enforce the file's extension consistency. - * @param int|null $filenameMaxLength Maximum length of the file name + * @param positive-int|null $filenameMaxLength Maximum length of the file name * @param string|null $disallowEmptyMessage Enable empty upload validation with this message in case of error * @param string|null $uploadIniSizeErrorMessage Message if the file size exceeds the max size configured in php.ini * @param string|null $uploadFormSizeErrorMessage Message if the file size exceeds the max size configured in the HTML input field diff --git a/Constraints/Image.php b/Constraints/Image.php index 158d4b2cb..0e86cc4bf 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -90,10 +90,10 @@ class Image extends File /** * @param array|null $options - * @param int|string|null $maxSize The max size of the underlying file + * @param positive-int|string|null $maxSize The max size of the underlying file * @param bool|null $binaryFormat Pass true to use binary-prefixed units (KiB, MiB, etc.) or false to use SI-prefixed units (kB, MB) in displayed messages. Pass null to guess the format from the maxSize option. (defaults to null) - * @param string[]|null $mimeTypes Acceptable media types - * @param int|null $filenameMaxLength Maximum length of the file name + * @param non-empty-string[]|null $mimeTypes Acceptable media types + * @param positive-int|null $filenameMaxLength Maximum length of the file name * @param string|null $disallowEmptyMessage Enable empty upload validation with this message in case of error * @param string|null $uploadIniSizeErrorMessage Message if the file size exceeds the max size configured in php.ini * @param string|null $uploadFormSizeErrorMessage Message if the file size exceeds the max size configured in the HTML input field @@ -102,14 +102,14 @@ class Image extends File * @param string|null $uploadCantWriteErrorMessage Message if the uploaded file can not be stored in the temporary directory * @param string|null $uploadErrorMessage Message if an unknown error occurred on upload * @param string[]|null $groups - * @param int|null $minWidth Minimum image width - * @param int|null $maxWidth Maximum image width - * @param int|null $maxHeight Maximum image height - * @param int|null $minHeight Minimum image weight - * @param int|float|null $maxRatio Maximum image ratio - * @param int|float|null $minRatio Minimum image ration - * @param int|float|null $minPixels Minimum amount of pixels - * @param int|float|null $maxPixels Maximum amount of pixels + * @param int<0, int>|null $minWidth Minimum image width + * @param positive-int|null $maxWidth Maximum image width + * @param positive-int|null $maxHeight Maximum image height + * @param int<0, int>|null $minHeight Minimum image weight + * @param positive-int|float|null $maxRatio Maximum image ratio + * @param int<0, max>|float|null $minRatio Minimum image ration + * @param int<0, max>|float|null $minPixels Minimum amount of pixels + * @param positive-int|float|null $maxPixels Maximum amount of pixels * @param bool|null $allowSquare Whether to allow a square image (defaults to true) * @param bool|null $allowLandscape Whether to allow a landscape image (defaults to true) * @param bool|null $allowPortrait Whether to allow a portrait image (defaults to true) diff --git a/Constraints/Length.php b/Constraints/Length.php index 233857760..d1bc7b9dc 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -58,14 +58,14 @@ class Length extends Constraint public string $countUnit = self::COUNT_CODEPOINTS; /** - * @param int|array|null $exactly The exact expected length - * @param int|null $min The minimum expected length - * @param int|null $max The maximum expected length - * @param string|null $charset The charset to be used when computing value's length (defaults to UTF-8) - * @param callable|null $normalizer A callable to normalize value before it is validated - * @param self::COUNT_*|null $countUnit The character count unit for the length check (defaults to {@see Length::COUNT_CODEPOINTS}) - * @param string[]|null $groups - * @param array $options + * @param positive-int|array|null $exactly The exact expected length + * @param int<0, max>|null $min The minimum expected length + * @param positive-int|null $max The maximum expected length + * @param string|null $charset The charset to be used when computing value's length (defaults to UTF-8) + * @param callable|null $normalizer A callable to normalize value before it is validated + * @param self::COUNT_*|null $countUnit The character count unit for the length check (defaults to {@see Length::COUNT_CODEPOINTS}) + * @param string[]|null $groups + * @param array $options */ public function __construct( int|array|null $exactly = null, diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index a122089af..d11df3ba6 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -33,7 +33,7 @@ class NotCompromisedPassword extends Constraint /** * @param array|null $options - * @param int|null $threshold The number of times the password should have been leaked to consider it is compromised (defaults to 1) + * @param positive-int|null $threshold The number of times the password should have been leaked to consider it is compromised (defaults to 1) * @param bool|null $skipOnError Whether to ignore HTTP errors while requesting the API and thus consider the password valid (defaults to false) * @param string[]|null $groups */ diff --git a/Constraints/Range.php b/Constraints/Range.php index ab274704c..cf26d3573 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -48,14 +48,14 @@ class Range extends Constraint public ?string $maxPropertyPath = null; /** - * @param array|null $options - * @param string|null $invalidMessage The message if min and max values are numeric but the given value is not - * @param string|null $invalidDateTimeMessage The message if min and max values are PHP datetimes but the given value is not - * @param int|float|string|null $min The minimum value, either numeric or a datetime string representation - * @param string|null $minPropertyPath Property path to the min value - * @param int|float|string|null $max The maximum value, either numeric or a datetime string representation - * @param string|null $maxPropertyPath Property path to the max value - * @param string[]|null $groups + * @param array|null $options + * @param string|null $invalidMessage The message if min and max values are numeric but the given value is not + * @param string|null $invalidDateTimeMessage The message if min and max values are PHP datetimes but the given value is not + * @param int|float|non-empty-string|null $min The minimum value, either numeric or a datetime string representation + * @param non-empty-string|null $minPropertyPath Property path to the min value + * @param int|float|non-empty-string|null $max The maximum value, either numeric or a datetime string representation + * @param non-empty-string|null $maxPropertyPath Property path to the max value + * @param string[]|null $groups */ public function __construct( ?array $options = null, diff --git a/Constraints/Week.php b/Constraints/Week.php index 182f8c055..f40f3462a 100644 --- a/Constraints/Week.php +++ b/Constraints/Week.php @@ -33,6 +33,10 @@ final class Week extends Constraint self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', ]; + /** + * @param non-empty-string|null $min + * @param non-empty-string|null $max + */ #[HasNamedArguments] public function __construct( public ?string $min = null, diff --git a/Constraints/WordCount.php b/Constraints/WordCount.php index 3c3c82f84..6b889aa4a 100644 --- a/Constraints/WordCount.php +++ b/Constraints/WordCount.php @@ -30,6 +30,10 @@ final class WordCount extends Constraint self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', ]; + /** + * @param int<0, max>|null $min + * @param positive-int|null $max + */ #[HasNamedArguments] public function __construct( public ?int $min = null, From ee2f6fdcb08f80f84c9b914a421d511d185768dd Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 27 Feb 2023 17:28:54 +0100 Subject: [PATCH 085/149] [Validator] Add `CompoundConstraintTestCase` to ease testing Compound Constraints --- CHANGELOG.md | 1 + Test/CompoundConstraintTestCase.php | 123 ++++++++++++++++++ Tests/Constraints/CompoundValidatorTest.php | 15 +-- Tests/Fixtures/DummyCompoundConstraint.php | 30 +++++ .../DummyCompoundConstraintWithGroups.php | 28 ++++ Tests/Test/CompoundConstraintTestCaseTest.php | 85 ++++++++++++ 6 files changed, 268 insertions(+), 14 deletions(-) create mode 100644 Test/CompoundConstraintTestCase.php create mode 100644 Tests/Fixtures/DummyCompoundConstraint.php create mode 100644 Tests/Fixtures/DummyCompoundConstraintWithGroups.php create mode 100644 Tests/Test/CompoundConstraintTestCaseTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d8ae5e6cc..b9af46e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * Add the `format` option to the `Ulid` constraint to allow accepting different ULID formats * Add the `WordCount` constraint * Add the `Week` constraint + * Add `CompoundConstraintTestCase` to ease testing Compound Constraints 7.1 --- diff --git a/Test/CompoundConstraintTestCase.php b/Test/CompoundConstraintTestCase.php new file mode 100644 index 000000000..7fb324595 --- /dev/null +++ b/Test/CompoundConstraintTestCase.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Test; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\CompoundValidator; +use Symfony\Component\Validator\ConstraintViolationListInterface; +use Symfony\Component\Validator\Context\ExecutionContext; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Validation; +use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * A test case to ease testing Compound Constraints. + * + * @author Alexandre Daubois + */ +abstract class CompoundConstraintTestCase extends TestCase +{ + protected ValidatorInterface $validator; + protected ?ConstraintViolationListInterface $violationList = null; + protected ExecutionContextInterface $context; + protected string $root; + + private mixed $validatedValue; + + protected function setUp(): void + { + $this->root = 'root'; + $this->validator = $this->createValidator(); + $this->context = $this->createContext($this->validator); + } + + protected function validateValue(mixed $value): void + { + $this->validator->inContext($this->context)->validate($this->validatedValue = $value, $this->createCompound()); + } + + protected function createValidator(): ValidatorInterface + { + return Validation::createValidator(); + } + + protected function createContext(?ValidatorInterface $validator = null): ExecutionContextInterface + { + $translator = $this->createMock(TranslatorInterface::class); + $translator->expects($this->any())->method('trans')->willReturnArgument(0); + + return new ExecutionContext($validator ?? $this->createValidator(), $this->root, $translator); + } + + public function assertViolationsRaisedByCompound(Constraint|array $constraints): void + { + if ($constraints instanceof Constraint) { + $constraints = [$constraints]; + } + + $validator = new CompoundValidator(); + $context = $this->createContext(); + $validator->initialize($context); + + $validator->validate($this->validatedValue, new class($constraints) extends Compound { + public function __construct(private array $testedConstraints) + { + parent::__construct(); + } + + protected function getConstraints(array $options): array + { + return $this->testedConstraints; + } + }); + + $expectedViolations = iterator_to_array($context->getViolations()); + + if (!$expectedViolations) { + throw new ExpectationFailedException(\sprintf('Expected at least one violation for constraint(s) "%s", got none raised.', implode(', ', array_map(fn ($constraint) => $constraint::class, $constraints)))); + } + + $failedToAssertViolations = []; + reset($expectedViolations); + foreach ($this->context->getViolations() as $violation) { + if ($violation != current($expectedViolations)) { + $failedToAssertViolations[] = $violation; + } + + next($expectedViolations); + } + + $this->assertEmpty( + $failedToAssertViolations, + \sprintf('Expected violation(s) for constraint(s) %s to be raised by compound.', + implode(', ', array_map(fn ($violation) => ($violation->getConstraint())::class, $failedToAssertViolations)) + ) + ); + } + + public function assertViolationsCount(int $count): void + { + $this->assertCount($count, $this->context->getViolations()); + } + + protected function assertNoViolation(): void + { + $violationsCount = \count($this->context->getViolations()); + $this->assertSame(0, $violationsCount, \sprintf('No violation expected. Got %d.', $violationsCount)); + } + + abstract protected function createCompound(): Compound; +} diff --git a/Tests/Constraints/CompoundValidatorTest.php b/Tests/Constraints/CompoundValidatorTest.php index 2f48657b2..9eb5c7add 100644 --- a/Tests/Constraints/CompoundValidatorTest.php +++ b/Tests/Constraints/CompoundValidatorTest.php @@ -11,11 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; -use Symfony\Component\Validator\Constraints\Compound; use Symfony\Component\Validator\Constraints\CompoundValidator; -use Symfony\Component\Validator\Constraints\Length; -use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint; class CompoundValidatorTest extends ConstraintValidatorTestCase { @@ -43,14 +41,3 @@ public function testValidateWithConstraints() $this->assertNoViolation(); } } - -class DummyCompoundConstraint extends Compound -{ - protected function getConstraints(array $options): array - { - return [ - new NotBlank(), - new Length(['max' => 3]), - ]; - } -} diff --git a/Tests/Fixtures/DummyCompoundConstraint.php b/Tests/Fixtures/DummyCompoundConstraint.php new file mode 100644 index 000000000..87253f25d --- /dev/null +++ b/Tests/Fixtures/DummyCompoundConstraint.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Regex; + +class DummyCompoundConstraint extends Compound +{ + protected function getConstraints(array $options): array + { + return [ + new NotBlank(), + new Length(['max' => 3]), + new Regex('/[a-z]+/'), + new Regex('/[0-9]+/'), + ]; + } +} diff --git a/Tests/Fixtures/DummyCompoundConstraintWithGroups.php b/Tests/Fixtures/DummyCompoundConstraintWithGroups.php new file mode 100644 index 000000000..8b2693ff6 --- /dev/null +++ b/Tests/Fixtures/DummyCompoundConstraintWithGroups.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Regex; + +class DummyCompoundConstraintWithGroups extends Compound +{ + protected function getConstraints(array $options): array + { + return [ + new NotBlank(groups: ['not_blank']), + new Length(['max' => 3], groups: ['max_length']), + ]; + } +} diff --git a/Tests/Test/CompoundConstraintTestCaseTest.php b/Tests/Test/CompoundConstraintTestCaseTest.php new file mode 100644 index 000000000..da62992be --- /dev/null +++ b/Tests/Test/CompoundConstraintTestCaseTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Test; + +use PHPUnit\Framework\ExpectationFailedException; +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Regex; +use Symfony\Component\Validator\Test\CompoundConstraintTestCase; +use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint; + +class CompoundConstraintTestCaseTest extends CompoundConstraintTestCase +{ + protected function createCompound(): Compound + { + return new DummyCompoundConstraint(); + } + + public function testAssertNoViolation() + { + $this->validateValue('ab1'); + + $this->assertNoViolation(); + $this->assertViolationsCount(0); + } + + public function testAssertIsRaisedByCompound() + { + $this->validateValue(''); + + $this->assertViolationsRaisedByCompound(new NotBlank()); + $this->assertViolationsCount(1); + } + + public function testMultipleAssertAreRaisedByCompound() + { + $this->validateValue('1245'); + + $this->assertViolationsRaisedByCompound([ + new Length(max: 3), + new Regex('/[a-z]+/'), + ]); + $this->assertViolationsCount(2); + } + + public function testNoAssertRaisedButExpected() + { + $this->validateValue('azert'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage("Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Length, Symfony\Component\Validator\Constraints\Regex to be raised by compound."); + $this->assertViolationsRaisedByCompound([ + new Length(max: 5), + new Regex('/^[A-Z]+$/'), + ]); + } + + public function testAssertRaisedByCompoundIsNotExactlyTheSame() + { + $this->validateValue('123'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Regex to be raised by compound.'); + $this->assertViolationsRaisedByCompound(new Regex('/^[a-z]+$/')); + } + + public function testAssertRaisedByCompoundButGotNone() + { + $this->validateValue('123'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Expected at least one violation for constraint(s) "Symfony\Component\Validator\Constraints\Length", got none raised.'); + $this->assertViolationsRaisedByCompound(new Length(max: 5)); + } +} From 1b5828d25e1b75badeae57449d430938283bfeab Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 22 Aug 2024 12:06:10 +0200 Subject: [PATCH 086/149] [Validator] Add $groups and $payload to Compound constructor --- Constraints/Compound.php | 4 ++-- Tests/Constraints/CompoundTest.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Constraints/Compound.php b/Constraints/Compound.php index 2862bca3f..ac2b5ac98 100644 --- a/Constraints/Compound.php +++ b/Constraints/Compound.php @@ -24,7 +24,7 @@ abstract class Compound extends Composite /** @var Constraint[] */ public array $constraints = []; - public function __construct(mixed $options = null) + public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null) { if (isset($options[$this->getCompositeOption()])) { throw new ConstraintDefinitionException(\sprintf('You can\'t redefine the "%s" option. Use the "%s::getConstraints()" method instead.', $this->getCompositeOption(), __CLASS__)); @@ -32,7 +32,7 @@ public function __construct(mixed $options = null) $this->constraints = $this->getConstraints($this->normalizeOptions($options)); - parent::__construct($options); + parent::__construct($options, $groups, $payload); } final protected function getCompositeOption(): string diff --git a/Tests/Constraints/CompoundTest.php b/Tests/Constraints/CompoundTest.php index d5cf9e9ce..26889a0cc 100644 --- a/Tests/Constraints/CompoundTest.php +++ b/Tests/Constraints/CompoundTest.php @@ -26,6 +26,24 @@ public function testItCannotRedefineConstraintsOption() new EmptyCompound(['constraints' => [new NotBlank()]]); } + public function testGroupsAndPayload() + { + $payload = new \stdClass(); + $compound = new EmptyCompound(groups: ['my-group', 'my-other-group'], payload: $payload); + + $this->assertSame(['my-group', 'my-other-group'], $compound->groups); + $this->assertSame($payload, $compound->payload); + } + + public function testGroupsAndPayloadInOptionsArray() + { + $payload = new \stdClass(); + $compound = new EmptyCompound(['groups' => ['my-group', 'my-other-group'], 'payload' => $payload]); + + $this->assertSame(['my-group', 'my-other-group'], $compound->groups); + $this->assertSame($payload, $compound->payload); + } + public function testCanDependOnNormalizedOptions() { $constraint = new ForwardingOptionCompound($min = 3); From ff25649fc38722a1b761487d007bcad990dbeff2 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 22 Aug 2024 14:21:30 +0200 Subject: [PATCH 087/149] [Validator] Add `@template` on CompoundConstraintTestCase` --- Test/CompoundConstraintTestCase.php | 5 +++++ Tests/Test/CompoundConstraintTestCaseTest.php | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Test/CompoundConstraintTestCase.php b/Test/CompoundConstraintTestCase.php index 7fb324595..db7021729 100644 --- a/Test/CompoundConstraintTestCase.php +++ b/Test/CompoundConstraintTestCase.php @@ -27,6 +27,8 @@ * A test case to ease testing Compound Constraints. * * @author Alexandre Daubois + * + * @template T of Compound */ abstract class CompoundConstraintTestCase extends TestCase { @@ -119,5 +121,8 @@ protected function assertNoViolation(): void $this->assertSame(0, $violationsCount, \sprintf('No violation expected. Got %d.', $violationsCount)); } + /** + * @return T + */ abstract protected function createCompound(): Compound; } diff --git a/Tests/Test/CompoundConstraintTestCaseTest.php b/Tests/Test/CompoundConstraintTestCaseTest.php index da62992be..c59f3bf57 100644 --- a/Tests/Test/CompoundConstraintTestCaseTest.php +++ b/Tests/Test/CompoundConstraintTestCaseTest.php @@ -19,6 +19,9 @@ use Symfony\Component\Validator\Test\CompoundConstraintTestCase; use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint; +/** + * @extends CompoundConstraintTestCase + */ class CompoundConstraintTestCaseTest extends CompoundConstraintTestCase { protected function createCompound(): Compound From 47c1dc87ea68765e774bc6ffba04917c132a1c0b Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 31 Aug 2024 00:31:12 +0200 Subject: [PATCH 088/149] CS: re-apply `trailing_comma_in_multiline` --- Tests/Constraints/AtLeastOneOfValidatorTest.php | 2 +- Tests/Fixtures/NestedAttribute/Entity.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Constraints/AtLeastOneOfValidatorTest.php b/Tests/Constraints/AtLeastOneOfValidatorTest.php index cc0bed103..6a5bce5ba 100644 --- a/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -314,7 +314,7 @@ public function testValidateNestedAtLeaseOneOfConstraints() new Collection([ 'bar' => new AtLeastOneOf([ new Type('int'), - new Choice(['test1', 'test2']) + new Choice(['test1', 'test2']), ]), ]), new Collection([ diff --git a/Tests/Fixtures/NestedAttribute/Entity.php b/Tests/Fixtures/NestedAttribute/Entity.php index 2384e8734..3558d5624 100644 --- a/Tests/Fixtures/NestedAttribute/Entity.php +++ b/Tests/Fixtures/NestedAttribute/Entity.php @@ -22,7 +22,7 @@ Assert\GroupSequence(['Foo', 'Entity']), Assert\Callback([CallbackClass::class, 'callback']), Assert\Sequentially([ - new Assert\Expression('this.getFirstName() != null') + new Assert\Expression('this.getFirstName() != null'), ]) ] class Entity extends EntityParent implements EntityInterfaceB From eebc1fab7e3b6b4b96076ee850374a8ef966f62f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 23 Sep 2024 13:26:48 +0200 Subject: [PATCH 089/149] [Serializer] Fix expectation --- Tests/Mapping/Loader/PropertyInfoLoaderTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php index fde1d7d57..6c49f0d25 100644 --- a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php +++ b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php @@ -126,6 +126,7 @@ public function getTypes(string $class, string $property, array $context = []): true, true, false, + true, true ) ; From a6194127067798baeed011c6b827ce2fb80729f0 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 23 Sep 2024 11:36:51 +0200 Subject: [PATCH 090/149] do not skip tests from data providers --- .../AbstractComparisonValidatorTestCase.php | 165 ------------------ ...pareWithNullValueAtPropertyAtTestTrait.php | 42 +++++ .../Constraints/DivisibleByValidatorTest.php | 9 +- Tests/Constraints/EqualToValidatorTest.php | 42 ++++- .../GreaterThanOrEqualValidatorTest.php | 11 +- ...idatorWithPositiveOrZeroConstraintTest.php | 45 ++--- .../Constraints/GreaterThanValidatorTest.php | 11 +- ...hanValidatorWithPositiveConstraintTest.php | 44 ++--- .../Constraints/IdenticalToValidatorTest.php | 42 ++++- .../InvalidComparisonToValueTestTrait.php | 77 ++++++++ .../LessThanOrEqualValidatorTest.php | 11 +- ...idatorWithNegativeOrZeroConstraintTest.php | 47 ++--- Tests/Constraints/LessThanValidatorTest.php | 11 +- ...hanValidatorWithNegativeConstraintTest.php | 46 ++--- Tests/Constraints/NotEqualToValidatorTest.php | 11 +- .../NotIdenticalToValidatorTest.php | 11 +- .../ThrowsOnInvalidStringDatesTestTrait.php | 41 +++++ .../ValidComparisonToValueTrait.php | 41 +++++ 18 files changed, 363 insertions(+), 344 deletions(-) create mode 100644 Tests/Constraints/CompareWithNullValueAtPropertyAtTestTrait.php create mode 100644 Tests/Constraints/InvalidComparisonToValueTestTrait.php create mode 100644 Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php create mode 100644 Tests/Constraints/ValidComparisonToValueTrait.php diff --git a/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/Tests/Constraints/AbstractComparisonValidatorTestCase.php index 39e97f949..25fed976c 100644 --- a/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -11,12 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; -use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\AbstractComparison; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; -use Symfony\Component\Validator\Tests\Constraints\Fixtures\TypedDummy; class ComparisonTest_Class { @@ -98,32 +95,6 @@ public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() ]); } - /** - * @dataProvider provideAllValidComparisons - */ - public function testValidComparisonToValue($dirtyValue, $comparisonValue) - { - $constraint = $this->createConstraint(['value' => $comparisonValue]); - - $this->validator->validate($dirtyValue, $constraint); - - $this->assertNoViolation(); - } - - public static function provideAllValidComparisons(): array - { - // The provider runs before setUp(), so we need to manually fix - // the default timezone - $timezone = date_default_timezone_get(); - date_default_timezone_set('UTC'); - - $comparisons = self::addPhp5Dot5Comparisons(static::provideValidComparisons()); - - date_default_timezone_set($timezone); - - return $comparisons; - } - /** * @dataProvider provideValidComparisonsToPropertyPath */ @@ -169,144 +140,8 @@ abstract public static function provideValidComparisons(): array; abstract public static function provideValidComparisonsToPropertyPath(): array; - /** - * @dataProvider provideAllInvalidComparisons - */ - public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType) - { - // Conversion of dates to string differs between ICU versions - // Make sure we have the correct version loaded - if ($dirtyValue instanceof \DateTimeInterface) { - IntlTestHelper::requireIntl($this, '57.1'); - } - - $constraint = $this->createConstraint(['value' => $comparedValue]); - $constraint->message = 'Constraint Message'; - - $this->validator->validate($dirtyValue, $constraint); - - $this->buildViolation('Constraint Message') - ->setParameter('{{ value }}', $dirtyValueAsString) - ->setParameter('{{ compared_value }}', $comparedValueString) - ->setParameter('{{ compared_value_type }}', $comparedValueType) - ->setCode($this->getErrorCode()) - ->assertRaised(); - } - - public function testInvalidComparisonToPropertyPathAddsPathAsParameter() - { - [$dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType] = current($this->provideAllInvalidComparisons()); - - $constraint = $this->createConstraint(['propertyPath' => 'value']); - $constraint->message = 'Constraint Message'; - - $object = new ComparisonTest_Class($comparedValue); - - $this->setObject($object); - - $this->validator->validate($dirtyValue, $constraint); - - $this->buildViolation('Constraint Message') - ->setParameter('{{ value }}', $dirtyValueAsString) - ->setParameter('{{ compared_value }}', $comparedValueString) - ->setParameter('{{ compared_value_path }}', 'value') - ->setParameter('{{ compared_value_type }}', $comparedValueType) - ->setCode($this->getErrorCode()) - ->assertRaised(); - } - - /** - * @dataProvider throwsOnInvalidStringDatesProvider - */ - public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value) - { - $this->expectException(ConstraintDefinitionException::class); - $this->expectExceptionMessage($expectedMessage); - - $this->validator->validate($value, $constraint); - } - - public static function throwsOnInvalidStringDatesProvider(): array - { - $constraint = static::createConstraint([ - 'value' => 'foo', - ]); - - return [ - [$constraint, \sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraint::class), new \DateTimeImmutable()], - [$constraint, \sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraint::class), new \DateTime()], - ]; - } - - /** - * @dataProvider provideComparisonsToNullValueAtPropertyPath - */ - public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsString, $isValid) - { - $constraint = $this->createConstraint(['propertyPath' => 'value']); - $constraint->message = 'Constraint Message'; - - $object = new ComparisonTest_Class(null); - $this->setObject($object); - - $this->validator->validate($dirtyValue, $constraint); - - if ($isValid) { - $this->assertNoViolation(); - } else { - $this->buildViolation('Constraint Message') - ->setParameter('{{ value }}', $dirtyValueAsString) - ->setParameter('{{ compared_value }}', 'null') - ->setParameter('{{ compared_value_type }}', 'null') - ->setParameter('{{ compared_value_path }}', 'value') - ->setCode($this->getErrorCode()) - ->assertRaised(); - } - } - - /** - * @dataProvider provideComparisonsToNullValueAtPropertyPath - */ - public function testCompareWithUninitializedPropertyAtPropertyPath($dirtyValue, $dirtyValueAsString, $isValid) - { - $this->setObject(new TypedDummy()); - - $this->validator->validate($dirtyValue, $this->createConstraint([ - 'message' => 'Constraint Message', - 'propertyPath' => 'value', - ])); - - if ($isValid) { - $this->assertNoViolation(); - } else { - $this->buildViolation('Constraint Message') - ->setParameter('{{ value }}', $dirtyValueAsString) - ->setParameter('{{ compared_value }}', 'null') - ->setParameter('{{ compared_value_type }}', 'null') - ->setParameter('{{ compared_value_path }}', 'value') - ->setCode($this->getErrorCode()) - ->assertRaised(); - } - } - - public static function provideAllInvalidComparisons(): array - { - // The provider runs before setUp(), so we need to manually fix - // the default timezone - $timezone = date_default_timezone_get(); - date_default_timezone_set('UTC'); - - $comparisons = self::addPhp5Dot5Comparisons(static::provideInvalidComparisons()); - - date_default_timezone_set($timezone); - - return $comparisons; - } - abstract public static function provideInvalidComparisons(): array; - abstract public static function provideComparisonsToNullValueAtPropertyPath(); - abstract protected static function createConstraint(?array $options = null): Constraint; protected function getErrorCode(): ?string diff --git a/Tests/Constraints/CompareWithNullValueAtPropertyAtTestTrait.php b/Tests/Constraints/CompareWithNullValueAtPropertyAtTestTrait.php new file mode 100644 index 000000000..d99090295 --- /dev/null +++ b/Tests/Constraints/CompareWithNullValueAtPropertyAtTestTrait.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Tests\Constraints\Fixtures\TypedDummy; + +trait CompareWithNullValueAtPropertyAtTestTrait +{ + public function testCompareWithNullValueAtPropertyAt() + { + $constraint = $this->createConstraint(['propertyPath' => 'value']); + $constraint->message = 'Constraint Message'; + + $object = new ComparisonTest_Class(null); + $this->setObject($object); + + $this->validator->validate(5, $constraint); + + $this->assertNoViolation(); + } + + public function testCompareWithUninitializedPropertyAtPropertyPath() + { + $this->setObject(new TypedDummy()); + + $this->validator->validate(5, $this->createConstraint([ + 'message' => 'Constraint Message', + 'propertyPath' => 'value', + ])); + + $this->assertNoViolation(); + } +} diff --git a/Tests/Constraints/DivisibleByValidatorTest.php b/Tests/Constraints/DivisibleByValidatorTest.php index 37ae2087e..22dc683fb 100644 --- a/Tests/Constraints/DivisibleByValidatorTest.php +++ b/Tests/Constraints/DivisibleByValidatorTest.php @@ -21,6 +21,10 @@ */ class DivisibleByValidatorTest extends AbstractComparisonValidatorTestCase { + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; + protected function createValidator(): DivisibleByValidator { return new DivisibleByValidator(); @@ -102,9 +106,4 @@ public static function throwsOnNonNumericValuesProvider() [\ArrayIterator::class, new \ArrayIterator(), 12], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath() - { - self::markTestSkipped('DivisibleByValidator rejects null values.'); - } } diff --git a/Tests/Constraints/EqualToValidatorTest.php b/Tests/Constraints/EqualToValidatorTest.php index 0a57fa8d3..b1af4ed18 100644 --- a/Tests/Constraints/EqualToValidatorTest.php +++ b/Tests/Constraints/EqualToValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\EqualTo; use Symfony\Component\Validator\Constraints\EqualToValidator; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\TypedDummy; use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** @@ -22,6 +23,9 @@ class EqualToValidatorTest extends AbstractComparisonValidatorTestCase { use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): EqualToValidator { @@ -71,10 +75,40 @@ public static function provideInvalidComparisons(): array ]; } - public static function provideComparisonsToNullValueAtPropertyPath(): array + public function testCompareWithNullValueAtPropertyAt() { - return [ - [5, '5', false], - ]; + $constraint = $this->createConstraint(['propertyPath' => 'value']); + $constraint->message = 'Constraint Message'; + + $object = new ComparisonTest_Class(null); + $this->setObject($object); + + $this->validator->validate(5, $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', '5') + ->setParameter('{{ compared_value }}', 'null') + ->setParameter('{{ compared_value_type }}', 'null') + ->setParameter('{{ compared_value_path }}', 'value') + ->setCode($this->getErrorCode()) + ->assertRaised(); + } + + public function testCompareWithUninitializedPropertyAtPropertyPath() + { + $this->setObject(new TypedDummy()); + + $this->validator->validate(5, $this->createConstraint([ + 'message' => 'Constraint Message', + 'propertyPath' => 'value', + ])); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', '5') + ->setParameter('{{ compared_value }}', 'null') + ->setParameter('{{ compared_value_type }}', 'null') + ->setParameter('{{ compared_value_path }}', 'value') + ->setCode($this->getErrorCode()) + ->assertRaised(); } } diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorTest.php index aeaf46f56..1bd4b6538 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorTest.php @@ -21,7 +21,11 @@ */ class GreaterThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase { + use CompareWithNullValueAtPropertyAtTestTrait; use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): GreaterThanOrEqualValidator { @@ -73,11 +77,4 @@ public static function provideInvalidComparisons(): array ['b', '"b"', 'c', '"c"', 'string'], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - return [ - [5, '5', true], - ]; - } } diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php index 39759c09e..b05ba53a5 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php @@ -12,15 +12,20 @@ namespace Symfony\Component\Validator\Tests\Constraints; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\AbstractComparison; +use Symfony\Component\Validator\Constraints\GreaterThanOrEqualValidator; use Symfony\Component\Validator\Constraints\PositiveOrZero; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @author Jan Schädlich */ -class GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest extends GreaterThanOrEqualValidatorTest +class GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest extends AbstractComparisonValidatorTestCase { + protected function createValidator(): GreaterThanOrEqualValidator + { + return new GreaterThanOrEqualValidator(); + } + protected static function createConstraint(?array $options = null): Constraint { return new PositiveOrZero($options); @@ -39,6 +44,14 @@ public static function provideValidComparisons(): array ]; } + public static function provideValidComparisonsToPropertyPath(): array + { + return [ + [5], + [6], + ]; + } + public static function provideInvalidComparisons(): array { return [ @@ -82,11 +95,6 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); } - public static function provideAllValidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the PositiveOrZero constraint'); - } - /** * @dataProvider provideValidComparisonsToPropertyPath */ @@ -100,31 +108,8 @@ public function testNoViolationOnNullObjectWithPropertyPath() $this->markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); } - /** - * @dataProvider throwsOnInvalidStringDatesProvider - */ - public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value) - { - $this->markTestSkipped('The compared value cannot be an invalid string date because it is hardcoded to 0.'); - } - public function testInvalidComparisonToPropertyPathAddsPathAsParameter() { $this->markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); } - - public static function throwsOnInvalidStringDatesProvider(): array - { - self::markTestSkipped('The "value" option cannot be used in the PositiveOrZero constraint'); - } - - public static function provideAllInvalidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the Negative constraint'); - } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - self::markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); - } } diff --git a/Tests/Constraints/GreaterThanValidatorTest.php b/Tests/Constraints/GreaterThanValidatorTest.php index 709e01c26..4cc64396d 100644 --- a/Tests/Constraints/GreaterThanValidatorTest.php +++ b/Tests/Constraints/GreaterThanValidatorTest.php @@ -21,7 +21,11 @@ */ class GreaterThanValidatorTest extends AbstractComparisonValidatorTestCase { + use CompareWithNullValueAtPropertyAtTestTrait; use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): GreaterThanValidator { @@ -75,11 +79,4 @@ public static function provideInvalidComparisons(): array ['22', '"22"', '22', '"22"', 'string'], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - return [ - [5, '5', true], - ]; - } } diff --git a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php index f14c8fce7..0aa8aee93 100644 --- a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php +++ b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php @@ -12,15 +12,20 @@ namespace Symfony\Component\Validator\Tests\Constraints; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\AbstractComparison; +use Symfony\Component\Validator\Constraints\GreaterThanValidator; use Symfony\Component\Validator\Constraints\Positive; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @author Jan Schädlich */ -class GreaterThanValidatorWithPositiveConstraintTest extends GreaterThanValidatorTest +class GreaterThanValidatorWithPositiveConstraintTest extends AbstractComparisonValidatorTestCase { + protected function createValidator(): GreaterThanValidator + { + return new GreaterThanValidator(); + } + protected static function createConstraint(?array $options = null): Constraint { return new Positive($options); @@ -36,6 +41,13 @@ public static function provideValidComparisons(): array ]; } + public static function provideValidComparisonsToPropertyPath(): array + { + return [ + [6], + ]; + } + public static function provideInvalidComparisons(): array { return [ @@ -85,11 +97,6 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } - public static function provideAllValidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the Positive constraint'); - } - /** * @dataProvider provideValidComparisonsToPropertyPath */ @@ -98,31 +105,8 @@ public function testValidComparisonToPropertyPath($comparedValue) $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } - /** - * @dataProvider throwsOnInvalidStringDatesProvider - */ - public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value) - { - $this->markTestSkipped('The compared value cannot be an invalid string date because it is hardcoded to 0.'); - } - public function testInvalidComparisonToPropertyPathAddsPathAsParameter() { $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } - - public static function throwsOnInvalidStringDatesProvider(): array - { - self::markTestSkipped('The "value" option cannot be used in the Positive constraint'); - } - - public static function provideAllInvalidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the Positive constraint'); - } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - self::markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); - } } diff --git a/Tests/Constraints/IdenticalToValidatorTest.php b/Tests/Constraints/IdenticalToValidatorTest.php index 2855e83f0..66390a6de 100644 --- a/Tests/Constraints/IdenticalToValidatorTest.php +++ b/Tests/Constraints/IdenticalToValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\IdenticalTo; use Symfony\Component\Validator\Constraints\IdenticalToValidator; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\TypedDummy; use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** @@ -22,6 +23,9 @@ class IdenticalToValidatorTest extends AbstractComparisonValidatorTestCase { use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): IdenticalToValidator { @@ -90,10 +94,40 @@ public static function provideInvalidComparisons(): array ]; } - public static function provideComparisonsToNullValueAtPropertyPath(): array + public function testCompareWithNullValueAtPropertyAt() { - return [ - [5, '5', false], - ]; + $constraint = $this->createConstraint(['propertyPath' => 'value']); + $constraint->message = 'Constraint Message'; + + $object = new ComparisonTest_Class(null); + $this->setObject($object); + + $this->validator->validate(5, $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', '5') + ->setParameter('{{ compared_value }}', 'null') + ->setParameter('{{ compared_value_type }}', 'null') + ->setParameter('{{ compared_value_path }}', 'value') + ->setCode($this->getErrorCode()) + ->assertRaised(); + } + + public function testCompareWithUninitializedPropertyAtPropertyPath() + { + $this->setObject(new TypedDummy()); + + $this->validator->validate(5, $this->createConstraint([ + 'message' => 'Constraint Message', + 'propertyPath' => 'value', + ])); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', '5') + ->setParameter('{{ compared_value }}', 'null') + ->setParameter('{{ compared_value_type }}', 'null') + ->setParameter('{{ compared_value_path }}', 'value') + ->setCode($this->getErrorCode()) + ->assertRaised(); } } diff --git a/Tests/Constraints/InvalidComparisonToValueTestTrait.php b/Tests/Constraints/InvalidComparisonToValueTestTrait.php new file mode 100644 index 000000000..5b261d504 --- /dev/null +++ b/Tests/Constraints/InvalidComparisonToValueTestTrait.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +trait InvalidComparisonToValueTestTrait +{ + /** + * @dataProvider provideAllInvalidComparisons + */ + public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType) + { + // Conversion of dates to string differs between ICU versions + // Make sure we have the correct version loaded + if ($dirtyValue instanceof \DateTimeInterface) { + IntlTestHelper::requireIntl($this, '57.1'); + } + + $constraint = $this->createConstraint(['value' => $comparedValue]); + $constraint->message = 'Constraint Message'; + + $this->validator->validate($dirtyValue, $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', $dirtyValueAsString) + ->setParameter('{{ compared_value }}', $comparedValueString) + ->setParameter('{{ compared_value_type }}', $comparedValueType) + ->setCode($this->getErrorCode()) + ->assertRaised(); + } + + public function testInvalidComparisonToPropertyPathAddsPathAsParameter() + { + [$dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType] = current($this->provideAllInvalidComparisons()); + + $constraint = $this->createConstraint(['propertyPath' => 'value']); + $constraint->message = 'Constraint Message'; + + $object = new ComparisonTest_Class($comparedValue); + + $this->setObject($object); + + $this->validator->validate($dirtyValue, $constraint); + + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', $dirtyValueAsString) + ->setParameter('{{ compared_value }}', $comparedValueString) + ->setParameter('{{ compared_value_path }}', 'value') + ->setParameter('{{ compared_value_type }}', $comparedValueType) + ->setCode($this->getErrorCode()) + ->assertRaised(); + } + + public static function provideAllInvalidComparisons(): array + { + // The provider runs before setUp(), so we need to manually fix + // the default timezone + $timezone = date_default_timezone_get(); + date_default_timezone_set('UTC'); + + $comparisons = self::addPhp5Dot5Comparisons(static::provideInvalidComparisons()); + + date_default_timezone_set($timezone); + + return $comparisons; + } +} diff --git a/Tests/Constraints/LessThanOrEqualValidatorTest.php b/Tests/Constraints/LessThanOrEqualValidatorTest.php index ba81bbd55..4c0aa5349 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorTest.php @@ -21,7 +21,11 @@ */ class LessThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase { + use CompareWithNullValueAtPropertyAtTestTrait; use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): LessThanOrEqualValidator { @@ -76,11 +80,4 @@ public static function provideInvalidComparisons(): array ['c', '"c"', 'b', '"b"', 'string'], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - return [ - [5, '5', true], - ]; - } } diff --git a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php index 2f6d5d624..43bca5121 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php @@ -12,14 +12,20 @@ namespace Symfony\Component\Validator\Tests\Constraints; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\LessThanOrEqualValidator; use Symfony\Component\Validator\Constraints\NegativeOrZero; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @author Jan Schädlich */ -class LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest extends LessThanOrEqualValidatorTest +class LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest extends AbstractComparisonValidatorTestCase { + protected function createValidator(): LessThanOrEqualValidator + { + return new LessThanOrEqualValidator(); + } + protected static function createConstraint(?array $options = null): Constraint { return new NegativeOrZero($options); @@ -36,6 +42,14 @@ public static function provideValidComparisons(): array ]; } + public static function provideValidComparisonsToPropertyPath(): array + { + return [ + [4], + [5], + ]; + } + public static function provideInvalidComparisons(): array { return [ @@ -84,11 +98,6 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); } - public static function provideAllValidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the NegativeOrZero constraint'); - } - /** * @dataProvider provideValidComparisonsToPropertyPath */ @@ -101,30 +110,4 @@ public function testInvalidComparisonToPropertyPathAddsPathAsParameter() { $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); } - - /** - * @dataProvider provideComparisonsToNullValueAtPropertyPath - */ - public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsString, $isValid) - { - $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); - } - - /** - * @dataProvider provideComparisonsToNullValueAtPropertyPath - */ - public function testCompareWithUninitializedPropertyAtPropertyPath($dirtyValue, $dirtyValueAsString, $isValid) - { - $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); - } - - public static function throwsOnInvalidStringDatesProvider(): array - { - self::markTestSkipped('The "value" option cannot be used in the NegativeOrZero constraint'); - } - - public static function provideAllInvalidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the NegativeOrZero constraint'); - } } diff --git a/Tests/Constraints/LessThanValidatorTest.php b/Tests/Constraints/LessThanValidatorTest.php index 0c4afb7b5..c6918942d 100644 --- a/Tests/Constraints/LessThanValidatorTest.php +++ b/Tests/Constraints/LessThanValidatorTest.php @@ -21,7 +21,11 @@ */ class LessThanValidatorTest extends AbstractComparisonValidatorTestCase { + use CompareWithNullValueAtPropertyAtTestTrait; use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): LessThanValidator { @@ -74,11 +78,4 @@ public static function provideInvalidComparisons(): array ['333', '"333"', '22', '"22"', 'string'], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - return [ - [5, '5', true], - ]; - } } diff --git a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php index c52d8bc2d..fa820c19b 100644 --- a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php +++ b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php @@ -12,14 +12,20 @@ namespace Symfony\Component\Validator\Tests\Constraints; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\LessThanValidator; use Symfony\Component\Validator\Constraints\Negative; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** * @author Jan Schädlich */ -class LessThanValidatorWithNegativeConstraintTest extends LessThanValidatorTest +class LessThanValidatorWithNegativeConstraintTest extends AbstractComparisonValidatorTestCase { + protected function createValidator(): LessThanValidator + { + return new LessThanValidator(); + } + protected static function createConstraint(?array $options = null): Constraint { return new Negative($options); @@ -35,6 +41,13 @@ public static function provideValidComparisons(): array ]; } + public static function provideValidComparisonsToPropertyPath(): array + { + return [ + [4], + ]; + } + public static function provideInvalidComparisons(): array { return [ @@ -84,11 +97,6 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); } - public static function provideAllValidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the Negative constraint'); - } - /** * @dataProvider provideValidComparisonsToPropertyPath */ @@ -97,34 +105,8 @@ public function testValidComparisonToPropertyPath($comparedValue) $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); } - public static function throwsOnInvalidStringDatesProvider(): array - { - self::markTestSkipped('The "value" option cannot be used in the Negative constraint'); - } - - /** - * @dataProvider provideComparisonsToNullValueAtPropertyPath - */ - public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsString, $isValid) - { - $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); - } - - /** - * @dataProvider provideComparisonsToNullValueAtPropertyPath - */ - public function testCompareWithUninitializedPropertyAtPropertyPath($dirtyValue, $dirtyValueAsString, $isValid) - { - $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); - } - public function testInvalidComparisonToPropertyPathAddsPathAsParameter() { $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); } - - public static function provideAllInvalidComparisons(): array - { - self::markTestSkipped('The "value" option cannot be used in the Negative constraint'); - } } diff --git a/Tests/Constraints/NotEqualToValidatorTest.php b/Tests/Constraints/NotEqualToValidatorTest.php index fc5275e3c..52e25a8db 100644 --- a/Tests/Constraints/NotEqualToValidatorTest.php +++ b/Tests/Constraints/NotEqualToValidatorTest.php @@ -21,7 +21,11 @@ */ class NotEqualToValidatorTest extends AbstractComparisonValidatorTestCase { + use CompareWithNullValueAtPropertyAtTestTrait; use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): NotEqualToValidator { @@ -70,11 +74,4 @@ public static function provideInvalidComparisons(): array [new ComparisonTest_Class(5), '5', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - return [ - [5, '5', true], - ]; - } } diff --git a/Tests/Constraints/NotIdenticalToValidatorTest.php b/Tests/Constraints/NotIdenticalToValidatorTest.php index 0376403e2..825a5ff9f 100644 --- a/Tests/Constraints/NotIdenticalToValidatorTest.php +++ b/Tests/Constraints/NotIdenticalToValidatorTest.php @@ -21,7 +21,11 @@ */ class NotIdenticalToValidatorTest extends AbstractComparisonValidatorTestCase { + use CompareWithNullValueAtPropertyAtTestTrait; use IcuCompatibilityTrait; + use InvalidComparisonToValueTestTrait; + use ThrowsOnInvalidStringDatesTestTrait; + use ValidComparisonToValueTrait; protected function createValidator(): NotIdenticalToValidator { @@ -87,11 +91,4 @@ public static function provideInvalidComparisons(): array [$object, '2', $object, '2', __NAMESPACE__.'\ComparisonTest_Class'], ]; } - - public static function provideComparisonsToNullValueAtPropertyPath(): array - { - return [ - [5, '5', true], - ]; - } } diff --git a/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php b/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php new file mode 100644 index 000000000..ac40492fd --- /dev/null +++ b/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\AbstractComparison; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; + +trait ThrowsOnInvalidStringDatesTestTrait +{ + /** + * @dataProvider throwsOnInvalidStringDatesProvider + */ + public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value) + { + $this->expectException(ConstraintDefinitionException::class); + $this->expectExceptionMessage($expectedMessage); + + $this->validator->validate($value, $constraint); + } + + public static function throwsOnInvalidStringDatesProvider(): array + { + $constraint = static::createConstraint([ + 'value' => 'foo', + ]); + + return [ + [$constraint, \sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraint::class), new \DateTimeImmutable()], + [$constraint, \sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraint::class), new \DateTime()], + ]; + } +} diff --git a/Tests/Constraints/ValidComparisonToValueTrait.php b/Tests/Constraints/ValidComparisonToValueTrait.php new file mode 100644 index 000000000..c4c70d8ce --- /dev/null +++ b/Tests/Constraints/ValidComparisonToValueTrait.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +trait ValidComparisonToValueTrait +{ + /** + * @dataProvider provideAllValidComparisons + */ + public function testValidComparisonToValue($dirtyValue, $comparisonValue) + { + $constraint = $this->createConstraint(['value' => $comparisonValue]); + + $this->validator->validate($dirtyValue, $constraint); + + $this->assertNoViolation(); + } + + public static function provideAllValidComparisons(): array + { + // The provider runs before setUp(), so we need to manually fix + // the default timezone + $timezone = date_default_timezone_get(); + date_default_timezone_set('UTC'); + + $comparisons = self::addPhp5Dot5Comparisons(static::provideValidComparisons()); + + date_default_timezone_set($timezone); + + return $comparisons; + } +} From 22be8e71f3f67fb40703f660d9b5da78e52b6565 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 26 Sep 2024 10:09:09 +0200 Subject: [PATCH 091/149] Remove unused imports --- Tests/Constraints/AtLeastOneOfValidatorTest.php | 1 - Tests/Fixtures/DummyCompoundConstraintWithGroups.php | 1 - 2 files changed, 2 deletions(-) diff --git a/Tests/Constraints/AtLeastOneOfValidatorTest.php b/Tests/Constraints/AtLeastOneOfValidatorTest.php index 6a5bce5ba..8bda680b2 100644 --- a/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -28,7 +28,6 @@ use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\LessThan; use Symfony\Component\Validator\Constraints\Negative; -use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Constraints\Regex; diff --git a/Tests/Fixtures/DummyCompoundConstraintWithGroups.php b/Tests/Fixtures/DummyCompoundConstraintWithGroups.php index 8b2693ff6..f106fb577 100644 --- a/Tests/Fixtures/DummyCompoundConstraintWithGroups.php +++ b/Tests/Fixtures/DummyCompoundConstraintWithGroups.php @@ -14,7 +14,6 @@ use Symfony\Component\Validator\Constraints\Compound; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; -use Symfony\Component\Validator\Constraints\Regex; class DummyCompoundConstraintWithGroups extends Compound { From 651feaea32097823591fe700a19e537dc78a1ba5 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 1 Oct 2024 15:17:35 +0200 Subject: [PATCH 092/149] Remove a few unnecessary full qualifier --- Mapping/Factory/MetadataFactoryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapping/Factory/MetadataFactoryInterface.php b/Mapping/Factory/MetadataFactoryInterface.php index 8e89d3b7d..b7a86ffc8 100644 --- a/Mapping/Factory/MetadataFactoryInterface.php +++ b/Mapping/Factory/MetadataFactoryInterface.php @@ -15,7 +15,7 @@ use Symfony\Component\Validator\Mapping\MetadataInterface; /** - * Returns {@link \Symfony\Component\Validator\Mapping\MetadataInterface} instances for values. + * Returns {@link MetadataInterface} instances for values. * * @author Bernhard Schussek */ From d7aec1c5b21fae00c09d5ad684ef7d529d9f4e21 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 8 Oct 2024 13:36:14 +0200 Subject: [PATCH 093/149] [Validator] Enhance PHPDoc on `Constraint::getTargets()` --- Constraint.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Constraint.php b/Constraint.php index 98bd4b116..5fd8ce84c 100644 --- a/Constraint.php +++ b/Constraint.php @@ -278,10 +278,7 @@ public function validatedBy(): string * Returns whether the constraint can be put onto classes, properties or * both. * - * This method should return one or more of the constants - * Constraint::CLASS_CONSTRAINT and Constraint::PROPERTY_CONSTRAINT. - * - * @return string|string[] One or more constant values + * @return self::CLASS_CONSTRAINT|self::PROPERTY_CONSTRAINT|array */ public function getTargets(): string|array { From ad53851b1d8fe91e20a3d2a93d7f3077cab7322b Mon Sep 17 00:00:00 2001 From: Fabdouarrahmane Date: Wed, 25 Sep 2024 17:46:27 +0200 Subject: [PATCH 094/149] [Validator][CidrValidator] Fix error message for `OutOfRangeNetmask` validation --- Constraints/CidrValidator.php | 2 +- Tests/Constraints/CidrValidatorTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Constraints/CidrValidator.php b/Constraints/CidrValidator.php index 1c6f4c0bc..f9dc5c233 100644 --- a/Constraints/CidrValidator.php +++ b/Constraints/CidrValidator.php @@ -81,7 +81,7 @@ public function validate($value, Constraint $constraint): void $this->context ->buildViolation($constraint->netmaskRangeViolationMessage) ->setParameter('{{ min }}', $constraint->netmaskMin) - ->setParameter('{{ max }}', $constraint->netmaskMax) + ->setParameter('{{ max }}', $netmaskMax) ->setCode(Cidr::OUT_OF_RANGE_ERROR) ->addViolation(); } diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 5ac531094..04d0b8999 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -106,7 +106,7 @@ public function testInvalidIpAddressAndNetmask(string|\Stringable $cidr) /** * @dataProvider getOutOfRangeNetmask */ - public function testOutOfRangeNetmask(string $cidr, ?string $version = null, ?int $min = null, ?int $max = null) + public function testOutOfRangeNetmask(string $cidr, int $maxExpected, ?string $version = null, ?int $min = null, ?int $max = null) { $cidrConstraint = new Cidr([ 'version' => $version, @@ -118,7 +118,7 @@ public function testOutOfRangeNetmask(string $cidr, ?string $version = null, ?in $this ->buildViolation('The value of the netmask should be between {{ min }} and {{ max }}.') ->setParameter('{{ min }}', $cidrConstraint->netmaskMin) - ->setParameter('{{ max }}', $cidrConstraint->netmaskMax) + ->setParameter('{{ max }}', $maxExpected) ->setCode(Cidr::OUT_OF_RANGE_ERROR) ->assertRaised(); } @@ -240,9 +240,9 @@ public static function getWithInvalidMasksAndIps(): array public static function getOutOfRangeNetmask(): array { return [ - ['10.0.0.0/24', Ip::V4, 10, 20], - ['10.0.0.0/128'], - ['2001:0DB8:85A3:0000:0000:8A2E:0370:7334/24', Ip::V6, 10, 20], + ['10.0.0.0/24', 20, Ip::V4, 10, 20], + ['10.0.0.0/128', 32], + ['2001:0DB8:85A3:0000:0000:8A2E:0370:7334/24', 20, Ip::V6, 10, 20], ]; } From c08e0274ce73ededf8eb7dbe74df01d773b299b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A6=85KoNekoD?= Date: Wed, 9 Oct 2024 15:09:40 +0300 Subject: [PATCH 095/149] [Validator] Pass context to expressions used in `When` constraints --- CHANGELOG.md | 1 + Constraints/WhenValidator.php | 1 + Tests/Constraints/WhenValidatorTest.php | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9af46e49..6b5be184c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG * Add the `WordCount` constraint * Add the `Week` constraint * Add `CompoundConstraintTestCase` to ease testing Compound Constraints + * Add context variable to `WhenValidator` 7.1 --- diff --git a/Constraints/WhenValidator.php b/Constraints/WhenValidator.php index c02a450e8..b41ba83ff 100644 --- a/Constraints/WhenValidator.php +++ b/Constraints/WhenValidator.php @@ -33,6 +33,7 @@ public function validate(mixed $value, Constraint $constraint): void $variables = $constraint->values; $variables['value'] = $value; $variables['this'] = $context->getObject(); + $variables['context'] = $context; if ($this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { $context->getValidator()->inContext($context) diff --git a/Tests/Constraints/WhenValidatorTest.php b/Tests/Constraints/WhenValidatorTest.php index cba81b25a..f79d53031 100644 --- a/Tests/Constraints/WhenValidatorTest.php +++ b/Tests/Constraints/WhenValidatorTest.php @@ -85,6 +85,31 @@ public function testConstraintsAreExecutedWithObject() ])); } + public function testConstraintsAreExecutedWithNestedObject() + { + $parent = new \stdClass(); + $parent->child = new \stdClass(); + $parent->ok = true; + + $number = new \stdClass(); + $number->value = 1; + + $this->setObject($parent); + $this->setPropertyPath('child.value'); + $this->setRoot($parent); + + $constraints = [ + new PositiveOrZero(), + ]; + + $this->expectValidateValue(0, $number->value, $constraints); + + $this->validator->validate($number->value, new When([ + 'expression' => 'context.getRoot().ok === true', + 'constraints' => $constraints, + ])); + } + public function testConstraintsAreExecutedWithValue() { $constraints = [ From 948cd1bea6eace7e236412f789cae74f5a89f1f9 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 14 Oct 2024 20:03:05 +0200 Subject: [PATCH 096/149] Reduce common control flows --- Mapping/Loader/PropertyInfoLoader.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index f878974ec..39a00792a 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -133,10 +133,6 @@ public function loadClassMetadata(ClassMetadata $metadata): bool $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar'])); } } - - if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) { - $metadata->addPropertyConstraint($property, new NotNull()); - } } else { if ($hasTypeConstraint) { continue; @@ -157,10 +153,10 @@ public function loadClassMetadata(ClassMetadata $metadata): bool if (null !== $typeConstraint = $this->getTypeConstraint($type)) { $metadata->addPropertyConstraint($property, $typeConstraint); } + } - if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) { - $metadata->addPropertyConstraint($property, new NotNull()); - } + if (!$nullable && !$hasNotBlankConstraint && !$hasNotNullConstraint) { + $metadata->addPropertyConstraint($property, new NotNull()); } } From 9cc24fc67f37ebf70c92e08fa1d21f73e654deba Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 14 Aug 2024 15:51:51 +0200 Subject: [PATCH 097/149] [TypeInfo] Redesign Type methods and nullability --- Mapping/Loader/PropertyInfoLoader.php | 36 +++++++++++++++++---------- composer.json | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index 39a00792a..335d10e65 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -16,11 +16,13 @@ use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type as PropertyInfoType; use Symfony\Component\TypeInfo\Type as TypeInfoType; +use Symfony\Component\TypeInfo\Type\BuiltinType; use Symfony\Component\TypeInfo\Type\CollectionType; -use Symfony\Component\TypeInfo\Type\IntersectionType; +use Symfony\Component\TypeInfo\Type\CompositeTypeInterface; +use Symfony\Component\TypeInfo\Type\NullableType; use Symfony\Component\TypeInfo\Type\ObjectType; -use Symfony\Component\TypeInfo\Type\UnionType; use Symfony\Component\TypeInfo\TypeIdentifier; +use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; @@ -139,11 +141,10 @@ public function loadClassMetadata(ClassMetadata $metadata): bool } $type = $types; - $nullable = false; + $nullable = $type->isNullable(); - if ($type instanceof UnionType && $type->isNullable()) { - $nullable = true; - $type = $type->asNonNullable(); + if ($type instanceof NullableType) { + $type = $type->getWrappedType(); } if ($type instanceof CollectionType) { @@ -193,18 +194,27 @@ private function getTypeConstraintLegacy(string $builtinType, PropertyInfoType $ private function getTypeConstraint(TypeInfoType $type): ?Type { - if ($type instanceof UnionType || $type instanceof IntersectionType) { - return ($type->isA(TypeIdentifier::INT) || $type->isA(TypeIdentifier::FLOAT) || $type->isA(TypeIdentifier::STRING) || $type->isA(TypeIdentifier::BOOL)) ? new Type(['type' => 'scalar']) : null; + if ($type instanceof CompositeTypeInterface) { + return $type->isIdentifiedBy( + TypeIdentifier::INT, + TypeIdentifier::FLOAT, + TypeIdentifier::STRING, + TypeIdentifier::BOOL, + TypeIdentifier::TRUE, + TypeIdentifier::FALSE, + ) ? new Type(['type' => 'scalar']) : null; } - $baseType = $type->getBaseType(); + while ($type instanceof WrappingTypeInterface) { + $type = $type->getWrappedType(); + } - if ($baseType instanceof ObjectType) { - return new Type(['type' => $baseType->getClassName()]); + if ($type instanceof ObjectType) { + return new Type(['type' => $type->getClassName()]); } - if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) { - return new Type(['type' => $baseType->getTypeIdentifier()->value]); + if ($type instanceof BuiltinType && TypeIdentifier::MIXED !== $type->getTypeIdentifier()) { + return new Type(['type' => $type->getTypeIdentifier()->value]); } return null; diff --git a/composer.json b/composer.json index 5177d37d2..aacf40731 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation": "^6.4.3|^7.0.3", - "symfony/type-info": "^7.1", + "symfony/type-info": "^7.2", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { From 0e893cee94b62a0350d8cb140482a714446245f9 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 18 Oct 2024 16:04:52 +0200 Subject: [PATCH 098/149] Remove always true/false occurrences --- Tests/Constraints/CallbackValidatorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/Constraints/CallbackValidatorTest.php b/Tests/Constraints/CallbackValidatorTest.php index e888baa7a..ef92d3072 100644 --- a/Tests/Constraints/CallbackValidatorTest.php +++ b/Tests/Constraints/CallbackValidatorTest.php @@ -246,7 +246,6 @@ public function testPayloadIsPassedToCallback() $constraint = new Callback(callback: $callback, payload: 'Hello world!'); $this->validator->validate($object, $constraint); $this->assertEquals('Hello world!', $payloadCopy); - $payloadCopy = 'Replace me!'; $payloadCopy = 'Replace me!'; $constraint = new Callback([ From fd1350fd33a5ac62e6569e9d39086bfb337f9a28 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Fri, 8 Nov 2024 12:26:09 +0100 Subject: [PATCH 099/149] Improve type for the `mode` property of the Bic constraint - the property should not be nullable as the constructor prevents assigning null and the validator does not expect it either - phpdoc types are added to let static analysis tool know about the valid modes thanks to the available class constants --- Constraints/Bic.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Constraints/Bic.php b/Constraints/Bic.php index c0974c918..692d83117 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -58,14 +58,17 @@ class Bic extends Constraint public string $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.'; public ?string $iban = null; public ?string $ibanPropertyPath = null; - public ?string $mode = self::VALIDATION_MODE_STRICT; + /** + * @var self::VALIDATION_MODE_* + */ + public string $mode = self::VALIDATION_MODE_STRICT; /** - * @param array|null $options - * @param string|null $iban An IBAN value to validate that its country code is the same as the BIC's one - * @param string|null $ibanPropertyPath Property path to the IBAN value when validating objects - * @param string[]|null $groups - * @param string|null $mode The mode used to validate the BIC; pass null to use the default mode (strict) + * @param array|null $options + * @param string|null $iban An IBAN value to validate that its country code is the same as the BIC's one + * @param string|null $ibanPropertyPath Property path to the IBAN value when validating objects + * @param string[]|null $groups + * @param self::VALIDATION_MODE_*|null $mode The mode used to validate the BIC; pass null to use the default mode (strict) */ public function __construct( ?array $options = null, From 06044beed4a19130e9980aa47d4e9ca2d526b7b9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 14 Nov 2024 09:07:34 +0100 Subject: [PATCH 100/149] fix compatibility with PHP < 8.2.4 --- Constraints/NoSuspiciousCharactersValidator.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Constraints/NoSuspiciousCharactersValidator.php b/Constraints/NoSuspiciousCharactersValidator.php index d82a62d57..0b7a78ef9 100644 --- a/Constraints/NoSuspiciousCharactersValidator.php +++ b/Constraints/NoSuspiciousCharactersValidator.php @@ -99,7 +99,17 @@ public function validate(mixed $value, Constraint $constraint): void } foreach (self::CHECK_ERROR as $check => $error) { - if (!($errorCode & $check)) { + if (\PHP_VERSION_ID < 80204) { + if (!($checks & $check)) { + continue; + } + + $checker->setChecks($check); + + if (!$checker->isSuspicious($value)) { + continue; + } + } elseif (!($errorCode & $check)) { continue; } From 2e3ef85a977d7b18ae0cc2f21f8112d4fb42cb70 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 14 Nov 2024 10:51:05 +0100 Subject: [PATCH 101/149] prevent failures around not existing TypeInfo classes Having a getType() method on an extractor is not enough. Such a method may exist to be forward-compatible with the TypeInfo component. We still must not call it if the TypeInfo component is not installed to prevent running into errors for not-defined classes when the TypeInfo component is not installed. --- Mapping/Loader/PropertyInfoLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index f878974ec..2b4582a76 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -175,7 +175,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool */ private function getPropertyTypes(string $className, string $property): TypeInfoType|array|null { - if (method_exists($this->typeExtractor, 'getType')) { + if (class_exists(TypeInfoType::class) && method_exists($this->typeExtractor, 'getType')) { return $this->typeExtractor->getType($className, $property); } From 39a0d0b92fba00e554706983315e06a919dc0ce1 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 14 Nov 2024 13:01:20 +0100 Subject: [PATCH 102/149] [Serializer][PropertyInfo][Validator] TypeInfo 7.2 compatibility --- Mapping/Loader/PropertyInfoLoader.php | 64 +++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index f878974ec..c95ffce53 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -16,10 +16,14 @@ use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type as PropertyInfoType; use Symfony\Component\TypeInfo\Type as TypeInfoType; +use Symfony\Component\TypeInfo\Type\BuiltinType; use Symfony\Component\TypeInfo\Type\CollectionType; +use Symfony\Component\TypeInfo\Type\CompositeTypeInterface; use Symfony\Component\TypeInfo\Type\IntersectionType; +use Symfony\Component\TypeInfo\Type\NullableType; use Symfony\Component\TypeInfo\Type\ObjectType; use Symfony\Component\TypeInfo\Type\UnionType; +use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; use Symfony\Component\TypeInfo\TypeIdentifier; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\NotBlank; @@ -143,7 +147,23 @@ public function loadClassMetadata(ClassMetadata $metadata): bool } $type = $types; - $nullable = false; + + // BC layer for type-info < 7.2 + if (!class_exists(NullableType::class)) { + $nullable = false; + + if ($type instanceof UnionType && $type->isNullable()) { + $nullable = true; + $type = $type->asNonNullable(); + } + } else { + $nullable = $type->isNullable(); + + if ($type instanceof NullableType) { + $type = $type->getWrappedType(); + } + } + if ($type instanceof UnionType && $type->isNullable()) { $nullable = true; @@ -197,18 +217,46 @@ private function getTypeConstraintLegacy(string $builtinType, PropertyInfoType $ private function getTypeConstraint(TypeInfoType $type): ?Type { - if ($type instanceof UnionType || $type instanceof IntersectionType) { - return ($type->isA(TypeIdentifier::INT) || $type->isA(TypeIdentifier::FLOAT) || $type->isA(TypeIdentifier::STRING) || $type->isA(TypeIdentifier::BOOL)) ? new Type(['type' => 'scalar']) : null; + // BC layer for type-info < 7.2 + if (!interface_exists(CompositeTypeInterface::class)) { + if ($type instanceof UnionType || $type instanceof IntersectionType) { + return ($type->isA(TypeIdentifier::INT) || $type->isA(TypeIdentifier::FLOAT) || $type->isA(TypeIdentifier::STRING) || $type->isA(TypeIdentifier::BOOL)) ? new Type(['type' => 'scalar']) : null; + } + + $baseType = $type->getBaseType(); + + if ($baseType instanceof ObjectType) { + return new Type(['type' => $baseType->getClassName()]); + } + + if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) { + return new Type(['type' => $baseType->getTypeIdentifier()->value]); + } + + return null; + } + + if ($type instanceof CompositeTypeInterface) { + return $type->isIdentifiedBy( + TypeIdentifier::INT, + TypeIdentifier::FLOAT, + TypeIdentifier::STRING, + TypeIdentifier::BOOL, + TypeIdentifier::TRUE, + TypeIdentifier::FALSE, + ) ? new Type(['type' => 'scalar']) : null; } - $baseType = $type->getBaseType(); + while ($type instanceof WrappingTypeInterface) { + $type = $type->getWrappedType(); + } - if ($baseType instanceof ObjectType) { - return new Type(['type' => $baseType->getClassName()]); + if ($type instanceof ObjectType) { + return new Type(['type' => $type->getClassName()]); } - if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) { - return new Type(['type' => $baseType->getTypeIdentifier()->value]); + if ($type instanceof BuiltinType && TypeIdentifier::MIXED !== $type->getTypeIdentifier()) { + return new Type(['type' => $type->getTypeIdentifier()->value]); } return null; From d8ec57f689f66db04468554b517be9c623e05ead Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 14 Nov 2024 17:57:42 +0100 Subject: [PATCH 103/149] fix lowest allowed TypeInfo version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index aacf40731..ba2d1c208 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation": "^6.4.3|^7.0.3", - "symfony/type-info": "^7.2", + "symfony/type-info": "^7.2-RC1", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { From 41af3ca1f1f10710700dfc9e94466fe20ed7375c Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 14 Nov 2024 13:01:20 +0100 Subject: [PATCH 104/149] [TypeInfo][Serializer][PropertyInfo][Validator] TypeInfo 7.1 compatibility --- Mapping/Loader/PropertyInfoLoader.php | 40 +++++++++++++++++++++++++-- composer.json | 2 +- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index 773a73a9e..444e864f2 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -19,10 +19,12 @@ use Symfony\Component\TypeInfo\Type\BuiltinType; use Symfony\Component\TypeInfo\Type\CollectionType; use Symfony\Component\TypeInfo\Type\CompositeTypeInterface; +use Symfony\Component\TypeInfo\Type\IntersectionType; use Symfony\Component\TypeInfo\Type\NullableType; use Symfony\Component\TypeInfo\Type\ObjectType; -use Symfony\Component\TypeInfo\TypeIdentifier; +use Symfony\Component\TypeInfo\Type\UnionType; use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; +use Symfony\Component\TypeInfo\TypeIdentifier; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; @@ -141,7 +143,22 @@ public function loadClassMetadata(ClassMetadata $metadata): bool } $type = $types; - $nullable = $type->isNullable(); + + // BC layer for type-info < 7.2 + if (!class_exists(NullableType::class)) { + $nullable = false; + + if ($type instanceof UnionType && $type->isNullable()) { + $nullable = true; + $type = $type->asNonNullable(); + } + } else { + $nullable = $type->isNullable(); + + if ($type instanceof NullableType) { + $type = $type->getWrappedType(); + } + } if ($type instanceof NullableType) { $type = $type->getWrappedType(); @@ -194,6 +211,25 @@ private function getTypeConstraintLegacy(string $builtinType, PropertyInfoType $ private function getTypeConstraint(TypeInfoType $type): ?Type { + // BC layer for type-info < 7.2 + if (!interface_exists(CompositeTypeInterface::class)) { + if ($type instanceof UnionType || $type instanceof IntersectionType) { + return ($type->isA(TypeIdentifier::INT) || $type->isA(TypeIdentifier::FLOAT) || $type->isA(TypeIdentifier::STRING) || $type->isA(TypeIdentifier::BOOL)) ? new Type(['type' => 'scalar']) : null; + } + + $baseType = $type->getBaseType(); + + if ($baseType instanceof ObjectType) { + return new Type(['type' => $baseType->getClassName()]); + } + + if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) { + return new Type(['type' => $baseType->getTypeIdentifier()->value]); + } + + return null; + } + if ($type instanceof CompositeTypeInterface) { return $type->isIdentifiedBy( TypeIdentifier::INT, diff --git a/composer.json b/composer.json index ba2d1c208..5177d37d2 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation": "^6.4.3|^7.0.3", - "symfony/type-info": "^7.2-RC1", + "symfony/type-info": "^7.1", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { From f7ed4ad2df0f4d6c7ed23b0ffa27652ba05a19f4 Mon Sep 17 00:00:00 2001 From: Bart Baaten Date: Mon, 11 Nov 2024 22:11:18 +0100 Subject: [PATCH 105/149] Updated Dutch translations for validator --- Resources/translations/validators.nl.xlf | 44 ++++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Resources/translations/validators.nl.xlf b/Resources/translations/validators.nl.xlf index fdea10f0e..f5f3d2ee1 100644 --- a/Resources/translations/validators.nl.xlf +++ b/Resources/translations/validators.nl.xlf @@ -76,7 +76,7 @@ This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less. - Deze waarde is te lang. Hij mag maximaal {{ limit }} teken bevatten.|Deze waarde is te lang. Hij mag maximaal {{ limit }} tekens bevatten. + Deze waarde is te lang. Deze mag maximaal één teken bevatten.|Deze waarde is te lang. Deze mag maximaal {{ limit }} tekens bevatten. This value should be {{ limit }} or more. @@ -84,7 +84,7 @@ This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more. - Deze waarde is te kort. Hij moet tenminste {{ limit }} teken bevatten.|Deze waarde is te kort. Hij moet tenminste {{ limit }} tekens bevatten. + Deze waarde is te kort. Deze moet ten minste één teken bevatten.|Deze waarde is te kort. Deze moet ten minste {{ limit }} tekens bevatten. This value should not be blank. @@ -160,7 +160,7 @@ The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px. - De afbeelding is te breed ({{ width }}px). De maximaal breedte is {{ max_width }}px. + De afbeelding is te breed ({{ width }}px). De maximale breedte is {{ max_width }}px. The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. @@ -168,7 +168,7 @@ The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. - De afbeelding is te hoog ({{ height }}px). De maximaal hoogte is {{ max_height }}px. + De afbeelding is te hoog ({{ height }}px). De maximale hoogte is {{ max_height }}px. The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. @@ -180,7 +180,7 @@ This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters. - Deze waarde moet exact {{ limit }} teken lang zijn.|Deze waarde moet exact {{ limit }} tekens lang zijn. + Deze waarde moet exact één teken lang zijn.|Deze waarde moet exact {{ limit }} tekens lang zijn. The file was only partially uploaded. @@ -196,7 +196,7 @@ Cannot write temporary file to disk. - Kan het tijdelijke bestand niet wegschrijven op disk. + Kan het tijdelijke bestand niet wegschrijven op de schijf. A PHP extension caused the upload to fail. @@ -204,15 +204,15 @@ This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more. - Deze collectie moet {{ limit }} element of meer bevatten.|Deze collectie moet {{ limit }} elementen of meer bevatten. + Deze collectie moet één of meer elementen bevatten.|Deze collectie moet {{ limit }} of meer elementen bevatten. This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less. - Deze collectie moet {{ limit }} element of minder bevatten.|Deze collectie moet {{ limit }} elementen of minder bevatten. + Deze collectie moet één of minder elementen bevatten.|Deze collectie moet {{ limit }} of minder elementen bevatten. This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements. - Deze collectie moet exact {{ limit }} element bevatten.|Deze collectie moet exact {{ limit }} elementen bevatten. + Deze collectie moet exact één element bevatten.|Deze collectie moet exact {{ limit }} elementen bevatten. Invalid card number. @@ -236,11 +236,11 @@ This value is neither a valid ISBN-10 nor a valid ISBN-13. - Deze waarde is geen geldige ISBN-10 of ISBN-13 waarde. + Deze waarde is geen geldige ISBN-10 of ISBN-13. This value is not a valid ISSN. - Deze waarde is geen geldige ISSN waarde. + Deze waarde is geen geldige ISSN. This value is not a valid currency. @@ -256,7 +256,7 @@ This value should be greater than or equal to {{ compared_value }}. - Deze waarde moet groter dan of gelijk aan {{ compared_value }} zijn. + Deze waarde moet groter of gelijk aan {{ compared_value }} zijn. This value should be identical to {{ compared_value_type }} {{ compared_value }}. @@ -304,7 +304,7 @@ The host could not be resolved. - De hostnaam kon niet worden bepaald. + De hostnaam kon niet worden gevonden. This value does not match the expected {{ charset }} charset. @@ -312,7 +312,7 @@ This value is not a valid Business Identifier Code (BIC). - Deze waarde is geen geldige zakelijke identificatiecode (BIC). + Deze waarde is geen geldige bankidentificatiecode (BIC). Error @@ -328,7 +328,7 @@ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. - Deze bedrijfsidentificatiecode (BIC) is niet gekoppeld aan IBAN {{ iban }}. + Deze bankidentificatiecode (BIC) is niet gekoppeld aan IBAN {{ iban }}. This value should be valid JSON. @@ -360,7 +360,7 @@ This password has been leaked in a data breach, it must not be used. Please use another password. - Dit wachtwoord is gelekt vanwege een data-inbreuk, het moet niet worden gebruikt. Kies een ander wachtwoord. + Dit wachtwoord is gelekt bij een datalek en mag niet worden gebruikt. Kies een ander wachtwoord. This value should be between {{ min }} and {{ max }}. @@ -400,11 +400,11 @@ The value of the netmask should be between {{ min }} and {{ max }}. - De waarde van de netmask moet zich tussen {{ min }} en {{ max }} bevinden. + De waarde van het netmasker moet tussen {{ min }} en {{ max }} liggen. The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. - De bestandsnaam is te lang. Het moet {{ filename_max_length }} karakter of minder zijn.|De bestandsnaam is te lang. Het moet {{ filename_max_length }} karakters of minder zijn. + De bestandsnaam is te lang. Het moet {{ filename_max_length }} of minder karakters zijn.|De bestandsnaam is te lang. Het moet {{ filename_max_length }} of minder karakters zijn. The password strength is too low. Please use a stronger password. @@ -452,19 +452,19 @@ This value does not represent a valid week in the ISO 8601 format. - This value does not represent a valid week in the ISO 8601 format. + Deze waarde vertegenwoordigt geen geldige week in het ISO 8601-formaat. This value is not a valid week. - This value is not a valid week. + Deze waarde is geen geldige week. This value should not be before week "{{ min }}". - This value should not be before week "{{ min }}". + Deze waarde mag niet vóór week "{{ min }}" liggen. This value should not be after week "{{ max }}". - This value should not be after week "{{ max }}". + Deze waarde mag niet na week "{{ max }}" liggen. From 0114282f37e4861ea2ab7e12db72d977570f35e7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 2 Dec 2024 10:54:37 +0100 Subject: [PATCH 106/149] set the violation path only if the "errorPath" option is set --- Constraints/UniqueValidator.php | 12 ++++++--- Tests/Constraints/UniqueValidatorTest.php | 30 +++++++++-------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Constraints/UniqueValidator.php b/Constraints/UniqueValidator.php index 408088c7e..8977fd221 100644 --- a/Constraints/UniqueValidator.php +++ b/Constraints/UniqueValidator.php @@ -47,11 +47,15 @@ public function validate(mixed $value, Constraint $constraint): void } if (\in_array($element, $collectionElements, true)) { - $this->context->buildViolation($constraint->message) - ->atPath("[$index]".(null !== $constraint->errorPath ? ".{$constraint->errorPath}" : '')) + $violationBuilder = $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($element)) - ->setCode(Unique::IS_NOT_UNIQUE) - ->addViolation(); + ->setCode(Unique::IS_NOT_UNIQUE); + + if (null !== $constraint->errorPath) { + $violationBuilder->atPath("[$index].{$constraint->errorPath}"); + } + + $violationBuilder->addViolation(); return; } diff --git a/Tests/Constraints/UniqueValidatorTest.php b/Tests/Constraints/UniqueValidatorTest.php index 76862e73f..f81621d65 100644 --- a/Tests/Constraints/UniqueValidatorTest.php +++ b/Tests/Constraints/UniqueValidatorTest.php @@ -61,7 +61,7 @@ public static function getValidValues() /** * @dataProvider getInvalidValues */ - public function testInvalidValues($value, $expectedMessageParam, string $expectedErrorPath) + public function testInvalidValues($value, $expectedMessageParam) { $constraint = new Unique([ 'message' => 'myMessage', @@ -71,7 +71,6 @@ public function testInvalidValues($value, $expectedMessageParam, string $expecte $this->buildViolation('myMessage') ->setParameter('{{ value }}', $expectedMessageParam) ->setCode(Unique::IS_NOT_UNIQUE) - ->atPath($expectedErrorPath) ->assertRaised(); } @@ -80,12 +79,12 @@ public static function getInvalidValues() $object = new \stdClass(); return [ - yield 'not unique booleans' => [[true, true], 'true', 'property.path[1]'], - yield 'not unique integers' => [[1, 2, 3, 3], 3, 'property.path[3]'], - yield 'not unique floats' => [[0.1, 0.2, 0.1], 0.1, 'property.path[2]'], - yield 'not unique string' => [['a', 'b', 'a'], '"a"', 'property.path[2]'], - yield 'not unique arrays' => [[[1, 1], [2, 3], [1, 1]], 'array', 'property.path[2]'], - yield 'not unique objects' => [[$object, $object], 'object', 'property.path[1]'], + yield 'not unique booleans' => [[true, true], 'true'], + yield 'not unique integers' => [[1, 2, 3, 3], 3], + yield 'not unique floats' => [[0.1, 0.2, 0.1], 0.1], + yield 'not unique string' => [['a', 'b', 'a'], '"a"'], + yield 'not unique arrays' => [[[1, 1], [2, 3], [1, 1]], 'array'], + yield 'not unique objects' => [[$object, $object], 'object'], ]; } @@ -97,7 +96,6 @@ public function testInvalidValueNamed() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '3') ->setCode(Unique::IS_NOT_UNIQUE) - ->atPath('property.path[3]') ->assertRaised(); } @@ -154,7 +152,6 @@ public function testExpectsNonUniqueObjects($callback) $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') ->setCode(Unique::IS_NOT_UNIQUE) - ->atPath('property.path[2]') ->assertRaised(); } @@ -179,7 +176,6 @@ public function testExpectsInvalidNonStrictComparison() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '1') ->setCode(Unique::IS_NOT_UNIQUE) - ->atPath('property.path[1]') ->assertRaised(); } @@ -206,7 +202,6 @@ public function testExpectsInvalidCaseInsensitiveComparison() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"hello"') ->setCode(Unique::IS_NOT_UNIQUE) - ->atPath('property.path[1]') ->assertRaised(); } @@ -251,7 +246,7 @@ public static function getInvalidFieldNames(): array /** * @dataProvider getInvalidCollectionValues */ - public function testInvalidCollectionValues(array $value, array $fields, string $expectedMessageParam, string $expectedErrorPath) + public function testInvalidCollectionValues(array $value, array $fields, string $expectedMessageParam) { $this->validator->validate($value, new Unique([ 'message' => 'myMessage', @@ -260,7 +255,6 @@ public function testInvalidCollectionValues(array $value, array $fields, string $this->buildViolation('myMessage') ->setParameter('{{ value }}', $expectedMessageParam) ->setCode(Unique::IS_NOT_UNIQUE) - ->atPath($expectedErrorPath) ->assertRaised(); } @@ -270,27 +264,25 @@ public static function getInvalidCollectionValues(): array 'unique string' => [[ ['lang' => 'eng', 'translation' => 'hi'], ['lang' => 'eng', 'translation' => 'hello'], - ], ['lang'], 'array', 'property.path[1]'], + ], ['lang'], 'array'], 'unique floats' => [[ ['latitude' => 51.509865, 'longitude' => -0.118092, 'poi' => 'capital'], ['latitude' => 52.520008, 'longitude' => 13.404954], ['latitude' => 51.509865, 'longitude' => -0.118092], - ], ['latitude', 'longitude'], 'array', 'property.path[2]'], + ], ['latitude', 'longitude'], 'array'], 'unique int' => [[ ['id' => 1, 'email' => 'bar@email.com'], ['id' => 1, 'email' => 'foo@email.com'], - ], ['id'], 'array', 'property.path[1]'], + ], ['id'], 'array'], 'unique null' => [ [null, null], [], 'null', - 'property.path[1]', ], 'unique field null' => [ [['nullField' => null], ['nullField' => null]], ['nullField'], 'array', - 'property.path[1]', ], ]; } From 6036f24941aa2f5bc6d2cf274f04521273485dec Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 11 Dec 2024 14:08:35 +0100 Subject: [PATCH 107/149] chore: PHP CS Fixer fixes --- Constraints/ChoiceValidator.php | 4 ++-- Constraints/WeekValidator.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Constraints/ChoiceValidator.php b/Constraints/ChoiceValidator.php index 367cc6567..916c0732a 100644 --- a/Constraints/ChoiceValidator.php +++ b/Constraints/ChoiceValidator.php @@ -53,8 +53,8 @@ public function validate(mixed $value, Constraint $constraint): void throw new ConstraintDefinitionException('The Choice constraint expects a valid callback.'); } $choices = $choices(); - if (!is_array($choices)) { - throw new ConstraintDefinitionException(sprintf('The Choice constraint callback "%s" is expected to return an array, but returned "%s".', trim($this->formatValue($constraint->callback), '"'), get_debug_type($choices))); + if (!\is_array($choices)) { + throw new ConstraintDefinitionException(\sprintf('The Choice constraint callback "%s" is expected to return an array, but returned "%s".', trim($this->formatValue($constraint->callback), '"'), get_debug_type($choices))); } } else { $choices = $constraint->choices; diff --git a/Constraints/WeekValidator.php b/Constraints/WeekValidator.php index 83052c1a9..8139b156e 100644 --- a/Constraints/WeekValidator.php +++ b/Constraints/WeekValidator.php @@ -43,8 +43,8 @@ public function validate(mixed $value, Constraint $constraint): void return; } - [$year, $weekNumber] = \explode('-W', $value, 2); - $weeksInYear = (int) \date('W', \mktime(0, 0, 0, 12, 28, $year)); + [$year, $weekNumber] = explode('-W', $value, 2); + $weeksInYear = (int) date('W', mktime(0, 0, 0, 12, 28, $year)); if ($weekNumber > $weeksInYear) { $this->context->buildViolation($constraint->invalidWeekNumberMessage) @@ -56,7 +56,7 @@ public function validate(mixed $value, Constraint $constraint): void } if ($constraint->min) { - [$minYear, $minWeekNumber] = \explode('-W', $constraint->min, 2); + [$minYear, $minWeekNumber] = explode('-W', $constraint->min, 2); if ($year < $minYear || ($year === $minYear && $weekNumber < $minWeekNumber)) { $this->context->buildViolation($constraint->tooLowMessage) ->setCode(Week::TOO_LOW_ERROR) @@ -69,7 +69,7 @@ public function validate(mixed $value, Constraint $constraint): void } if ($constraint->max) { - [$maxYear, $maxWeekNumber] = \explode('-W', $constraint->max, 2); + [$maxYear, $maxWeekNumber] = explode('-W', $constraint->max, 2); if ($year > $maxYear || ($year === $maxYear && $weekNumber > $maxWeekNumber)) { $this->context->buildViolation($constraint->tooHighMessage) ->setCode(Week::TOO_HIGH_ERROR) From 188f7d2811fbb5a200920d6d4a7df17f8918749a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 27 Dec 2024 12:59:17 +0100 Subject: [PATCH 108/149] the "max" option can be zero --- Constraints/Count.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/Count.php b/Constraints/Count.php index 175e07ebb..38ea8d6e7 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -45,7 +45,7 @@ class Count extends Constraint /** * @param int<0, max>|array|null $exactly The exact expected number of elements * @param int<0, max>|null $min Minimum expected number of elements - * @param positive-int|null $max Maximum expected number of elements + * @param int<0, max>|null $max Maximum expected number of elements * @param positive-int|null $divisibleBy The number the collection count should be divisible by * @param string[]|null $groups * @param array $options From bf859ce6bdb96796a5a41265c3714f60c5025067 Mon Sep 17 00:00:00 2001 From: Maxime COLIN Date: Thu, 19 Dec 2024 11:01:08 +0100 Subject: [PATCH 109/149] [Validator] Validate SVG ratio in Image validator --- CHANGELOG.md | 5 + Constraints/ImageValidator.php | 70 ++++++++- Tests/Constraints/Fixtures/test_landscape.svg | 2 + .../Fixtures/test_landscape_height.svg | 2 + .../Fixtures/test_landscape_width.svg | 2 + .../Fixtures/test_landscape_width_height.svg | 2 + Tests/Constraints/Fixtures/test_no_size.svg | 2 + Tests/Constraints/Fixtures/test_portrait.svg | 2 + Tests/Constraints/Fixtures/test_square.svg | 2 + Tests/Constraints/ImageValidatorTest.php | 136 ++++++++++++++++++ 10 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 Tests/Constraints/Fixtures/test_landscape.svg create mode 100644 Tests/Constraints/Fixtures/test_landscape_height.svg create mode 100644 Tests/Constraints/Fixtures/test_landscape_width.svg create mode 100644 Tests/Constraints/Fixtures/test_landscape_width_height.svg create mode 100644 Tests/Constraints/Fixtures/test_no_size.svg create mode 100644 Tests/Constraints/Fixtures/test_portrait.svg create mode 100644 Tests/Constraints/Fixtures/test_square.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b5be184c..5e480139e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.3 +--- + + * Add support for ratio checks for SVG files to the `Image` constraint + 7.2 --- diff --git a/Constraints/ImageValidator.php b/Constraints/ImageValidator.php index a715471d9..219ad620c 100644 --- a/Constraints/ImageValidator.php +++ b/Constraints/ImageValidator.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\Mime\MimeTypes; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\LogicException; @@ -50,7 +52,13 @@ public function validate(mixed $value, Constraint $constraint): void return; } - $size = @getimagesize($value); + $isSvg = $this->isSvg($value); + + if ($isSvg) { + $size = $this->getSvgSize($value); + } else { + $size = @getimagesize($value); + } if (!$size || (0 === $size[0]) || (0 === $size[1])) { $this->context->buildViolation($constraint->sizeNotDetectedMessage) @@ -63,7 +71,7 @@ public function validate(mixed $value, Constraint $constraint): void $width = $size[0]; $height = $size[1]; - if ($constraint->minWidth) { + if (!$isSvg && $constraint->minWidth) { if (!ctype_digit((string) $constraint->minWidth)) { throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum width.', $constraint->minWidth)); } @@ -79,7 +87,7 @@ public function validate(mixed $value, Constraint $constraint): void } } - if ($constraint->maxWidth) { + if (!$isSvg && $constraint->maxWidth) { if (!ctype_digit((string) $constraint->maxWidth)) { throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum width.', $constraint->maxWidth)); } @@ -95,7 +103,7 @@ public function validate(mixed $value, Constraint $constraint): void } } - if ($constraint->minHeight) { + if (!$isSvg && $constraint->minHeight) { if (!ctype_digit((string) $constraint->minHeight)) { throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum height.', $constraint->minHeight)); } @@ -111,7 +119,7 @@ public function validate(mixed $value, Constraint $constraint): void } } - if ($constraint->maxHeight) { + if (!$isSvg && $constraint->maxHeight) { if (!ctype_digit((string) $constraint->maxHeight)) { throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum height.', $constraint->maxHeight)); } @@ -127,7 +135,7 @@ public function validate(mixed $value, Constraint $constraint): void $pixels = $width * $height; - if (null !== $constraint->minPixels) { + if (!$isSvg && null !== $constraint->minPixels) { if (!ctype_digit((string) $constraint->minPixels)) { throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum amount of pixels.', $constraint->minPixels)); } @@ -143,7 +151,7 @@ public function validate(mixed $value, Constraint $constraint): void } } - if (null !== $constraint->maxPixels) { + if (!$isSvg && null !== $constraint->maxPixels) { if (!ctype_digit((string) $constraint->maxPixels)) { throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum amount of pixels.', $constraint->maxPixels)); } @@ -231,4 +239,52 @@ public function validate(mixed $value, Constraint $constraint): void imagedestroy($resource); } } + + private function isSvg(mixed $value): bool + { + if ($value instanceof File) { + $mime = $value->getMimeType(); + } elseif (class_exists(MimeTypes::class)) { + $mime = MimeTypes::getDefault()->guessMimeType($value); + } elseif (!class_exists(File::class)) { + return false; + } else { + $mime = (new File($value))->getMimeType(); + } + + return 'image/svg+xml' === $mime; + } + + /** + * @return array{int, int}|null index 0 and 1 contains respectively the width and the height of the image, null if size can't be found + */ + private function getSvgSize(mixed $value): ?array + { + if ($value instanceof File) { + $content = $value->getContent(); + } elseif (!class_exists(File::class)) { + return null; + } else { + $content = (new File($value))->getContent(); + } + + if (1 === preg_match('/]+width="(?[0-9]+)"[^<>]*>/', $content, $widthMatches)) { + $width = (int) $widthMatches['width']; + } + + if (1 === preg_match('/]+height="(?[0-9]+)"[^<>]*>/', $content, $heightMatches)) { + $height = (int) $heightMatches['height']; + } + + if (1 === preg_match('/]+viewBox="-?[0-9]+ -?[0-9]+ (?-?[0-9]+) (?-?[0-9]+)"[^<>]*>/', $content, $viewBoxMatches)) { + $width ??= (int) $viewBoxMatches['width']; + $height ??= (int) $viewBoxMatches['height']; + } + + if (isset($width) && isset($height)) { + return [$width, $height]; + } + + return null; + } } diff --git a/Tests/Constraints/Fixtures/test_landscape.svg b/Tests/Constraints/Fixtures/test_landscape.svg new file mode 100644 index 000000000..e1212da08 --- /dev/null +++ b/Tests/Constraints/Fixtures/test_landscape.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/Fixtures/test_landscape_height.svg b/Tests/Constraints/Fixtures/test_landscape_height.svg new file mode 100644 index 000000000..7a54631f1 --- /dev/null +++ b/Tests/Constraints/Fixtures/test_landscape_height.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/Fixtures/test_landscape_width.svg b/Tests/Constraints/Fixtures/test_landscape_width.svg new file mode 100644 index 000000000..a64c0b1e0 --- /dev/null +++ b/Tests/Constraints/Fixtures/test_landscape_width.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/Fixtures/test_landscape_width_height.svg b/Tests/Constraints/Fixtures/test_landscape_width_height.svg new file mode 100644 index 000000000..ec7b52445 --- /dev/null +++ b/Tests/Constraints/Fixtures/test_landscape_width_height.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/Fixtures/test_no_size.svg b/Tests/Constraints/Fixtures/test_no_size.svg new file mode 100644 index 000000000..e0af766e8 --- /dev/null +++ b/Tests/Constraints/Fixtures/test_no_size.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/Fixtures/test_portrait.svg b/Tests/Constraints/Fixtures/test_portrait.svg new file mode 100644 index 000000000..d17c991be --- /dev/null +++ b/Tests/Constraints/Fixtures/test_portrait.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/Fixtures/test_square.svg b/Tests/Constraints/Fixtures/test_square.svg new file mode 100644 index 000000000..ffac7f14a --- /dev/null +++ b/Tests/Constraints/Fixtures/test_square.svg @@ -0,0 +1,2 @@ + + diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index 908517081..d18d81eea 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -498,4 +498,140 @@ public static function provideInvalidMimeTypeWithNarrowedSet() ]), ]; } + + /** @dataProvider provideSvgWithViolation */ + public function testSvgWithViolation(string $image, Image $constraint, string $violation, array $parameters = []) + { + $this->validator->validate($image, $constraint); + + $this->buildViolation('myMessage') + ->setCode($violation) + ->setParameters($parameters) + ->assertRaised(); + } + + public static function provideSvgWithViolation(): iterable + { + yield 'No size svg' => [ + __DIR__.'/Fixtures/test_no_size.svg', + new Image(allowLandscape: false, sizeNotDetectedMessage: 'myMessage'), + Image::SIZE_NOT_DETECTED_ERROR, + ]; + + yield 'Landscape SVG not allowed' => [ + __DIR__.'/Fixtures/test_landscape.svg', + new Image(allowLandscape: false, allowLandscapeMessage: 'myMessage'), + Image::LANDSCAPE_NOT_ALLOWED_ERROR, + [ + '{{ width }}' => 500, + '{{ height }}' => 200, + ], + ]; + + yield 'Portrait SVG not allowed' => [ + __DIR__.'/Fixtures/test_portrait.svg', + new Image(allowPortrait: false, allowPortraitMessage: 'myMessage'), + Image::PORTRAIT_NOT_ALLOWED_ERROR, + [ + '{{ width }}' => 200, + '{{ height }}' => 500, + ], + ]; + + yield 'Square SVG not allowed' => [ + __DIR__.'/Fixtures/test_square.svg', + new Image(allowSquare: false, allowSquareMessage: 'myMessage'), + Image::SQUARE_NOT_ALLOWED_ERROR, + [ + '{{ width }}' => 500, + '{{ height }}' => 500, + ], + ]; + + yield 'Landscape with width attribute SVG allowed' => [ + __DIR__.'/Fixtures/test_landscape_width.svg', + new Image(allowLandscape: false, allowLandscapeMessage: 'myMessage'), + Image::LANDSCAPE_NOT_ALLOWED_ERROR, + [ + '{{ width }}' => 600, + '{{ height }}' => 200, + ], + ]; + + yield 'Landscape with height attribute SVG not allowed' => [ + __DIR__.'/Fixtures/test_landscape_height.svg', + new Image(allowLandscape: false, allowLandscapeMessage: 'myMessage'), + Image::LANDSCAPE_NOT_ALLOWED_ERROR, + [ + '{{ width }}' => 500, + '{{ height }}' => 300, + ], + ]; + + yield 'Landscape with width and height attribute SVG not allowed' => [ + __DIR__.'/Fixtures/test_landscape_width_height.svg', + new Image(allowLandscape: false, allowLandscapeMessage: 'myMessage'), + Image::LANDSCAPE_NOT_ALLOWED_ERROR, + [ + '{{ width }}' => 600, + '{{ height }}' => 300, + ], + ]; + + yield 'SVG Min ratio 2' => [ + __DIR__.'/Fixtures/test_square.svg', + new Image(minRatio: 2, minRatioMessage: 'myMessage'), + Image::RATIO_TOO_SMALL_ERROR, + [ + '{{ ratio }}' => '1', + '{{ min_ratio }}' => '2', + ], + ]; + + yield 'SVG Min ratio 0.5' => [ + __DIR__.'/Fixtures/test_square.svg', + new Image(maxRatio: 0.5, maxRatioMessage: 'myMessage'), + Image::RATIO_TOO_BIG_ERROR, + [ + '{{ ratio }}' => '1', + '{{ max_ratio }}' => '0.5', + ], + ]; + } + + /** @dataProvider provideSvgWithoutViolation */ + public function testSvgWithoutViolation(string $image, Image $constraint) + { + $this->validator->validate($image, $constraint); + + $this->assertNoViolation(); + } + + public static function provideSvgWithoutViolation(): iterable + { + yield 'Landscape SVG allowed' => [ + __DIR__.'/Fixtures/test_landscape.svg', + new Image(allowLandscape: true, allowLandscapeMessage: 'myMessage'), + ]; + + yield 'Portrait SVG allowed' => [ + __DIR__.'/Fixtures/test_portrait.svg', + new Image(allowPortrait: true, allowPortraitMessage: 'myMessage'), + ]; + + yield 'Square SVG allowed' => [ + __DIR__.'/Fixtures/test_square.svg', + new Image(allowSquare: true, allowSquareMessage: 'myMessage'), + ]; + + yield 'SVG Min ratio 1' => [ + __DIR__.'/Fixtures/test_square.svg', + new Image(minRatio: 1, minRatioMessage: 'myMessage'), + ]; + + yield 'SVG Max ratio 1' => [ + __DIR__.'/Fixtures/test_square.svg', + new Image(maxRatio: 1, maxRatioMessage: 'myMessage'), + ]; + } } From fa6ce06c5ae3884d037b0fa33aed1afda6689da9 Mon Sep 17 00:00:00 2001 From: sauliusnord Date: Mon, 14 Oct 2024 10:54:55 +0300 Subject: [PATCH 110/149] [Validator] [DateTime] Add `format` to error messages --- CHANGELOG.md | 1 + Constraints/DateTimeValidator.php | 4 ++++ Tests/Constraints/DateTimeValidatorTest.php | 3 +++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e480139e..b5e79134e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ CHANGELOG * Add the `Week` constraint * Add `CompoundConstraintTestCase` to ease testing Compound Constraints * Add context variable to `WhenValidator` + * Add `format` parameter to `DateTime` constraint violation message 7.1 --- diff --git a/Constraints/DateTimeValidator.php b/Constraints/DateTimeValidator.php index 9784a5797..f5765cbf6 100644 --- a/Constraints/DateTimeValidator.php +++ b/Constraints/DateTimeValidator.php @@ -44,6 +44,7 @@ public function validate(mixed $value, Constraint $constraint): void if (0 < $errors['error_count']) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ format }}', $this->formatValue($constraint->format)) ->setCode(DateTime::INVALID_FORMAT_ERROR) ->addViolation(); @@ -58,16 +59,19 @@ public function validate(mixed $value, Constraint $constraint): void if ('The parsed date was invalid' === $warning) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ format }}', $this->formatValue($constraint->format)) ->setCode(DateTime::INVALID_DATE_ERROR) ->addViolation(); } elseif ('The parsed time was invalid' === $warning) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ format }}', $this->formatValue($constraint->format)) ->setCode(DateTime::INVALID_TIME_ERROR) ->addViolation(); } else { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ format }}', $this->formatValue($constraint->format)) ->setCode(DateTime::INVALID_FORMAT_ERROR) ->addViolation(); } diff --git a/Tests/Constraints/DateTimeValidatorTest.php b/Tests/Constraints/DateTimeValidatorTest.php index 8da07c424..42519ffd4 100644 --- a/Tests/Constraints/DateTimeValidatorTest.php +++ b/Tests/Constraints/DateTimeValidatorTest.php @@ -53,6 +53,7 @@ public function testDateTimeWithDefaultFormat() $this->buildViolation('This value is not a valid datetime.') ->setParameter('{{ value }}', '"1995-03-24"') + ->setParameter('{{ format }}', '"Y-m-d H:i:s"') ->setCode(DateTime::INVALID_FORMAT_ERROR) ->assertRaised(); } @@ -96,6 +97,7 @@ public function testInvalidDateTimes($format, $dateTime, $code) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$dateTime.'"') + ->setParameter('{{ format }}', '"'.$format.'"') ->setCode($code) ->assertRaised(); } @@ -124,6 +126,7 @@ public function testInvalidDateTimeNamed() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"2010-01-01 00:00:00"') + ->setParameter('{{ format }}', '"Y-m-d"') ->setCode(DateTime::INVALID_FORMAT_ERROR) ->assertRaised(); } From 9ef1ad0bfb365d11101b51b6fde98b59a7920515 Mon Sep 17 00:00:00 2001 From: Raffaele Carelle Date: Fri, 11 Oct 2024 14:24:50 +0200 Subject: [PATCH 111/149] [Validator] Add `Slug` constraint --- CHANGELOG.md | 1 + Constraints/Slug.php | 41 +++++++++ Constraints/SlugValidator.php | 47 +++++++++++ Tests/Constraints/SlugTest.php | 47 +++++++++++ Tests/Constraints/SlugValidatorTest.php | 106 ++++++++++++++++++++++++ 5 files changed, 242 insertions(+) create mode 100644 Constraints/Slug.php create mode 100644 Constraints/SlugValidator.php create mode 100644 Tests/Constraints/SlugTest.php create mode 100644 Tests/Constraints/SlugValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e79134e..70468d4d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add support for ratio checks for SVG files to the `Image` constraint + * Add the `Slug` constraint 7.2 --- diff --git a/Constraints/Slug.php b/Constraints/Slug.php new file mode 100644 index 000000000..68dcf9925 --- /dev/null +++ b/Constraints/Slug.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * Validates that a value is a valid slug. + * + * @author Raffaele Carelle + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class Slug extends Constraint +{ + public const NOT_SLUG_ERROR = '14e6df1e-c8ab-4395-b6ce-04b132a3765e'; + + public string $message = 'This value is not a valid slug.'; + public string $regex = '/^[a-z0-9]+(?:-[a-z0-9]+)*$/'; + + public function __construct( + ?array $options = null, + ?string $regex = null, + ?string $message = null, + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct($options, $groups, $payload); + + $this->message = $message ?? $this->message; + $this->regex = $regex ?? $this->regex; + } +} diff --git a/Constraints/SlugValidator.php b/Constraints/SlugValidator.php new file mode 100644 index 000000000..b914cad31 --- /dev/null +++ b/Constraints/SlugValidator.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\UnexpectedValueException; + +/** + * @author Raffaele Carelle + */ +class SlugValidator extends ConstraintValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof Slug) { + throw new UnexpectedTypeException($constraint, Slug::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!\is_scalar($value) && !$value instanceof \Stringable) { + throw new UnexpectedValueException($value, 'string'); + } + + $value = (string) $value; + + if (0 === preg_match($constraint->regex, $value)) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Slug::NOT_SLUG_ERROR) + ->addViolation(); + } + } +} diff --git a/Tests/Constraints/SlugTest.php b/Tests/Constraints/SlugTest.php new file mode 100644 index 000000000..a2c5b07d3 --- /dev/null +++ b/Tests/Constraints/SlugTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Slug; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; + +class SlugTest extends TestCase +{ + public function testAttributes() + { + $metadata = new ClassMetadata(SlugDummy::class); + $loader = new AttributeLoader(); + self::assertTrue($loader->loadClassMetadata($metadata)); + + [$bConstraint] = $metadata->properties['b']->getConstraints(); + self::assertSame('myMessage', $bConstraint->message); + self::assertSame(['Default', 'SlugDummy'], $bConstraint->groups); + + [$cConstraint] = $metadata->properties['c']->getConstraints(); + self::assertSame(['my_group'], $cConstraint->groups); + self::assertSame('some attached data', $cConstraint->payload); + } +} + +class SlugDummy +{ + #[Slug] + private $a; + + #[Slug(message: 'myMessage')] + private $b; + + #[Slug(groups: ['my_group'], payload: 'some attached data')] + private $c; +} diff --git a/Tests/Constraints/SlugValidatorTest.php b/Tests/Constraints/SlugValidatorTest.php new file mode 100644 index 000000000..8a2270ff2 --- /dev/null +++ b/Tests/Constraints/SlugValidatorTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\Slug; +use Symfony\Component\Validator\Constraints\SlugValidator; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class SlugValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): SlugValidator + { + return new SlugValidator(); + } + + public function testNullIsValid() + { + $this->validator->validate(null, new Slug()); + + $this->assertNoViolation(); + } + + public function testEmptyStringIsValid() + { + $this->validator->validate('', new Slug()); + + $this->assertNoViolation(); + } + + public function testExpectsStringCompatibleType() + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate(new \stdClass(), new Slug()); + } + + /** + * @testWith ["test-slug"] + * ["slug-123-test"] + * ["slug"] + */ + public function testValidSlugs($slug) + { + $this->validator->validate($slug, new Slug()); + + $this->assertNoViolation(); + } + + /** + * @testWith ["NotASlug"] + * ["Not a slug"] + * ["not-á-slug"] + * ["not-@-slug"] + */ + public function testInvalidSlugs($slug) + { + $constraint = new Slug([ + 'message' => 'myMessage', + ]); + + $this->validator->validate($slug, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$slug.'"') + ->setCode(Slug::NOT_SLUG_ERROR) + ->assertRaised(); + } + + /** + * @testWith ["test-slug", true] + * ["slug-123-test", true] + */ + public function testCustomRegexInvalidSlugs($slug) + { + $constraint = new Slug(regex: '/^[a-z0-9]+$/i'); + + $this->validator->validate($slug, $constraint); + + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', '"'.$slug.'"') + ->setCode(Slug::NOT_SLUG_ERROR) + ->assertRaised(); + } + + /** + * @testWith ["slug"] + * @testWith ["test1234"] + */ + public function testCustomRegexValidSlugs($slug) + { + $constraint = new Slug(regex: '/^[a-z0-9]+$/i'); + + $this->validator->validate($slug, $constraint); + + $this->assertNoViolation(); + } +} From a2a27c12b3bd86f10a5ed17c54fd1348768a4cff Mon Sep 17 00:00:00 2001 From: Jan Rosier Date: Mon, 6 Jan 2025 15:35:18 +0100 Subject: [PATCH 112/149] Use spl_object_id() instead of spl_object_hash() --- Tests/Fixtures/FakeMetadataFactory.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/Fixtures/FakeMetadataFactory.php b/Tests/Fixtures/FakeMetadataFactory.php index 6e673ee9f..f905b66fd 100644 --- a/Tests/Fixtures/FakeMetadataFactory.php +++ b/Tests/Fixtures/FakeMetadataFactory.php @@ -21,10 +21,10 @@ class FakeMetadataFactory implements MetadataFactoryInterface public function getMetadataFor($class): MetadataInterface { - $hash = null; + $objectId = null; if (\is_object($class)) { - $hash = spl_object_hash($class); + $objectId = spl_object_id($class); $class = $class::class; } @@ -33,8 +33,8 @@ public function getMetadataFor($class): MetadataInterface } if (!isset($this->metadatas[$class])) { - if (isset($this->metadatas[$hash])) { - return $this->metadatas[$hash]; + if (isset($this->metadatas[$objectId])) { + return $this->metadatas[$objectId]; } throw new NoSuchMetadataException(sprintf('No metadata for "%s"', $class)); @@ -45,10 +45,10 @@ public function getMetadataFor($class): MetadataInterface public function hasMetadataFor($class): bool { - $hash = null; + $objectId = null; if (\is_object($class)) { - $hash = spl_object_hash($class); + $objectId = spl_object_id($class); $class = $class::class; } @@ -56,7 +56,7 @@ public function hasMetadataFor($class): bool return false; } - return isset($this->metadatas[$class]) || isset($this->metadatas[$hash]); + return isset($this->metadatas[$class]) || isset($this->metadatas[$objectId]); } public function addMetadata($metadata) @@ -66,7 +66,7 @@ public function addMetadata($metadata) public function addMetadataForValue($value, MetadataInterface $metadata) { - $key = \is_object($value) ? spl_object_hash($value) : $value; + $key = \is_object($value) ? spl_object_id($value) : $value; $this->metadatas[$key] = $metadata; } } From 67a6f134a034e121a809cc6516ad9f8242a102e7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 7 Jan 2025 09:33:41 +0100 Subject: [PATCH 113/149] fix named arguments support for the Slug constraint --- Constraints/Slug.php | 5 +++-- Tests/Constraints/SlugValidatorTest.php | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Constraints/Slug.php b/Constraints/Slug.php index 68dcf9925..52a5d94c2 100644 --- a/Constraints/Slug.php +++ b/Constraints/Slug.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -26,14 +27,14 @@ class Slug extends Constraint public string $message = 'This value is not a valid slug.'; public string $regex = '/^[a-z0-9]+(?:-[a-z0-9]+)*$/'; + #[HasNamedArguments] public function __construct( - ?array $options = null, ?string $regex = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ) { - parent::__construct($options, $groups, $payload); + parent::__construct([], $groups, $payload); $this->message = $message ?? $this->message; $this->regex = $regex ?? $this->regex; diff --git a/Tests/Constraints/SlugValidatorTest.php b/Tests/Constraints/SlugValidatorTest.php index 8a2270ff2..e8d210b83 100644 --- a/Tests/Constraints/SlugValidatorTest.php +++ b/Tests/Constraints/SlugValidatorTest.php @@ -63,9 +63,7 @@ public function testValidSlugs($slug) */ public function testInvalidSlugs($slug) { - $constraint = new Slug([ - 'message' => 'myMessage', - ]); + $constraint = new Slug(message: 'myMessage'); $this->validator->validate($slug, $constraint); From 04178471f06ebc63be2ae03c92fa7626fce2494f Mon Sep 17 00:00:00 2001 From: Kev Date: Fri, 10 Jan 2025 16:40:38 +0100 Subject: [PATCH 114/149] Fix typo ratio comment --- Constraints/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/Image.php b/Constraints/Image.php index 158d4b2cb..b4afa1eb6 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -107,7 +107,7 @@ class Image extends File * @param int|null $maxHeight Maximum image height * @param int|null $minHeight Minimum image weight * @param int|float|null $maxRatio Maximum image ratio - * @param int|float|null $minRatio Minimum image ration + * @param int|float|null $minRatio Minimum image ratio * @param int|float|null $minPixels Minimum amount of pixels * @param int|float|null $maxPixels Maximum amount of pixels * @param bool|null $allowSquare Whether to allow a square image (defaults to true) From bf6aecc2f873951e2def51a0c62d425046a41638 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 24 Apr 2024 12:12:14 +0200 Subject: [PATCH 115/149] deprecate the use of option arrays to configure validation constraints --- Constraints/AbstractComparison.php | 14 +- Constraints/All.php | 6 + Constraints/AtLeastOneOf.php | 4 + Constraints/Bic.php | 6 + Constraints/Blank.php | 6 + Constraints/Callback.php | 16 +- Constraints/CardScheme.php | 16 +- Constraints/Cascade.php | 8 + Constraints/Choice.php | 5 + Constraints/Cidr.php | 6 + Constraints/Collection.php | 4 + Constraints/Count.php | 14 +- Constraints/CountValidator.php | 8 +- Constraints/Country.php | 6 + Constraints/CssColor.php | 4 + Constraints/Currency.php | 6 + Constraints/Date.php | 6 + Constraints/DateTime.php | 16 +- Constraints/DisableAutoMapping.php | 10 +- Constraints/Email.php | 6 + Constraints/EnableAutoMapping.php | 10 +- Constraints/Expression.php | 16 +- Constraints/ExpressionSyntax.php | 6 + Constraints/File.php | 6 + Constraints/Hostname.php | 6 + Constraints/Iban.php | 6 + Constraints/Ip.php | 6 + Constraints/IsFalse.php | 6 + Constraints/IsNull.php | 6 + Constraints/IsTrue.php | 6 + Constraints/Isbn.php | 16 +- Constraints/Isin.php | 6 + Constraints/Issn.php | 6 + Constraints/Json.php | 6 + Constraints/Language.php | 6 + Constraints/Length.php | 14 +- Constraints/Locale.php | 6 + Constraints/Luhn.php | 6 + Constraints/NoSuspiciousCharacters.php | 6 + Constraints/NotBlank.php | 6 + Constraints/NotCompromisedPassword.php | 6 + Constraints/NotNull.php | 6 + Constraints/PasswordStrength.php | 6 + Constraints/Range.php | 6 + Constraints/Regex.php | 16 +- Constraints/Sequentially.php | 4 + Constraints/Time.php | 6 + Constraints/Timezone.php | 16 +- Constraints/Traverse.php | 10 +- Constraints/Type.php | 18 +- Constraints/Ulid.php | 6 + Constraints/Unique.php | 6 + Constraints/Url.php | 6 + Constraints/Uuid.php | 6 + Constraints/Valid.php | 6 + Constraints/When.php | 16 +- Constraints/ZeroComparisonConstraintTrait.php | 10 +- Context/ExecutionContextInterface.php | 2 +- Mapping/Loader/AbstractLoader.php | 12 +- Mapping/Loader/PropertyInfoLoader.php | 20 +- Tests/Constraints/AllValidatorTest.php | 8 +- .../Constraints/AtLeastOneOfValidatorTest.php | 106 ++-- Tests/Constraints/BicValidatorTest.php | 30 +- Tests/Constraints/BlankValidatorTest.php | 6 +- Tests/Constraints/CallbackValidatorTest.php | 42 +- Tests/Constraints/CardSchemeValidatorTest.php | 16 +- Tests/Constraints/ChoiceValidatorTest.php | 266 ++++---- Tests/Constraints/CidrTest.php | 26 +- Tests/Constraints/CidrValidatorTest.php | 14 +- Tests/Constraints/CollectionTest.php | 64 +- .../CollectionValidatorTestCase.php | 110 ++-- Tests/Constraints/CompositeTest.php | 18 +- Tests/Constraints/CompoundTest.php | 5 +- Tests/Constraints/CountValidatorTestCase.php | 36 +- Tests/Constraints/CountryValidatorTest.php | 16 +- Tests/Constraints/CurrencyValidatorTest.php | 4 +- Tests/Constraints/DateTimeValidatorTest.php | 16 +- Tests/Constraints/DateValidatorTest.php | 6 +- Tests/Constraints/DisableAutoMappingTest.php | 3 + .../Constraints/DivisibleByValidatorTest.php | 6 +- Tests/Constraints/EmailTest.php | 14 +- Tests/Constraints/EmailValidatorTest.php | 38 +- Tests/Constraints/EnableAutoMappingTest.php | 3 + Tests/Constraints/EqualToValidatorTest.php | 6 +- Tests/Constraints/ExpressionSyntaxTest.php | 12 +- .../ExpressionSyntaxValidatorTest.php | 44 +- Tests/Constraints/ExpressionValidatorTest.php | 88 +-- Tests/Constraints/FileTest.php | 12 +- Tests/Constraints/FileValidatorPathTest.php | 6 +- Tests/Constraints/FileValidatorTestCase.php | 115 ++-- .../GreaterThanOrEqualValidatorTest.php | 6 +- ...idatorWithPositiveOrZeroConstraintTest.php | 6 + .../Constraints/GreaterThanValidatorTest.php | 6 +- ...hanValidatorWithPositiveConstraintTest.php | 6 + Tests/Constraints/HostnameValidatorTest.php | 34 +- Tests/Constraints/IbanValidatorTest.php | 4 +- .../Constraints/IdenticalToValidatorTest.php | 6 +- Tests/Constraints/ImageValidatorTest.php | 400 ++++++------ Tests/Constraints/IpTest.php | 8 +- Tests/Constraints/IpValidatorTest.php | 152 +++-- Tests/Constraints/IsFalseValidatorTest.php | 24 +- Tests/Constraints/IsNullValidatorTest.php | 4 +- Tests/Constraints/IsTrueValidatorTest.php | 22 +- Tests/Constraints/IsbnValidatorTest.php | 33 +- Tests/Constraints/IsinValidatorTest.php | 4 +- Tests/Constraints/IssnValidatorTest.php | 20 +- Tests/Constraints/JsonValidatorTest.php | 4 +- Tests/Constraints/LanguageValidatorTest.php | 20 +- Tests/Constraints/LengthTest.php | 20 +- Tests/Constraints/LengthValidatorTest.php | 66 +- .../LessThanOrEqualValidatorTest.php | 6 +- ...idatorWithNegativeOrZeroConstraintTest.php | 6 + Tests/Constraints/LessThanValidatorTest.php | 6 +- ...hanValidatorWithNegativeConstraintTest.php | 6 + Tests/Constraints/LocaleValidatorTest.php | 24 +- Tests/Constraints/LuhnValidatorTest.php | 4 +- .../NoSuspiciousCharactersValidatorTest.php | 4 +- Tests/Constraints/NotBlankTest.php | 8 +- Tests/Constraints/NotBlankValidatorTest.php | 40 +- .../NotCompromisedPasswordValidatorTest.php | 36 +- Tests/Constraints/NotEqualToValidatorTest.php | 6 +- .../NotIdenticalToValidatorTest.php | 6 +- Tests/Constraints/NotNullValidatorTest.php | 22 +- Tests/Constraints/RangeTest.php | 15 + Tests/Constraints/RangeValidatorTest.php | 222 +++---- Tests/Constraints/RegexTest.php | 24 +- Tests/Constraints/RegexValidatorTest.php | 10 +- .../Constraints/SequentiallyValidatorTest.php | 30 +- Tests/Constraints/TimeValidatorTest.php | 8 +- Tests/Constraints/TimezoneTest.php | 22 +- Tests/Constraints/TimezoneValidatorTest.php | 56 +- Tests/Constraints/TypeValidatorTest.php | 47 +- Tests/Constraints/UlidValidatorTest.php | 4 +- Tests/Constraints/UniqueTest.php | 6 + Tests/Constraints/UniqueValidatorTest.php | 47 +- Tests/Constraints/UrlTest.php | 8 +- Tests/Constraints/UrlValidatorTest.php | 46 +- Tests/Constraints/UuidTest.php | 8 +- Tests/Constraints/UuidValidatorTest.php | 22 +- Tests/Constraints/ValidTest.php | 2 +- Tests/Constraints/WhenTest.php | 41 +- Tests/Constraints/WhenValidatorTest.php | 96 ++- Tests/Fixtures/DummyCompoundConstraint.php | 2 +- ...yEntityConstraintWithoutNamedArguments.php | 16 + Tests/Fixtures/EntityStaticCar.php | 2 +- Tests/Fixtures/EntityStaticCarTurbo.php | 2 +- Tests/Fixtures/EntityStaticVehicle.php | 2 +- .../LazyLoadingMetadataFactoryTest.php | 28 +- Tests/Mapping/Loader/AttributeLoaderTest.php | 56 +- .../ConstraintWithoutNamedArguments.php | 22 + Tests/Mapping/Loader/XmlFileLoaderTest.php | 39 +- Tests/Mapping/Loader/YamlFileLoaderTest.php | 39 +- ...traint-without-named-arguments-support.xml | 10 + ...traint-without-named-arguments-support.yml | 4 + Tests/Mapping/MemberMetadataTest.php | 4 +- Tests/Validator/RecursiveValidatorTest.php | 583 +++++++++--------- 156 files changed, 2429 insertions(+), 1752 deletions(-) create mode 100644 Tests/Fixtures/DummyEntityConstraintWithoutNamedArguments.php create mode 100644 Tests/Mapping/Loader/Fixtures/ConstraintWithoutNamedArguments.php create mode 100644 Tests/Mapping/Loader/constraint-without-named-arguments-support.xml create mode 100644 Tests/Mapping/Loader/constraint-without-named-arguments-support.yml diff --git a/Constraints/AbstractComparison.php b/Constraints/AbstractComparison.php index 4d34b1401..1b4629c43 100644 --- a/Constraints/AbstractComparison.php +++ b/Constraints/AbstractComparison.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\LogicException; @@ -28,11 +29,20 @@ abstract class AbstractComparison extends Constraint public mixed $value = null; public ?string $propertyPath = null; - public function __construct(mixed $value = null, ?string $propertyPath = null, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) + #[HasNamedArguments] + public function __construct(mixed $value = null, ?string $propertyPath = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($value)) { - $options = array_merge($value, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($value, $options ?? []); } elseif (null !== $value) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $value; } diff --git a/Constraints/All.php b/Constraints/All.php index 1da939dd5..bbaa9a9a5 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -28,8 +29,13 @@ class All extends Composite * @param array|array|null $constraints * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { + if (\is_array($constraints) && !array_is_list($constraints)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($constraints ?? [], $groups, $payload); } diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index 7fe57972d..a03ca7f7a 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -41,6 +41,10 @@ class AtLeastOneOf extends Composite */ public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null, ?string $message = null, ?string $messageCollection = null, ?bool $includeInternalMessages = null) { + if (\is_array($constraints) && !array_is_list($constraints)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($constraints ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 692d83117..34121af75 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -13,6 +13,7 @@ use Symfony\Component\Intl\Countries; use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -70,6 +71,7 @@ class Bic extends Constraint * @param string[]|null $groups * @param self::VALIDATION_MODE_*|null $mode The mode used to validate the BIC; pass null to use the default mode (strict) */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -90,6 +92,10 @@ public function __construct( throw new InvalidArgumentException('The "mode" parameter value is not valid.'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Blank.php b/Constraints/Blank.php index 283164fc1..09c5a5fac 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,8 +34,13 @@ class Blank extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Callback.php b/Constraints/Callback.php index 5ef48d880..ff02183bd 100644 --- a/Constraints/Callback.php +++ b/Constraints/Callback.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -30,17 +31,28 @@ class Callback extends Constraint * @param string|string[]|callable|array|null $callback The callback definition * @param string[]|null $groups */ - public function __construct(array|string|callable|null $callback = null, ?array $groups = null, mixed $payload = null, array $options = []) + #[HasNamedArguments] + public function __construct(array|string|callable|null $callback = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { // Invocation through attributes with an array parameter only if (\is_array($callback) && 1 === \count($callback) && isset($callback['value'])) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + $callback = $callback['value']; } if (!\is_array($callback) || (!isset($callback['callback']) && !isset($callback['groups']) && !isset($callback['payload']))) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['callback'] = $callback; } else { - $options = array_merge($callback, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($callback, $options ?? []); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 86085ee2e..0944761d8 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -49,13 +50,22 @@ class CardScheme extends Constraint /** * @param non-empty-string|non-empty-string[]|array|null $schemes Name(s) of the number scheme(s) used to validate the credit card number * @param string[]|null $groups - * @param array $options + * @param array|null $options */ - public function __construct(array|string|null $schemes, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) + #[HasNamedArguments] + public function __construct(array|string|null $schemes, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($schemes) && \is_string(key($schemes))) { - $options = array_merge($schemes, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($schemes, $options ?? []); } else { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $schemes; } diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index 8879ca657..016b7e7ef 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -28,11 +29,18 @@ class Cascade extends Constraint * @param non-empty-string[]|non-empty-string|array|null $exclude Properties excluded from validation * @param array|null $options */ + #[HasNamedArguments] public function __construct(array|string|null $exclude = null, ?array $options = null) { if (\is_array($exclude) && !array_is_list($exclude)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + $options = array_merge($exclude, $options ?? []); } else { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + $this->exclude = array_flip((array) $exclude); } diff --git a/Constraints/Choice.php b/Constraints/Choice.php index 18570c5c9..d17e5f654 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -59,6 +60,7 @@ public function getDefaultOption(): ?string * @param string[]|null $groups * @param bool|null $match Whether to validate the values are part of the choices or not (defaults to true) */ + #[HasNamedArguments] public function __construct( string|array $options = [], ?array $choices = null, @@ -78,7 +80,10 @@ public function __construct( if (\is_array($options) && $options && array_is_list($options)) { $choices ??= $options; $options = []; + } elseif (\is_array($options) && [] !== $options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } + if (null !== $choices) { $options['value'] = $choices; } diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index 349c29b66..82d52317a 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -74,6 +75,7 @@ class Cidr extends Constraint /** @var callable|null */ public $normalizer; + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $version = null, @@ -84,6 +86,10 @@ public function __construct( $payload = null, ?callable $normalizer = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + $this->version = $version ?? $options['version'] ?? $this->version; if (!\array_key_exists($this->version, self::NET_MAXES)) { diff --git a/Constraints/Collection.php b/Constraints/Collection.php index 3ffd0a6fc..4253697ef 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -41,10 +42,13 @@ class Collection extends Composite * @param bool|null $allowExtraFields Whether to allow additional keys not declared in the configured fields (defaults to false) * @param bool|null $allowMissingFields Whether to allow the collection to lack some fields declared in the configured fields (defaults to false) */ + #[HasNamedArguments] public function __construct(mixed $fields = null, ?array $groups = null, mixed $payload = null, ?bool $allowExtraFields = null, ?bool $allowMissingFields = null, ?string $extraFieldsMessage = null, ?string $missingFieldsMessage = null) { if (self::isFieldsOption($fields)) { $fields = ['fields' => $fields]; + } else { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($fields, $groups, $payload); diff --git a/Constraints/Count.php b/Constraints/Count.php index 38ea8d6e7..31479b578 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\MissingOptionsException; @@ -48,8 +49,9 @@ class Count extends Constraint * @param int<0, max>|null $max Maximum expected number of elements * @param positive-int|null $divisibleBy The number the collection count should be divisible by * @param string[]|null $groups - * @param array $options + * @param array|null $options */ + #[HasNamedArguments] public function __construct( int|array|null $exactly = null, ?int $min = null, @@ -61,11 +63,17 @@ public function __construct( ?string $divisibleByMessage = null, ?array $groups = null, mixed $payload = null, - array $options = [], + ?array $options = null, ) { if (\is_array($exactly)) { - $options = array_merge($exactly, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($exactly, $options ?? []); $exactly = $options['value'] ?? null; + } elseif (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; } $min ??= $options['min'] ?? null; diff --git a/Constraints/CountValidator.php b/Constraints/CountValidator.php index 04f539333..40d889fe9 100644 --- a/Constraints/CountValidator.php +++ b/Constraints/CountValidator.php @@ -70,10 +70,10 @@ public function validate(mixed $value, Constraint $constraint): void ->getValidator() ->inContext($this->context) ->validate($count, [ - new DivisibleBy([ - 'value' => $constraint->divisibleBy, - 'message' => $constraint->divisibleByMessage, - ]), + new DivisibleBy( + value: $constraint->divisibleBy, + message: $constraint->divisibleByMessage, + ), ], $this->context->getGroup()); } } diff --git a/Constraints/Country.php b/Constraints/Country.php index cb0016212..dcbdd01a1 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Intl\Countries; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -41,6 +42,7 @@ class Country extends Constraint * * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3#Current_codes */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -52,6 +54,10 @@ public function __construct( throw new LogicException('The Intl component is required to use the Country constraint. Try running "composer require symfony/intl".'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 4f61df18f..302e779eb 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -66,6 +67,7 @@ class CssColor extends Constraint * @param string[]|null $groups * @param array|null $options */ + #[HasNamedArguments] public function __construct(array|string $formats = [], ?string $message = null, ?array $groups = null, $payload = null, ?array $options = null) { $validationModesAsString = implode(', ', self::$validationModes); @@ -73,6 +75,8 @@ public function __construct(array|string $formats = [], ?string $message = null, if (!$formats) { $options['value'] = self::$validationModes; } elseif (\is_array($formats) && \is_string(key($formats))) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + $options = array_merge($formats, $options ?? []); } elseif (\is_array($formats)) { if ([] === array_intersect(self::$validationModes, $formats)) { diff --git a/Constraints/Currency.php b/Constraints/Currency.php index 337481543..c9b034d6d 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Intl\Currencies; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -38,12 +39,17 @@ class Currency extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { if (!class_exists(Currencies::class)) { throw new LogicException('The Intl component is required to use the Currency constraint. Try running "composer require symfony/intl".'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Date.php b/Constraints/Date.php index add108079..98d42ad72 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -37,8 +38,13 @@ class Date extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index 5b3fd1b0b..863b5d119 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -39,13 +40,22 @@ class DateTime extends Constraint /** * @param non-empty-string|array|null $format The datetime format to match (defaults to 'Y-m-d H:i:s') * @param string[]|null $groups - * @param array $options + * @param array|null $options */ - public function __construct(string|array|null $format = null, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) + #[HasNamedArguments] + public function __construct(string|array|null $format = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($format)) { - $options = array_merge($format, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($format, $options ?? []); } elseif (null !== $format) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $format; } diff --git a/Constraints/DisableAutoMapping.php b/Constraints/DisableAutoMapping.php index 2b762059f..2ece16a04 100644 --- a/Constraints/DisableAutoMapping.php +++ b/Constraints/DisableAutoMapping.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -28,13 +29,18 @@ class DisableAutoMapping extends Constraint /** * @param array|null $options */ - public function __construct(?array $options = null) + #[HasNamedArguments] + public function __construct(?array $options = null, mixed $payload = null) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + if (\is_array($options) && \array_key_exists('groups', $options)) { throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } - parent::__construct($options); + parent::__construct($options, null, $payload); } public function getTargets(): string|array diff --git a/Constraints/Email.php b/Constraints/Email.php index 7b9b9ba2b..05bd6ee1b 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Egulias\EmailValidator\EmailValidator as StrictEmailValidator; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Exception\LogicException; @@ -50,6 +51,7 @@ class Email extends Constraint * @param self::VALIDATION_MODE_*|null $mode The pattern used to validate the email address; pass null to use the default mode configured for the EmailValidator * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -66,6 +68,10 @@ public function __construct( throw new InvalidArgumentException('The "mode" parameter value is not valid.'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/EnableAutoMapping.php b/Constraints/EnableAutoMapping.php index a4808d08c..5de158a39 100644 --- a/Constraints/EnableAutoMapping.php +++ b/Constraints/EnableAutoMapping.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -28,13 +29,18 @@ class EnableAutoMapping extends Constraint /** * @param array|null $options */ - public function __construct(?array $options = null) + #[HasNamedArguments] + public function __construct(?array $options = null, mixed $payload = null) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + if (\is_array($options) && \array_key_exists('groups', $options)) { throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } - parent::__construct($options); + parent::__construct($options, null, $payload); } public function getTargets(): string|array diff --git a/Constraints/Expression.php b/Constraints/Expression.php index a9423a08b..355ac2610 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -13,6 +13,7 @@ use Symfony\Component\ExpressionLanguage\Expression as ExpressionObject; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -42,16 +43,17 @@ class Expression extends Constraint * @param string|ExpressionObject|array|null $expression The expression to evaluate * @param array|null $values The values of the custom variables used in the expression (defaults to an empty array) * @param string[]|null $groups - * @param array $options + * @param array|null $options * @param bool|null $negate Whether to fail if the expression evaluates to true (defaults to false) */ + #[HasNamedArguments] public function __construct( string|ExpressionObject|array|null $expression, ?string $message = null, ?array $values = null, ?array $groups = null, mixed $payload = null, - array $options = [], + ?array $options = null, ?bool $negate = null, ) { if (!class_exists(ExpressionLanguage::class)) { @@ -59,8 +61,16 @@ public function __construct( } if (\is_array($expression)) { - $options = array_merge($expression, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($expression, $options ?? []); } else { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $expression; } diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index 8f4f59834..0dcf8a4e0 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -37,8 +38,13 @@ class ExpressionSyntax extends Constraint * @param string[]|null $allowedVariables Restrict the available variables in the expression to these values (defaults to null that allows any variable) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?string $service = null, ?array $allowedVariables = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/File.php b/Constraints/File.php index 8948b9ea6..6c77559ca 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -89,6 +90,7 @@ class File extends Constraint * * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types */ + #[HasNamedArguments] public function __construct( ?array $options = null, int|string|null $maxSize = null, @@ -116,6 +118,10 @@ public function __construct( array|string|null $extensions = null, ?string $extensionsMessage = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->maxSize = $maxSize ?? $this->maxSize; diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index 3090f1ecc..1ea588ce5 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -35,6 +36,7 @@ class Hostname extends Constraint * @param bool|null $requireTld Whether to require the hostname to include its top-level domain (defaults to true) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -42,6 +44,10 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Iban.php b/Constraints/Iban.php index 71b6d18c7..be79d3dd3 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -45,8 +46,13 @@ class Iban extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 97743030d..4930b5f82 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -111,6 +112,7 @@ class Ip extends Constraint * @param self::V4*|self::V6*|self::ALL*|null $version The IP version to validate (defaults to {@see self::V4}) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $version = null, @@ -119,6 +121,10 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->version = $version ?? $this->version; diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index a46b071c9..42ef5aac2 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,8 +34,13 @@ class IsFalse extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index 9f86e8560..b97a88660 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,8 +34,13 @@ class IsNull extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index c8418a280..849ded086 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,8 +34,13 @@ class IsTrue extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index c1bc83a34..36813bb7e 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -52,8 +53,9 @@ class Isbn extends Constraint * @param self::ISBN_*|array|null $type The type of ISBN to validate (i.e. {@see Isbn::ISBN_10}, {@see Isbn::ISBN_13} or null to accept both, defaults to null) * @param string|null $message If defined, this message has priority over the others * @param string[]|null $groups - * @param array $options + * @param array|null $options */ + #[HasNamedArguments] public function __construct( string|array|null $type = null, ?string $message = null, @@ -62,11 +64,19 @@ public function __construct( ?string $bothIsbnMessage = null, ?array $groups = null, mixed $payload = null, - array $options = [], + ?array $options = null, ) { if (\is_array($type)) { - $options = array_merge($type, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($type, $options ?? []); } elseif (null !== $type) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $type; } diff --git a/Constraints/Isin.php b/Constraints/Isin.php index 3f722d21a..405175914 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -42,8 +43,13 @@ class Isin extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Issn.php b/Constraints/Issn.php index 7d0e5b515..d70b26b38 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -50,6 +51,7 @@ class Issn extends Constraint * @param bool|null $requireHyphen Whether to require a hyphenated ISSN value (defaults to false) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -58,6 +60,10 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Json.php b/Constraints/Json.php index 3b85a347c..954487fc9 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,8 +34,13 @@ class Json extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Language.php b/Constraints/Language.php index 67c228448..38fd164d0 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Intl\Languages; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -39,6 +40,7 @@ class Language extends Constraint * @param bool|null $alpha3 Pass true to validate the language with three-letter code (ISO 639-2 (2T)) or false with two-letter code (ISO 639-1) (defaults to false) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -50,6 +52,10 @@ public function __construct( throw new LogicException('The Intl component is required to use the Language constraint. Try running "composer require symfony/intl".'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Length.php b/Constraints/Length.php index d1bc7b9dc..254487642 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Exception\MissingOptionsException; @@ -65,8 +66,9 @@ class Length extends Constraint * @param callable|null $normalizer A callable to normalize value before it is validated * @param self::COUNT_*|null $countUnit The character count unit for the length check (defaults to {@see Length::COUNT_CODEPOINTS}) * @param string[]|null $groups - * @param array $options + * @param array|null $options */ + #[HasNamedArguments] public function __construct( int|array|null $exactly = null, ?int $min = null, @@ -80,11 +82,17 @@ public function __construct( ?string $charsetMessage = null, ?array $groups = null, mixed $payload = null, - array $options = [], + ?array $options = null, ) { if (\is_array($exactly)) { - $options = array_merge($exactly, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($exactly, $options ?? []); $exactly = $options['value'] ?? null; + } elseif (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; } $min ??= $options['min'] ?? null; diff --git a/Constraints/Locale.php b/Constraints/Locale.php index fa31fbac4..739ce79ed 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\Intl\Locales; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -39,6 +40,7 @@ class Locale extends Constraint * @param bool|null $canonicalize Whether to canonicalize the value before validation (defaults to true) (see {@see https://www.php.net/manual/en/locale.canonicalize.php}) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -50,6 +52,10 @@ public function __construct( throw new LogicException('The Intl component is required to use the Locale constraint. Try running "composer require symfony/intl".'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index df26b283e..2ecb5edc7 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -39,12 +40,17 @@ class Luhn extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/NoSuspiciousCharacters.php b/Constraints/NoSuspiciousCharacters.php index 2dd6fb8f5..ea9ef8b6f 100644 --- a/Constraints/NoSuspiciousCharacters.php +++ b/Constraints/NoSuspiciousCharacters.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -89,6 +90,7 @@ class NoSuspiciousCharacters extends Constraint * @param string[]|null $locales Restrict the string's characters to those normally used with these locales. Pass null to use the default locales configured for the NoSuspiciousCharactersValidator. (defaults to null) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $restrictionLevelMessage = null, @@ -105,6 +107,10 @@ public function __construct( throw new LogicException('The intl extension is required to use the NoSuspiciousCharacters constraint.'); } + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->restrictionLevelMessage = $restrictionLevelMessage ?? $this->restrictionLevelMessage; diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index db3602615..18c8e533b 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -39,8 +40,13 @@ class NotBlank extends Constraint * @param bool|null $allowNull Whether to allow null values (defaults to false) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?bool $allowNull = null, ?callable $normalizer = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index d11df3ba6..fd0d5e185 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -37,6 +38,7 @@ class NotCompromisedPassword extends Constraint * @param bool|null $skipOnError Whether to ignore HTTP errors while requesting the API and thus consider the password valid (defaults to false) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -45,6 +47,10 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 32a327a57..4eb57c6c9 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,8 +34,13 @@ class NotNull extends Constraint * @param array|null $options * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/PasswordStrength.php b/Constraints/PasswordStrength.php index 42a93c530..7db3bb3a0 100644 --- a/Constraints/PasswordStrength.php +++ b/Constraints/PasswordStrength.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -43,8 +44,13 @@ final class PasswordStrength extends Constraint * @param self::STRENGTH_*|null $minScore The minimum required strength of the password (defaults to {@see PasswordStrength::STRENGTH_MEDIUM}) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(?array $options = null, ?int $minScore = null, ?array $groups = null, mixed $payload = null, ?string $message = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + $options['minScore'] ??= self::STRENGTH_MEDIUM; parent::__construct($options, $groups, $payload); diff --git a/Constraints/Range.php b/Constraints/Range.php index cf26d3573..038f3bb1e 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\LogicException; @@ -57,6 +58,7 @@ class Range extends Constraint * @param non-empty-string|null $maxPropertyPath Property path to the max value * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $notInRangeMessage = null, @@ -71,6 +73,10 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->notInRangeMessage = $notInRangeMessage ?? $this->notInRangeMessage; diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 4a2a90610..f3f1fb07e 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -40,8 +41,9 @@ class Regex extends Constraint * @param string|null $htmlPattern The pattern to use in the HTML5 pattern attribute * @param bool|null $match Whether to validate the value matches the configured pattern or not (defaults to false) * @param string[]|null $groups - * @param array $options + * @param array|null $options */ + #[HasNamedArguments] public function __construct( string|array|null $pattern, ?string $message = null, @@ -50,11 +52,19 @@ public function __construct( ?callable $normalizer = null, ?array $groups = null, mixed $payload = null, - array $options = [], + ?array $options = null, ) { if (\is_array($pattern)) { - $options = array_merge($pattern, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($pattern, $options ?? []); } elseif (null !== $pattern) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $pattern; } diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index d2b45e4bb..93ae0fcdd 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -30,6 +30,10 @@ class Sequentially extends Composite */ public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { + if (is_array($constraints) && !array_is_list($constraints)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($constraints ?? [], $groups, $payload); } diff --git a/Constraints/Time.php b/Constraints/Time.php index dca9537cf..9973f681d 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -37,6 +38,7 @@ class Time extends Constraint * @param string[]|null $groups * @param bool|null $withSeconds Whether to allow seconds in the given value (defaults to true) */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -44,6 +46,10 @@ public function __construct( mixed $payload = null, ?bool $withSeconds = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->withSeconds = $withSeconds ?? $this->withSeconds; diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index de10e2803..c92f412f8 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -45,10 +46,11 @@ class Timezone extends Constraint * @param string|null $countryCode Restrict the valid timezones to this country if the zone option is {@see \DateTimeZone::PER_COUNTRY} * @param bool|null $intlCompatible Whether to restrict valid timezones to ones available in PHP's intl (defaults to false) * @param string[]|null $groups - * @param array $options + * @param array|null $options * * @see \DateTimeZone */ + #[HasNamedArguments] public function __construct( int|array|null $zone = null, ?string $message = null, @@ -56,11 +58,19 @@ public function __construct( ?bool $intlCompatible = null, ?array $groups = null, mixed $payload = null, - array $options = [], + ?array $options = null, ) { if (\is_array($zone)) { - $options = array_merge($zone, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($zone, $options ?? []); } elseif (null !== $zone) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $zone; } diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index 80c7b2d31..98671586d 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -27,13 +28,18 @@ class Traverse extends Constraint /** * @param bool|array|null $traverse Whether to traverse the given object or not (defaults to true). Pass an associative array to configure the constraint's options (e.g. payload). */ - public function __construct(bool|array|null $traverse = null) + #[HasNamedArguments] + public function __construct(bool|array|null $traverse = null, mixed $payload = null) { if (\is_array($traverse) && \array_key_exists('groups', $traverse)) { throw new ConstraintDefinitionException(\sprintf('The option "groups" is not supported by the constraint "%s".', __CLASS__)); } - parent::__construct($traverse); + if (\is_array($traverse)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + + parent::__construct($traverse, null, $payload); } public function getDefaultOption(): ?string diff --git a/Constraints/Type.php b/Constraints/Type.php index 0482ff253..087c1a340 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -33,14 +34,25 @@ class Type extends Constraint /** * @param string|string[]|array|null $type The type(s) to enforce on the value * @param string[]|null $groups - * @param array $options + * @param array|null $options */ - public function __construct(string|array|null $type, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) + #[HasNamedArguments] + public function __construct(string|array|null $type, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($type) && \is_string(key($type))) { - $options = array_merge($type, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($type, $options ?? []); } elseif (null !== $type) { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['value'] = $type; + } elseif (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index b73757c13..28b5ef25e 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -50,6 +51,7 @@ class Ulid extends Constraint * @param string[]|null $groups * @param self::FORMAT_*|null $format */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -57,6 +59,10 @@ public function __construct( mixed $payload = null, ?string $format = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Unique.php b/Constraints/Unique.php index 6e68e2c3a..a407764ff 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -40,6 +41,7 @@ class Unique extends Constraint * @param string[]|null $groups * @param string[]|string|null $fields Defines the key or keys in the collection that should be checked for uniqueness (defaults to null, which ensure uniqueness for all keys) */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -49,6 +51,10 @@ public function __construct( array|string|null $fields = null, ?string $errorPath = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Url.php b/Constraints/Url.php index 7225a8be0..ed0733ae6 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -45,6 +46,7 @@ class Url extends Constraint * @param string[]|null $groups * @param bool|null $requireTld Whether to require the URL to include a top-level domain (defaults to false) */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -56,6 +58,10 @@ public function __construct( ?bool $requireTld = null, ?string $tldMessage = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); if (null === ($options['requireTld'] ?? $requireTld)) { diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index 2e65d1a00..5a4de6a25 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -100,6 +101,7 @@ class Uuid extends Constraint * @param bool|null $strict Whether to force the value to follow the RFC's input format rules; pass false to allow alternate formats (defaults to true) * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, @@ -109,6 +111,10 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; diff --git a/Constraints/Valid.php b/Constraints/Valid.php index f94d959a3..2a8eab1d4 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -28,8 +29,13 @@ class Valid extends Constraint * @param string[]|null $groups * @param bool|null $traverse Whether to validate {@see \Traversable} objects (defaults to true) */ + #[HasNamedArguments] public function __construct(?array $options = null, ?array $groups = null, $payload = null, ?bool $traverse = null) { + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options ?? [], $groups, $payload); $this->traverse = $traverse ?? $this->traverse; diff --git a/Constraints/When.php b/Constraints/When.php index 10b5aa3c7..1c3113e1b 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -13,6 +13,7 @@ use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; @@ -33,17 +34,26 @@ class When extends Composite * @param Constraint[]|Constraint|null $constraints One or multiple constraints that are applied if the expression returns true * @param array|null $values The values of the custom variables used in the expression (defaults to []) * @param string[]|null $groups - * @param array $options + * @param array|null $options */ - public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, array $options = []) + #[HasNamedArguments] + public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null) { if (!class_exists(ExpressionLanguage::class)) { throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); } if (\is_array($expression)) { - $options = array_merge($expression, $options); + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + + $options = array_merge($expression, $options ?? []); } else { + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } else { + $options = []; + } + $options['expression'] = $expression; $options['constraints'] = $constraints; } diff --git a/Constraints/ZeroComparisonConstraintTrait.php b/Constraints/ZeroComparisonConstraintTrait.php index 78fab1f54..b369a9429 100644 --- a/Constraints/ZeroComparisonConstraintTrait.php +++ b/Constraints/ZeroComparisonConstraintTrait.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** @@ -21,15 +22,18 @@ */ trait ZeroComparisonConstraintTrait { + #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - $options ??= []; + if (null !== $options) { + trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } - if (isset($options['propertyPath'])) { + if (is_array($options) && isset($options['propertyPath'])) { throw new ConstraintDefinitionException(\sprintf('The "propertyPath" option of the "%s" constraint cannot be set.', static::class)); } - if (isset($options['value'])) { + if (is_array($options) && isset($options['value'])) { throw new ConstraintDefinitionException(\sprintf('The "value" option of the "%s" constraint cannot be set.', static::class)); } diff --git a/Context/ExecutionContextInterface.php b/Context/ExecutionContextInterface.php index bd7ec5f96..56e39bd6a 100644 --- a/Context/ExecutionContextInterface.php +++ b/Context/ExecutionContextInterface.php @@ -95,7 +95,7 @@ public function buildViolation(string $message, array $parameters = []): Constra * { * $validator = $this->context->getValidator(); * - * $violations = $validator->validate($value, new Length(['min' => 3])); + * $violations = $validator->validate($value, new Length(min: 3)); * * if (count($violations) > 0) { * // ... diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index a74b53348..184bca894 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -97,9 +97,19 @@ protected function newConstraint(string $name, mixed $options = null): Constrain return new $className($options['value']); } + if (array_is_list($options)) { + return new $className($options); + } + return new $className(...$options); } - return new $className($options); + if ($options) { + trigger_deprecation('symfony/validator', '7.2', 'Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to %s.', $className); + + return new $className($options); + } + + return new $className(); } } diff --git a/Mapping/Loader/PropertyInfoLoader.php b/Mapping/Loader/PropertyInfoLoader.php index 444e864f2..57d65696e 100644 --- a/Mapping/Loader/PropertyInfoLoader.php +++ b/Mapping/Loader/PropertyInfoLoader.php @@ -134,7 +134,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool $metadata->addPropertyConstraint($property, $this->getTypeConstraintLegacy($builtinTypes[0], $types[0])); } elseif ($scalar) { - $metadata->addPropertyConstraint($property, new Type(['type' => 'scalar'])); + $metadata->addPropertyConstraint($property, new Type(type: 'scalar')); } } } else { @@ -203,10 +203,10 @@ private function getPropertyTypes(string $className, string $property): TypeInfo private function getTypeConstraintLegacy(string $builtinType, PropertyInfoType $type): Type { if (PropertyInfoType::BUILTIN_TYPE_OBJECT === $builtinType && null !== $className = $type->getClassName()) { - return new Type(['type' => $className]); + return new Type(type: $className); } - return new Type(['type' => $builtinType]); + return new Type(type: $builtinType); } private function getTypeConstraint(TypeInfoType $type): ?Type @@ -220,11 +220,11 @@ private function getTypeConstraint(TypeInfoType $type): ?Type $baseType = $type->getBaseType(); if ($baseType instanceof ObjectType) { - return new Type(['type' => $baseType->getClassName()]); + return new Type(type: $baseType->getClassName()); } if (TypeIdentifier::MIXED !== $baseType->getTypeIdentifier()) { - return new Type(['type' => $baseType->getTypeIdentifier()->value]); + return new Type(type: $baseType->getTypeIdentifier()->value); } return null; @@ -238,7 +238,7 @@ private function getTypeConstraint(TypeInfoType $type): ?Type TypeIdentifier::BOOL, TypeIdentifier::TRUE, TypeIdentifier::FALSE, - ) ? new Type(['type' => 'scalar']) : null; + ) ? new Type(type: 'scalar') : null; } while ($type instanceof WrappingTypeInterface) { @@ -246,11 +246,11 @@ private function getTypeConstraint(TypeInfoType $type): ?Type } if ($type instanceof ObjectType) { - return new Type(['type' => $type->getClassName()]); + return new Type(type: $type->getClassName()); } if ($type instanceof BuiltinType && TypeIdentifier::MIXED !== $type->getTypeIdentifier()) { - return new Type(['type' => $type->getTypeIdentifier()->value]); + return new Type(type: $type->getTypeIdentifier()->value); } return null; @@ -284,7 +284,7 @@ private function handleAllConstraint(string $property, ?All $allConstraint, Type } if (null === $allConstraint) { - $metadata->addPropertyConstraint($property, new All(['constraints' => $constraints])); + $metadata->addPropertyConstraint($property, new All(constraints: $constraints)); } else { $allConstraint->constraints = array_merge($allConstraint->constraints, $constraints); } @@ -318,7 +318,7 @@ private function handleAllConstraintLegacy(string $property, ?All $allConstraint } if (null === $allConstraint) { - $metadata->addPropertyConstraint($property, new All(['constraints' => $constraints])); + $metadata->addPropertyConstraint($property, new All(constraints: $constraints)); } else { $allConstraint->constraints = array_merge($allConstraint->constraints, $constraints); } diff --git a/Tests/Constraints/AllValidatorTest.php b/Tests/Constraints/AllValidatorTest.php index 65dae6275..ee6a29174 100644 --- a/Tests/Constraints/AllValidatorTest.php +++ b/Tests/Constraints/AllValidatorTest.php @@ -27,7 +27,7 @@ protected function createValidator(): AllValidator public function testNullIsValid() { - $this->validator->validate(null, new All(new Range(['min' => 4]))); + $this->validator->validate(null, new All(new Range(min: 4))); $this->assertNoViolation(); } @@ -35,7 +35,7 @@ public function testNullIsValid() public function testThrowsExceptionIfNotTraversable() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate('foo.barbar', new All(new Range(['min' => 4]))); + $this->validator->validate('foo.barbar', new All(new Range(min: 4))); } /** @@ -43,7 +43,7 @@ public function testThrowsExceptionIfNotTraversable() */ public function testWalkSingleConstraint($array) { - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $i = 0; @@ -61,7 +61,7 @@ public function testWalkSingleConstraint($array) */ public function testWalkMultipleConstraints($array) { - $constraint1 = new Range(['min' => 4]); + $constraint1 = new Range(min: 4); $constraint2 = new NotNull(); $constraints = [$constraint1, $constraint2]; diff --git a/Tests/Constraints/AtLeastOneOfValidatorTest.php b/Tests/Constraints/AtLeastOneOfValidatorTest.php index 8bda680b2..22b53dd13 100644 --- a/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -66,31 +66,31 @@ public static function getValidCombinations() { return [ ['symfony', [ - new Length(['min' => 10]), - new EqualTo(['value' => 'symfony']), + new Length(min: 10), + new EqualTo(value: 'symfony'), ]], [150, [ - new Range(['min' => 10, 'max' => 20]), - new GreaterThanOrEqual(['value' => 100]), + new Range(min: 10, max: 20), + new GreaterThanOrEqual(value: 100), ]], [7, [ - new LessThan(['value' => 5]), - new IdenticalTo(['value' => 7]), + new LessThan(value: 5), + new IdenticalTo(value: 7), ]], [-3, [ - new DivisibleBy(['value' => 4]), + new DivisibleBy(value: 4), new Negative(), ]], ['FOO', [ - new Choice(['choices' => ['bar', 'BAR']]), - new Regex(['pattern' => '/foo/i']), + new Choice(choices: ['bar', 'BAR']), + new Regex(pattern: '/foo/i'), ]], ['fr', [ new Country(), new Language(), ]], [[1, 3, 5], [ - new Count(['min' => 5]), + new Count(min: 5), new Unique(), ]], ]; @@ -101,7 +101,7 @@ public static function getValidCombinations() */ public function testInvalidCombinationsWithDefaultMessage($value, $constraints) { - $atLeastOneOf = new AtLeastOneOf(['constraints' => $constraints]); + $atLeastOneOf = new AtLeastOneOf(constraints: $constraints); $validator = Validation::createValidator(); $message = [$atLeastOneOf->message]; @@ -123,7 +123,11 @@ public function testInvalidCombinationsWithDefaultMessage($value, $constraints) */ public function testInvalidCombinationsWithCustomMessage($value, $constraints) { - $atLeastOneOf = new AtLeastOneOf(['constraints' => $constraints, 'message' => 'foo', 'includeInternalMessages' => false]); + $atLeastOneOf = new AtLeastOneOf( + constraints: $constraints, + message: 'foo', + includeInternalMessages: false, + ); $violations = Validation::createValidator()->validate($value, $atLeastOneOf); @@ -135,31 +139,31 @@ public static function getInvalidCombinations() { return [ ['symphony', [ - new Length(['min' => 10]), - new EqualTo(['value' => 'symfony']), + new Length(min: 10), + new EqualTo(value: 'symfony'), ]], [70, [ - new Range(['min' => 10, 'max' => 20]), - new GreaterThanOrEqual(['value' => 100]), + new Range(min: 10, max: 20), + new GreaterThanOrEqual(value: 100), ]], [8, [ - new LessThan(['value' => 5]), - new IdenticalTo(['value' => 7]), + new LessThan(value: 5), + new IdenticalTo(value: 7), ]], [3, [ - new DivisibleBy(['value' => 4]), + new DivisibleBy(value: 4), new Negative(), ]], ['F_O_O', [ - new Choice(['choices' => ['bar', 'BAR']]), - new Regex(['pattern' => '/foo/i']), + new Choice(choices: ['bar', 'BAR']), + new Regex(pattern: '/foo/i'), ]], ['f_r', [ new Country(), new Language(), ]], [[1, 3, 3], [ - new Count(['min' => 5]), + new Count(min: 5), new Unique(), ]], ]; @@ -169,21 +173,21 @@ public function testGroupsArePropagatedToNestedConstraints() { $validator = Validation::createValidator(); - $violations = $validator->validate(50, new AtLeastOneOf([ - 'constraints' => [ - new Range([ - 'groups' => 'non_default_group', - 'min' => 10, - 'max' => 20, - ]), - new Range([ - 'groups' => 'non_default_group', - 'min' => 30, - 'max' => 40, - ]), + $violations = $validator->validate(50, new AtLeastOneOf( + constraints: [ + new Range( + groups: ['non_default_group'], + min: 10, + max: 20, + ), + new Range( + groups: ['non_default_group'], + min: 30, + max: 40, + ), ], - 'groups' => 'non_default_group', - ]), 'non_default_group'); + groups: ['non_default_group'], + ), ['non_default_group']); $this->assertCount(1, $violations); } @@ -221,9 +225,9 @@ public function testEmbeddedMessageTakenFromFailingConstraint() public function getMetadataFor($classOrObject): MetadataInterface { return (new ClassMetadata(Data::class)) - ->addPropertyConstraint('foo', new NotNull(['message' => 'custom message foo'])) + ->addPropertyConstraint('foo', new NotNull(message: 'custom message foo')) ->addPropertyConstraint('bar', new AtLeastOneOf([ - new NotNull(['message' => 'custom message bar']), + new NotNull(message: 'custom message bar'), ])) ; } @@ -247,20 +251,20 @@ public function testNestedConstraintsAreNotExecutedWhenGroupDoesNotMatch() { $validator = Validation::createValidator(); - $violations = $validator->validate(50, new AtLeastOneOf([ - 'constraints' => [ - new Range([ - 'groups' => 'adult', - 'min' => 18, - 'max' => 55, - ]), - new GreaterThan([ - 'groups' => 'senior', - 'value' => 55, - ]), + $violations = $validator->validate(50, new AtLeastOneOf( + constraints: [ + new Range( + groups: ['adult'], + min: 18, + max: 55, + ), + new GreaterThan( + groups: ['senior'], + value: 55, + ), ], - 'groups' => ['adult', 'senior'], - ]), 'senior'); + groups: ['adult', 'senior'], + ), 'senior'); $this->assertCount(1, $violations); } diff --git a/Tests/Constraints/BicValidatorTest.php b/Tests/Constraints/BicValidatorTest.php index 348613d00..315cb859e 100644 --- a/Tests/Constraints/BicValidatorTest.php +++ b/Tests/Constraints/BicValidatorTest.php @@ -44,7 +44,7 @@ public function testEmptyStringIsValid() public function testValidComparisonToPropertyPath() { - $constraint = new Bic(['ibanPropertyPath' => 'value']); + $constraint = new Bic(ibanPropertyPath: 'value'); $object = new BicComparisonTestClass('FR14 2004 1010 0505 0001 3M02 606'); @@ -57,7 +57,7 @@ public function testValidComparisonToPropertyPath() public function testInvalidComparisonToPropertyPath() { - $constraint = new Bic(['ibanPropertyPath' => 'value']); + $constraint = new Bic(ibanPropertyPath: 'value'); $constraint->ibanMessage = 'Constraint Message'; $object = new BicComparisonTestClass('FR14 2004 1010 0505 0001 3M02 606'); @@ -95,14 +95,14 @@ public function testPropertyPathReferencingUninitializedProperty() { $this->setObject(new BicTypedDummy()); - $this->validator->validate('UNCRIT2B912', new Bic(['ibanPropertyPath' => 'iban'])); + $this->validator->validate('UNCRIT2B912', new Bic(ibanPropertyPath: 'iban')); $this->assertNoViolation(); } public function testValidComparisonToValue() { - $constraint = new Bic(['iban' => 'FR14 2004 1010 0505 0001 3M02 606']); + $constraint = new Bic(iban: 'FR14 2004 1010 0505 0001 3M02 606'); $constraint->ibanMessage = 'Constraint Message'; $this->validator->validate('SOGEFRPP', $constraint); @@ -112,7 +112,7 @@ public function testValidComparisonToValue() public function testInvalidComparisonToValue() { - $constraint = new Bic(['iban' => 'FR14 2004 1010 0505 0001 3M02 606']); + $constraint = new Bic(iban: 'FR14 2004 1010 0505 0001 3M02 606'); $constraint->ibanMessage = 'Constraint Message'; $this->validator->validate('UNCRIT2B912', $constraint); @@ -142,7 +142,7 @@ public function testInvalidComparisonToValueFromAttribute() public function testNoViolationOnNullObjectWithPropertyPath() { - $constraint = new Bic(['ibanPropertyPath' => 'propertyPath']); + $constraint = new Bic(ibanPropertyPath: 'propertyPath'); $this->setObject(null); @@ -155,10 +155,10 @@ public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() { $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time'); - new Bic([ - 'iban' => 'value', - 'ibanPropertyPath' => 'propertyPath', - ]); + new Bic( + iban: 'value', + ibanPropertyPath: 'propertyPath', + ); } public function testThrowsConstraintExceptionIfBothValueAndPropertyPathNamed() @@ -171,7 +171,7 @@ public function testThrowsConstraintExceptionIfBothValueAndPropertyPathNamed() public function testInvalidValuePath() { - $constraint = new Bic(['ibanPropertyPath' => 'foo']); + $constraint = new Bic(ibanPropertyPath: 'foo'); $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage(\sprintf('Invalid property path "foo" provided to "%s" constraint', $constraint::class)); @@ -217,9 +217,9 @@ public static function getValidBics() */ public function testInvalidBics($bic, $code) { - $constraint = new Bic([ - 'message' => 'myMessage', - ]); + $constraint = new Bic( + message: 'myMessage', + ); $this->validator->validate($bic, $constraint); @@ -277,7 +277,7 @@ public static function getInvalidBics() */ public function testValidBicSpecialCases(string $bic, string $iban) { - $constraint = new Bic(['iban' => $iban]); + $constraint = new Bic(iban: $iban); $this->validator->validate($bic, $constraint); $this->assertNoViolation(); diff --git a/Tests/Constraints/BlankValidatorTest.php b/Tests/Constraints/BlankValidatorTest.php index 9643c6793..21d3fc83e 100644 --- a/Tests/Constraints/BlankValidatorTest.php +++ b/Tests/Constraints/BlankValidatorTest.php @@ -41,9 +41,9 @@ public function testBlankIsValid() */ public function testInvalidValues($value, $valueAsString) { - $constraint = new Blank([ - 'message' => 'myMessage', - ]); + $constraint = new Blank( + message: 'myMessage', + ); $this->validator->validate($value, $constraint); diff --git a/Tests/Constraints/CallbackValidatorTest.php b/Tests/Constraints/CallbackValidatorTest.php index ef92d3072..7fbcd2714 100644 --- a/Tests/Constraints/CallbackValidatorTest.php +++ b/Tests/Constraints/CallbackValidatorTest.php @@ -74,7 +74,7 @@ public function testSingleMethod() public function testSingleMethodExplicitName() { $object = new CallbackValidatorTest_Object(); - $constraint = new Callback(['callback' => 'validate']); + $constraint = new Callback(callback: 'validate'); $this->validator->validate($object, $constraint); @@ -129,13 +129,11 @@ public function testClosureNullObject() public function testClosureExplicitName() { $object = new CallbackValidatorTest_Object(); - $constraint = new Callback([ - 'callback' => function ($object, ExecutionContextInterface $context) { - $context->addViolation('My message', ['{{ value }}' => 'foobar']); + $constraint = new Callback(callback: function ($object, ExecutionContextInterface $context) { + $context->addViolation('My message', ['{{ value }}' => 'foobar']); - return false; - }, - ]); + return false; + }); $this->validator->validate($object, $constraint); @@ -170,9 +168,7 @@ public function testArrayCallableNullObject() public function testArrayCallableExplicitName() { $object = new CallbackValidatorTest_Object(); - $constraint = new Callback([ - 'callback' => [__CLASS__.'_Class', 'validateCallback'], - ]); + $constraint = new Callback(callback: [__CLASS__.'_Class', 'validateCallback']); $this->validator->validate($object, $constraint); @@ -186,7 +182,7 @@ public function testExpectValidMethods() $this->expectException(ConstraintDefinitionException::class); $object = new CallbackValidatorTest_Object(); - $this->validator->validate($object, new Callback(['callback' => ['foobar']])); + $this->validator->validate($object, new Callback(callback: ['foobar'])); } public function testExpectValidCallbacks() @@ -194,12 +190,12 @@ public function testExpectValidCallbacks() $this->expectException(ConstraintDefinitionException::class); $object = new CallbackValidatorTest_Object(); - $this->validator->validate($object, new Callback(['callback' => ['foo', 'bar']])); + $this->validator->validate($object, new Callback(callback: ['foo', 'bar'])); } public function testConstraintGetTargets() { - $constraint = new Callback([]); + $constraint = new Callback(callback: []); $targets = [Constraint::CLASS_CONSTRAINT, Constraint::PROPERTY_CONSTRAINT]; $this->assertEquals($targets, $constraint->getTargets()); @@ -215,16 +211,16 @@ public function testNoConstructorArguments() public function testAttributeInvocationSingleValued() { - $constraint = new Callback(['value' => 'validateStatic']); + $constraint = new Callback(callback: 'validateStatic'); - $this->assertEquals(new Callback('validateStatic'), $constraint); + $this->assertEquals(new Callback(callback: 'validateStatic'), $constraint); } public function testAttributeInvocationMultiValued() { - $constraint = new Callback(['value' => [__CLASS__.'_Class', 'validateCallback']]); + $constraint = new Callback(callback: [__CLASS__.'_Class', 'validateCallback']); - $this->assertEquals(new Callback([__CLASS__.'_Class', 'validateCallback']), $constraint); + $this->assertEquals(new Callback(callback: [__CLASS__.'_Class', 'validateCallback']), $constraint); } public function testPayloadIsPassedToCallback() @@ -235,10 +231,10 @@ public function testPayloadIsPassedToCallback() $payloadCopy = $payload; }; - $constraint = new Callback([ - 'callback' => $callback, - 'payload' => 'Hello world!', - ]); + $constraint = new Callback( + callback: $callback, + payload: 'Hello world!', + ); $this->validator->validate($object, $constraint); $this->assertEquals('Hello world!', $payloadCopy); @@ -248,9 +244,7 @@ public function testPayloadIsPassedToCallback() $this->assertEquals('Hello world!', $payloadCopy); $payloadCopy = 'Replace me!'; - $constraint = new Callback([ - 'callback' => $callback, - ]); + $constraint = new Callback(callback: $callback); $this->validator->validate($object, $constraint); $this->assertNull($payloadCopy); } diff --git a/Tests/Constraints/CardSchemeValidatorTest.php b/Tests/Constraints/CardSchemeValidatorTest.php index 15f4fa634..87b1daebc 100644 --- a/Tests/Constraints/CardSchemeValidatorTest.php +++ b/Tests/Constraints/CardSchemeValidatorTest.php @@ -24,14 +24,14 @@ protected function createValidator(): CardSchemeValidator public function testNullIsValid() { - $this->validator->validate(null, new CardScheme(['schemes' => []])); + $this->validator->validate(null, new CardScheme(schemes: [])); $this->assertNoViolation(); } public function testEmptyStringIsValid() { - $this->validator->validate('', new CardScheme(['schemes' => []])); + $this->validator->validate('', new CardScheme(schemes:[])); $this->assertNoViolation(); } @@ -41,7 +41,7 @@ public function testEmptyStringIsValid() */ public function testValidNumbers($scheme, $number) { - $this->validator->validate($number, new CardScheme(['schemes' => $scheme])); + $this->validator->validate($number, new CardScheme(schemes: $scheme)); $this->assertNoViolation(); } @@ -51,7 +51,7 @@ public function testValidNumbers($scheme, $number) */ public function testValidNumbersWithNewLine($scheme, $number) { - $this->validator->validate($number."\n", new CardScheme(['schemes' => $scheme, 'message' => 'myMessage'])); + $this->validator->validate($number."\n", new CardScheme(schemes: $scheme, message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$number."\n\"") @@ -74,10 +74,10 @@ public function testValidNumberWithOrderedArguments() */ public function testInvalidNumbers($scheme, $number, $code) { - $constraint = new CardScheme([ - 'schemes' => $scheme, - 'message' => 'myMessage', - ]); + $constraint = new CardScheme( + schemes: $scheme, + message: 'myMessage', + ); $this->validator->validate($number, $constraint); diff --git a/Tests/Constraints/ChoiceValidatorTest.php b/Tests/Constraints/ChoiceValidatorTest.php index a78a2bfa5..a219e44d8 100644 --- a/Tests/Constraints/ChoiceValidatorTest.php +++ b/Tests/Constraints/ChoiceValidatorTest.php @@ -47,22 +47,17 @@ public static function staticCallbackInvalid() public function testExpectArrayIfMultipleIsTrue() { $this->expectException(UnexpectedValueException::class); - $constraint = new Choice([ - 'choices' => ['foo', 'bar'], - 'multiple' => true, - ]); + $constraint = new Choice( + choices: ['foo', 'bar'], + multiple: true, + ); $this->validator->validate('asdf', $constraint); } public function testNullIsValid() { - $this->validator->validate( - null, - new Choice([ - 'choices' => ['foo', 'bar'], - ]) - ); + $this->validator->validate(null, new Choice(choices: ['foo', 'bar'])); $this->assertNoViolation(); } @@ -76,7 +71,7 @@ public function testChoicesOrCallbackExpected() public function testValidCallbackExpected() { $this->expectException(ConstraintDefinitionException::class); - $this->validator->validate('foobar', new Choice(['callback' => 'abcd'])); + $this->validator->validate('foobar', new Choice(callback: 'abcd')); } /** @@ -91,12 +86,27 @@ public function testValidChoiceArray(Choice $constraint) public static function provideConstraintsWithChoicesArray(): iterable { - yield 'Doctrine style' => [new Choice(['choices' => ['foo', 'bar']])]; - yield 'Doctrine default option' => [new Choice(['value' => ['foo', 'bar']])]; yield 'first argument' => [new Choice(['foo', 'bar'])]; yield 'named arguments' => [new Choice(choices: ['foo', 'bar'])]; } + /** + * @group legacy + * @dataProvider provideLegacyConstraintsWithChoicesArrayDoctrineStyle + */ + public function testValidChoiceArrayDoctrineStyle(Choice $constraint) + { + $this->validator->validate('bar', $constraint); + + $this->assertNoViolation(); + } + + public static function provideLegacyConstraintsWithChoicesArrayDoctrineStyle(): iterable + { + yield 'Doctrine style' => [new Choice(['choices' => ['foo', 'bar']])]; + yield 'Doctrine default option' => [new Choice(['value' => ['foo', 'bar']])]; + } + /** * @dataProvider provideConstraintsWithCallbackFunction */ @@ -108,15 +118,30 @@ public function testValidChoiceCallbackFunction(Choice $constraint) } public static function provideConstraintsWithCallbackFunction(): iterable + { + yield 'named arguments, namespaced function' => [new Choice(callback: __NAMESPACE__.'\choice_callback')]; + yield 'named arguments, closure' => [new Choice(callback: fn () => ['foo', 'bar'])]; + yield 'named arguments, static method' => [new Choice(callback: [__CLASS__, 'staticCallback'])]; + } + + /** + * @group legacy + * @dataProvider provideLegacyConstraintsWithCallbackFunctionDoctrineStyle + */ + public function testValidChoiceCallbackFunctionDoctrineStyle(Choice $constraint) + { + $this->validator->validate('bar', $constraint); + + $this->assertNoViolation(); + } + + public static function provideLegacyConstraintsWithCallbackFunctionDoctrineStyle(): iterable { yield 'doctrine style, namespaced function' => [new Choice(['callback' => __NAMESPACE__.'\choice_callback'])]; yield 'doctrine style, closure' => [new Choice([ 'callback' => fn () => ['foo', 'bar'], ])]; yield 'doctrine style, static method' => [new Choice(['callback' => [__CLASS__, 'staticCallback']])]; - yield 'named arguments, namespaced function' => [new Choice(callback: __NAMESPACE__.'\choice_callback')]; - yield 'named arguments, closure' => [new Choice(callback: fn () => ['foo', 'bar'])]; - yield 'named arguments, static method' => [new Choice(callback: [__CLASS__, 'staticCallback'])]; } public function testValidChoiceCallbackContextMethod() @@ -124,7 +149,7 @@ public function testValidChoiceCallbackContextMethod() // search $this for "staticCallback" $this->setObject($this); - $constraint = new Choice(['callback' => 'staticCallback']); + $constraint = new Choice(callback: 'staticCallback'); $this->validator->validate('bar', $constraint); @@ -139,7 +164,7 @@ public function testInvalidChoiceCallbackContextMethod() // search $this for "staticCallbackInvalid" $this->setObject($this); - $constraint = new Choice(['callback' => 'staticCallbackInvalid']); + $constraint = new Choice(callback: 'staticCallbackInvalid'); $this->validator->validate('bar', $constraint); } @@ -149,41 +174,39 @@ public function testValidChoiceCallbackContextObjectMethod() // search $this for "objectMethodCallback" $this->setObject($this); - $constraint = new Choice(['callback' => 'objectMethodCallback']); + $constraint = new Choice(callback: 'objectMethodCallback'); $this->validator->validate('bar', $constraint); $this->assertNoViolation(); } - /** - * @dataProvider provideConstraintsWithMultipleTrue - */ - public function testMultipleChoices(Choice $constraint) + public function testMultipleChoices() { - $this->validator->validate(['baz', 'bar'], $constraint); + $this->validator->validate(['baz', 'bar'], new Choice( + choices: ['foo', 'bar', 'baz'], + multiple: true, + )); $this->assertNoViolation(); } - public static function provideConstraintsWithMultipleTrue(): iterable + /** + * @group legacy + */ + public function testMultipleChoicesDoctrineStyle() { - yield 'Doctrine style' => [new Choice([ + $this->validator->validate(['baz', 'bar'], new Choice([ 'choices' => ['foo', 'bar', 'baz'], 'multiple' => true, - ])]; - yield 'named arguments' => [new Choice( - choices: ['foo', 'bar', 'baz'], - multiple: true, - )]; + ])); + + $this->assertNoViolation(); } - /** - * @dataProvider provideConstraintsWithMessage - */ - public function testInvalidChoice(Choice $constraint) + public function testInvalidChoice() { - $this->validator->validate('baz', $constraint); + $this->validator->validate('baz', new Choice(choices: ['foo', 'bar'], message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"baz"') @@ -192,20 +215,28 @@ public function testInvalidChoice(Choice $constraint) ->assertRaised(); } - public static function provideConstraintsWithMessage(): iterable + /** + * @group legacy + */ + public function testInvalidChoiceDoctrineStyle() { - yield 'Doctrine style' => [new Choice(['choices' => ['foo', 'bar'], 'message' => 'myMessage'])]; - yield 'named arguments' => [new Choice(choices: ['foo', 'bar'], message: 'myMessage')]; + $this->validator->validate('baz', new Choice(['choices' => ['foo', 'bar'], 'message' => 'myMessage'])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"baz"') + ->setParameter('{{ choices }}', '"foo", "bar"') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) + ->assertRaised(); } public function testInvalidChoiceEmptyChoices() { - $constraint = new Choice([ + $constraint = new Choice( // May happen when the choices are provided dynamically, e.g. from // the DB or the model - 'choices' => [], - 'message' => 'myMessage', - ]); + choices: [], + message: 'myMessage', + ); $this->validator->validate('baz', $constraint); @@ -216,12 +247,13 @@ public function testInvalidChoiceEmptyChoices() ->assertRaised(); } - /** - * @dataProvider provideConstraintsWithMultipleMessage - */ - public function testInvalidChoiceMultiple(Choice $constraint) + public function testInvalidChoiceMultiple() { - $this->validator->validate(['foo', 'baz'], $constraint); + $this->validator->validate(['foo', 'baz'], new Choice( + choices: ['foo', 'bar'], + multipleMessage: 'myMessage', + multiple: true, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"baz"') @@ -230,31 +262,37 @@ public function testInvalidChoiceMultiple(Choice $constraint) ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } - - public static function provideConstraintsWithMultipleMessage(): iterable + /** + * @group legacy + */ + public function testInvalidChoiceMultipleDoctrineStyle() { - yield 'Doctrine style' => [new Choice([ + $this->validator->validate(['foo', 'baz'], new Choice([ 'choices' => ['foo', 'bar'], 'multipleMessage' => 'myMessage', 'multiple' => true, - ])]; - yield 'named arguments' => [new Choice( - choices: ['foo', 'bar'], - multipleMessage: 'myMessage', - multiple: true, - )]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"baz"') + ->setParameter('{{ choices }}', '"foo", "bar"') + ->setInvalidValue('baz') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideConstraintsWithMin - */ - public function testTooFewChoices(Choice $constraint) + public function testTooFewChoices() { $value = ['foo']; $this->setValue($value); - $this->validator->validate($value, $constraint); + $this->validator->validate($value, new Choice( + choices: ['foo', 'bar', 'moo', 'maa'], + multiple: true, + min: 2, + minMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ limit }}', 2) @@ -264,32 +302,42 @@ public function testTooFewChoices(Choice $constraint) ->assertRaised(); } - public static function provideConstraintsWithMin(): iterable + /** + * @group legacy + */ + public function testTooFewChoicesDoctrineStyle() { - yield 'Doctrine style' => [new Choice([ + $value = ['foo']; + + $this->setValue($value); + + $this->validator->validate($value, new Choice([ 'choices' => ['foo', 'bar', 'moo', 'maa'], 'multiple' => true, 'min' => 2, 'minMessage' => 'myMessage', - ])]; - yield 'named arguments' => [new Choice( - choices: ['foo', 'bar', 'moo', 'maa'], - multiple: true, - min: 2, - minMessage: 'myMessage', - )]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ limit }}', 2) + ->setInvalidValue($value) + ->setPlural(2) + ->setCode(Choice::TOO_FEW_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideConstraintsWithMax - */ - public function testTooManyChoices(Choice $constraint) + public function testTooManyChoices() { $value = ['foo', 'bar', 'moo']; $this->setValue($value); - $this->validator->validate($value, $constraint); + $this->validator->validate($value, new Choice( + choices: ['foo', 'bar', 'moo', 'maa'], + multiple: true, + max: 2, + maxMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ limit }}', 2) @@ -299,27 +347,33 @@ public function testTooManyChoices(Choice $constraint) ->assertRaised(); } - public static function provideConstraintsWithMax(): iterable + /** + * @group legacy + */ + public function testTooManyChoicesDoctrineStyle() { - yield 'Doctrine style' => [new Choice([ + $value = ['foo', 'bar', 'moo']; + + $this->setValue($value); + + $this->validator->validate($value, new Choice([ 'choices' => ['foo', 'bar', 'moo', 'maa'], 'multiple' => true, 'max' => 2, 'maxMessage' => 'myMessage', - ])]; - yield 'named arguments' => [new Choice( - choices: ['foo', 'bar', 'moo', 'maa'], - multiple: true, - max: 2, - maxMessage: 'myMessage', - )]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ limit }}', 2) + ->setInvalidValue($value) + ->setPlural(2) + ->setCode(Choice::TOO_MANY_ERROR) + ->assertRaised(); } public function testStrictAllowsExactValue() { - $constraint = new Choice([ - 'choices' => [1, 2], - ]); + $constraint = new Choice(choices: [1, 2]); $this->validator->validate(2, $constraint); @@ -328,10 +382,10 @@ public function testStrictAllowsExactValue() public function testStrictDisallowsDifferentType() { - $constraint = new Choice([ - 'choices' => [1, 2], - 'message' => 'myMessage', - ]); + $constraint = new Choice( + choices: [1, 2], + message: 'myMessage', + ); $this->validator->validate('2', $constraint); @@ -344,11 +398,11 @@ public function testStrictDisallowsDifferentType() public function testStrictWithMultipleChoices() { - $constraint = new Choice([ - 'choices' => [1, 2, 3], - 'multiple' => true, - 'multipleMessage' => 'myMessage', - ]); + $constraint = new Choice( + choices: [1, 2, 3], + multiple: true, + multipleMessage: 'myMessage', + ); $this->validator->validate([2, '3'], $constraint); @@ -362,10 +416,10 @@ public function testStrictWithMultipleChoices() public function testMatchFalse() { - $this->validator->validate('foo', new Choice([ - 'choices' => ['foo', 'bar'], - 'match' => false, - ])); + $this->validator->validate('foo', new Choice( + choices: ['foo', 'bar'], + match: false, + )); $this->buildViolation('The value you selected is not a valid choice.') ->setParameter('{{ value }}', '"foo"') @@ -376,11 +430,11 @@ public function testMatchFalse() public function testMatchFalseWithMultiple() { - $this->validator->validate(['ccc', 'bar', 'zzz'], new Choice([ - 'choices' => ['foo', 'bar'], - 'multiple' => true, - 'match' => false, - ])); + $this->validator->validate(['ccc', 'bar', 'zzz'], new Choice( + choices: ['foo', 'bar'], + multiple: true, + match: false, + )); $this->buildViolation('One or more of the given values is invalid.') ->setParameter('{{ value }}', '"bar"') diff --git a/Tests/Constraints/CidrTest.php b/Tests/Constraints/CidrTest.php index 142783ec0..25059104d 100644 --- a/Tests/Constraints/CidrTest.php +++ b/Tests/Constraints/CidrTest.php @@ -31,7 +31,7 @@ public function testForAll() public function testForV4() { - $cidrConstraint = new Cidr(['version' => Ip::V4]); + $cidrConstraint = new Cidr(version: Ip::V4); self::assertEquals(Ip::V4, $cidrConstraint->version); self::assertEquals(0, $cidrConstraint->netmaskMin); @@ -40,7 +40,7 @@ public function testForV4() public function testForV6() { - $cidrConstraint = new Cidr(['version' => Ip::V6]); + $cidrConstraint = new Cidr(version: Ip::V6); self::assertEquals(Ip::V6, $cidrConstraint->version); self::assertEquals(0, $cidrConstraint->netmaskMin); @@ -62,7 +62,7 @@ public function testWithInvalidVersion() self::expectException(ConstraintDefinitionException::class); self::expectExceptionMessage(\sprintf('The option "version" must be one of "%s".', implode('", "', $availableVersions))); - new Cidr(['version' => '8']); + new Cidr(version: '8'); } /** @@ -70,11 +70,11 @@ public function testWithInvalidVersion() */ public function testWithValidMinMaxValues(string $ipVersion, int $netmaskMin, int $netmaskMax) { - $cidrConstraint = new Cidr([ - 'version' => $ipVersion, - 'netmaskMin' => $netmaskMin, - 'netmaskMax' => $netmaskMax, - ]); + $cidrConstraint = new Cidr( + version: $ipVersion, + netmaskMin: $netmaskMin, + netmaskMax: $netmaskMax, + ); self::assertEquals($ipVersion, $cidrConstraint->version); self::assertEquals($netmaskMin, $cidrConstraint->netmaskMin); @@ -91,11 +91,11 @@ public function testWithInvalidMinMaxValues(string $ipVersion, int $netmaskMin, self::expectException(ConstraintDefinitionException::class); self::expectExceptionMessage(\sprintf('The netmask range must be between 0 and %d.', $expectedMax)); - new Cidr([ - 'version' => $ipVersion, - 'netmaskMin' => $netmaskMin, - 'netmaskMax' => $netmaskMax, - ]); + new Cidr( + version: $ipVersion, + netmaskMin: $netmaskMin, + netmaskMax: $netmaskMax, + ); } public static function getInvalidMinMaxValues(): array diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 04d0b8999..6dfdc4931 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -86,7 +86,7 @@ public function testInvalidIpValue(string $cidr) */ public function testValidCidr(string|\Stringable $cidr, string $version) { - $this->validator->validate($cidr, new Cidr(['version' => $version])); + $this->validator->validate($cidr, new Cidr(version: $version)); $this->assertNoViolation(); } @@ -108,11 +108,11 @@ public function testInvalidIpAddressAndNetmask(string|\Stringable $cidr) */ public function testOutOfRangeNetmask(string $cidr, int $maxExpected, ?string $version = null, ?int $min = null, ?int $max = null) { - $cidrConstraint = new Cidr([ - 'version' => $version, - 'netmaskMin' => $min, - 'netmaskMax' => $max, - ]); + $cidrConstraint = new Cidr( + version: $version, + netmaskMin: $min, + netmaskMax: $max, + ); $this->validator->validate($cidr, $cidrConstraint); $this @@ -128,7 +128,7 @@ public function testOutOfRangeNetmask(string $cidr, int $maxExpected, ?string $v */ public function testWrongVersion(string $cidr, string $version) { - $this->validator->validate($cidr, new Cidr(['version' => $version])); + $this->validator->validate($cidr, new Cidr(version: $version)); $this ->buildViolation('This value is not a valid CIDR notation.') diff --git a/Tests/Constraints/CollectionTest.php b/Tests/Constraints/CollectionTest.php index a2c606154..4299edb26 100644 --- a/Tests/Constraints/CollectionTest.php +++ b/Tests/Constraints/CollectionTest.php @@ -25,6 +25,9 @@ */ class CollectionTest extends TestCase { + /** + * @group legacy + */ public function testRejectNonConstraints() { $this->expectException(InvalidOptionsException::class); @@ -59,18 +62,14 @@ public function testRejectValidConstraintWithinRequired() public function testAcceptOptionalConstraintAsOneElementArray() { - $collection1 = new Collection([ - 'fields' => [ - 'alternate_email' => [ - new Optional(new Email()), - ], + $collection1 = new Collection(fields: [ + 'alternate_email' => [ + new Optional(new Email()), ], ]); - $collection2 = new Collection([ - 'fields' => [ - 'alternate_email' => new Optional(new Email()), - ], + $collection2 = new Collection(fields: [ + 'alternate_email' => new Optional(new Email()), ]); $this->assertEquals($collection1, $collection2); @@ -78,18 +77,14 @@ public function testAcceptOptionalConstraintAsOneElementArray() public function testAcceptRequiredConstraintAsOneElementArray() { - $collection1 = new Collection([ - 'fields' => [ - 'alternate_email' => [ - new Required(new Email()), - ], + $collection1 = new Collection(fields: [ + 'alternate_email' => [ + new Required(new Email()), ], ]); - $collection2 = new Collection([ - 'fields' => [ - 'alternate_email' => new Required(new Email()), - ], + $collection2 = new Collection(fields: [ + 'alternate_email' => new Required(new Email()), ]); $this->assertEquals($collection1, $collection2); @@ -107,6 +102,9 @@ public function testConstraintHasDefaultGroupWithOptionalValues() $this->assertEquals(['Default'], $constraint->fields['bar']->groups); } + /** + * @group legacy + */ public function testOnlySomeKeysAreKnowOptions() { $constraint = new Collection([ @@ -125,15 +123,15 @@ public function testOnlySomeKeysAreKnowOptions() public function testAllKeysAreKnowOptions() { - $constraint = new Collection([ - 'fields' => [ + $constraint = new Collection( + fields: [ 'fields' => [new Required()], 'properties' => [new Required()], 'catalog' => [new Optional()], ], - 'allowExtraFields' => true, - 'extraFieldsMessage' => 'foo bar baz', - ]); + allowExtraFields: true, + extraFieldsMessage: 'foo bar baz', + ); $this->assertArrayHasKey('fields', $constraint->fields); $this->assertInstanceOf(Required::class, $constraint->fields['fields']); @@ -156,11 +154,11 @@ public function testEmptyFields() public function testEmptyFieldsInOptions() { - $constraint = new Collection([ - 'fields' => [], - 'allowExtraFields' => true, - 'extraFieldsMessage' => 'foo bar baz', - ]); + $constraint = new Collection( + fields: [], + allowExtraFields: true, + extraFieldsMessage: 'foo bar baz', + ); $this->assertSame([], $constraint->fields); $this->assertTrue($constraint->allowExtraFields); @@ -196,13 +194,13 @@ public function testEmptyConstraintListForField(?array $fieldConstraint) */ public function testEmptyConstraintListForFieldInOptions(?array $fieldConstraint) { - $constraint = new Collection([ - 'fields' => [ + $constraint = new Collection( + fields: [ 'foo' => $fieldConstraint, ], - 'allowExtraFields' => true, - 'extraFieldsMessage' => 'foo bar baz', - ]); + allowExtraFields: true, + extraFieldsMessage: 'foo bar baz', + ); $this->assertArrayHasKey('foo', $constraint->fields); $this->assertInstanceOf(Required::class, $constraint->fields['foo']); diff --git a/Tests/Constraints/CollectionValidatorTestCase.php b/Tests/Constraints/CollectionValidatorTestCase.php index 92260e966..8e03a9add 100644 --- a/Tests/Constraints/CollectionValidatorTestCase.php +++ b/Tests/Constraints/CollectionValidatorTestCase.php @@ -31,16 +31,16 @@ abstract protected function prepareTestData(array $contents); public function testNullIsValid() { - $this->validator->validate(null, new Collection(['fields' => [ - 'foo' => new Range(['min' => 4]), - ]])); + $this->validator->validate(null, new Collection(fields: [ + 'foo' => new Range(min: 4), + ])); $this->assertNoViolation(); } public function testFieldsAsDefaultOption() { - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $data = $this->prepareTestData(['foo' => 'foobar']); @@ -56,14 +56,14 @@ public function testFieldsAsDefaultOption() public function testThrowsExceptionIfNotTraversable() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate('foobar', new Collection(['fields' => [ - 'foo' => new Range(['min' => 4]), - ]])); + $this->validator->validate('foobar', new Collection(fields: [ + 'foo' => new Range(min: 4), + ])); } public function testWalkSingleConstraint() { - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $array = [ 'foo' => 3, @@ -78,12 +78,12 @@ public function testWalkSingleConstraint() $data = $this->prepareTestData($array); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, 'bar' => $constraint, ], - ])); + )); $this->assertNoViolation(); } @@ -91,7 +91,7 @@ public function testWalkSingleConstraint() public function testWalkMultipleConstraints() { $constraints = [ - new Range(['min' => 4]), + new Range(min: 4), new NotNull(), ]; @@ -108,19 +108,19 @@ public function testWalkMultipleConstraints() $data = $this->prepareTestData($array); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraints, 'bar' => $constraints, ], - ])); + )); $this->assertNoViolation(); } public function testExtraFieldsDisallowed() { - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $data = $this->prepareTestData([ 'foo' => 5, @@ -129,12 +129,12 @@ public function testExtraFieldsDisallowed() $this->expectValidateValueAt(0, '[foo]', $data['foo'], [$constraint]); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, ], - 'extraFieldsMessage' => 'myMessage', - ])); + extraFieldsMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ field }}', '"baz"') @@ -152,12 +152,12 @@ public function testExtraFieldsDisallowedWithOptionalValues() 'baz' => 6, ]); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, ], - 'extraFieldsMessage' => 'myMessage', - ])); + extraFieldsMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ field }}', '"baz"') @@ -174,15 +174,15 @@ public function testNullNotConsideredExtraField() 'foo' => null, ]); - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $this->expectValidateValueAt(0, '[foo]', $data['foo'], [$constraint]); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, ], - ])); + )); $this->assertNoViolation(); } @@ -194,16 +194,16 @@ public function testExtraFieldsAllowed() 'bar' => 6, ]); - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $this->expectValidateValueAt(0, '[foo]', $data['foo'], [$constraint]); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, ], - 'allowExtraFields' => true, - ])); + allowExtraFields: true, + )); $this->assertNoViolation(); } @@ -212,14 +212,14 @@ public function testMissingFieldsDisallowed() { $data = $this->prepareTestData([]); - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, ], - 'missingFieldsMessage' => 'myMessage', - ])); + missingFieldsMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ field }}', '"foo"') @@ -233,14 +233,14 @@ public function testMissingFieldsAllowed() { $data = $this->prepareTestData([]); - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => $constraint, ], - 'allowMissingFields' => true, - ])); + allowMissingFields: true, + )); $this->assertNoViolation(); } @@ -275,7 +275,7 @@ public function testOptionalFieldSingleConstraint() 'foo' => 5, ]; - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $this->expectValidateValueAt(0, '[foo]', $array['foo'], [$constraint]); @@ -296,7 +296,7 @@ public function testOptionalFieldMultipleConstraints() $constraints = [ new NotNull(), - new Range(['min' => 4]), + new Range(min: 4), ]; $this->expectValidateValueAt(0, '[foo]', $array['foo'], $constraints); @@ -327,12 +327,12 @@ public function testRequiredFieldNotPresent() { $data = $this->prepareTestData([]); - $this->validator->validate($data, new Collection([ - 'fields' => [ + $this->validator->validate($data, new Collection( + fields: [ 'foo' => new Required(), ], - 'missingFieldsMessage' => 'myMessage', - ])); + missingFieldsMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ field }}', '"foo"') @@ -348,7 +348,7 @@ public function testRequiredFieldSingleConstraint() 'foo' => 5, ]; - $constraint = new Range(['min' => 4]); + $constraint = new Range(min: 4); $this->expectValidateValueAt(0, '[foo]', $array['foo'], [$constraint]); @@ -369,7 +369,7 @@ public function testRequiredFieldMultipleConstraints() $constraints = [ new NotNull(), - new Range(['min' => 4]), + new Range(min: 4), ]; $this->expectValidateValueAt(0, '[foo]', $array['foo'], $constraints); @@ -389,15 +389,15 @@ public function testObjectShouldBeLeftUnchanged() 'foo' => 3, ]); - $constraint = new Range(['min' => 2]); + $constraint = new Range(min: 2); $this->expectValidateValueAt(0, '[foo]', $value['foo'], [$constraint]); - $this->validator->validate($value, new Collection([ - 'fields' => [ + $this->validator->validate($value, new Collection( + fields: [ 'foo' => $constraint, ], - ])); + )); $this->assertEquals([ 'foo' => 3, diff --git a/Tests/Constraints/CompositeTest.php b/Tests/Constraints/CompositeTest.php index 127ad21dd..a769a68e4 100644 --- a/Tests/Constraints/CompositeTest.php +++ b/Tests/Constraints/CompositeTest.php @@ -66,8 +66,8 @@ public function testNestedCompositeConstraintHasDefaultGroup() public function testMergeNestedGroupsIfNoExplicitParentGroup() { $constraint = new ConcreteComposite([ - new NotNull(['groups' => 'Default']), - new NotBlank(['groups' => ['Default', 'Strict']]), + new NotNull(groups: ['Default']), + new NotBlank(groups: ['Default', 'Strict']), ]); $this->assertEquals(['Default', 'Strict'], $constraint->groups); @@ -94,8 +94,8 @@ public function testExplicitNestedGroupsMustBeSubsetOfExplicitParentGroups() { $constraint = new ConcreteComposite([ 'constraints' => [ - new NotNull(['groups' => 'Default']), - new NotBlank(['groups' => 'Strict']), + new NotNull(groups: ['Default']), + new NotBlank(groups: ['Strict']), ], 'groups' => ['Default', 'Strict'], ]); @@ -110,7 +110,7 @@ public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroups() $this->expectException(ConstraintDefinitionException::class); new ConcreteComposite([ 'constraints' => [ - new NotNull(['groups' => ['Default', 'Foobar']]), + new NotNull(groups: ['Default', 'Foobar']), ], 'groups' => ['Default', 'Strict'], ]); @@ -119,8 +119,8 @@ public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroups() public function testImplicitGroupNamesAreForwarded() { $constraint = new ConcreteComposite([ - new NotNull(['groups' => 'Default']), - new NotBlank(['groups' => 'Strict']), + new NotNull(groups: ['Default']), + new NotBlank(groups: ['Strict']), ]); $constraint->addImplicitGroupName('ImplicitGroup'); @@ -142,7 +142,7 @@ public function testFailIfNoConstraint() { $this->expectException(ConstraintDefinitionException::class); new ConcreteComposite([ - new NotNull(['groups' => 'Default']), + new NotNull(groups: ['Default']), 'NotBlank', ]); } @@ -151,7 +151,7 @@ public function testFailIfNoConstraintObject() { $this->expectException(ConstraintDefinitionException::class); new ConcreteComposite([ - new NotNull(['groups' => 'Default']), + new NotNull(groups: ['Default']), new \ArrayObject(), ]); } diff --git a/Tests/Constraints/CompoundTest.php b/Tests/Constraints/CompoundTest.php index 26889a0cc..9b515a48c 100644 --- a/Tests/Constraints/CompoundTest.php +++ b/Tests/Constraints/CompoundTest.php @@ -19,6 +19,9 @@ class CompoundTest extends TestCase { + /** + * @group legacy + */ public function testItCannotRedefineConstraintsOption() { $this->expectException(ConstraintDefinitionException::class); @@ -72,7 +75,7 @@ public function getDefaultOption(): ?string protected function getConstraints(array $options): array { return [ - new Length(['min' => $options['min'] ?? null]), + new Length(min: $options['min'] ?? null), ]; } } diff --git a/Tests/Constraints/CountValidatorTestCase.php b/Tests/Constraints/CountValidatorTestCase.php index c52cd4e69..f60199027 100644 --- a/Tests/Constraints/CountValidatorTestCase.php +++ b/Tests/Constraints/CountValidatorTestCase.php @@ -70,6 +70,7 @@ public static function getFiveOrMoreElements() } /** + * @group legacy * @dataProvider getThreeOrLessElements */ public function testValidValuesMax($value) @@ -92,6 +93,7 @@ public function testValidValuesMaxNamed($value) } /** + * @group legacy * @dataProvider getFiveOrMoreElements */ public function testValidValuesMin($value) @@ -114,6 +116,7 @@ public function testValidValuesMinNamed($value) } /** + * @group legacy * @dataProvider getFourElements */ public function testValidValuesExact($value) @@ -136,6 +139,7 @@ public function testValidValuesExactNamed($value) } /** + * @group legacy * @dataProvider getFiveOrMoreElements */ public function testTooManyValues($value) @@ -175,6 +179,7 @@ public function testTooManyValuesNamed($value) } /** + * @group legacy * @dataProvider getThreeOrLessElements */ public function testTooFewValues($value) @@ -214,6 +219,7 @@ public function testTooFewValuesNamed($value) } /** + * @group legacy * @dataProvider getFiveOrMoreElements */ public function testTooManyValuesExact($value) @@ -258,11 +264,11 @@ public function testTooManyValuesExactNamed($value) */ public function testTooFewValuesExact($value) { - $constraint = new Count([ - 'min' => 4, - 'max' => 4, - 'exactMessage' => 'myMessage', - ]); + $constraint = new Count( + min: 4, + max: 4, + exactMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -285,7 +291,7 @@ public function testDefaultOption() public function testConstraintAttributeDefaultOption() { - $constraint = new Count(['value' => 5, 'exactMessage' => 'message']); + $constraint = new Count(exactly: 5, exactMessage: 'message'); $this->assertEquals(5, $constraint->min); $this->assertEquals(5, $constraint->max); @@ -296,15 +302,15 @@ public function testConstraintAttributeDefaultOption() // is called with the right DivisibleBy constraint. public function testDivisibleBy() { - $constraint = new Count([ - 'divisibleBy' => 123, - 'divisibleByMessage' => 'foo {{ compared_value }}', - ]); - - $this->expectValidateValue(0, 3, [new DivisibleBy([ - 'value' => 123, - 'message' => 'foo {{ compared_value }}', - ])], $this->group); + $constraint = new Count( + divisibleBy: 123, + divisibleByMessage: 'foo {{ compared_value }}', + ); + + $this->expectValidateValue(0, 3, [new DivisibleBy( + value: 123, + message: 'foo {{ compared_value }}', + )], $this->group); $this->validator->validate(['foo', 'bar', 'ccc'], $constraint); diff --git a/Tests/Constraints/CountryValidatorTest.php b/Tests/Constraints/CountryValidatorTest.php index 524d0bc54..e535ce4f5 100644 --- a/Tests/Constraints/CountryValidatorTest.php +++ b/Tests/Constraints/CountryValidatorTest.php @@ -84,9 +84,7 @@ public static function getValidCountries() */ public function testInvalidCountries($country) { - $constraint = new Country([ - 'message' => 'myMessage', - ]); + $constraint = new Country(message: 'myMessage'); $this->validator->validate($country, $constraint); @@ -109,9 +107,7 @@ public static function getInvalidCountries() */ public function testValidAlpha3Countries($country) { - $this->validator->validate($country, new Country([ - 'alpha3' => true, - ])); + $this->validator->validate($country, new Country(alpha3: true)); $this->assertNoViolation(); } @@ -130,10 +126,10 @@ public static function getValidAlpha3Countries() */ public function testInvalidAlpha3Countries($country) { - $constraint = new Country([ - 'alpha3' => true, - 'message' => 'myMessage', - ]); + $constraint = new Country( + alpha3: true, + message: 'myMessage', + ); $this->validator->validate($country, $constraint); diff --git a/Tests/Constraints/CurrencyValidatorTest.php b/Tests/Constraints/CurrencyValidatorTest.php index a0e16ec14..51def4a2a 100644 --- a/Tests/Constraints/CurrencyValidatorTest.php +++ b/Tests/Constraints/CurrencyValidatorTest.php @@ -100,9 +100,7 @@ public static function getValidCurrencies() */ public function testInvalidCurrencies($currency) { - $constraint = new Currency([ - 'message' => 'myMessage', - ]); + $constraint = new Currency(message: 'myMessage'); $this->validator->validate($currency, $constraint); diff --git a/Tests/Constraints/DateTimeValidatorTest.php b/Tests/Constraints/DateTimeValidatorTest.php index 42519ffd4..383f06215 100644 --- a/Tests/Constraints/DateTimeValidatorTest.php +++ b/Tests/Constraints/DateTimeValidatorTest.php @@ -63,9 +63,7 @@ public function testDateTimeWithDefaultFormat() */ public function testValidDateTimes($format, $dateTime) { - $constraint = new DateTime([ - 'format' => $format, - ]); + $constraint = new DateTime(format: $format); $this->validator->validate($dateTime, $constraint); @@ -88,10 +86,10 @@ public static function getValidDateTimes() */ public function testInvalidDateTimes($format, $dateTime, $code) { - $constraint = new DateTime([ - 'message' => 'myMessage', - 'format' => $format, - ]); + $constraint = new DateTime( + message: 'myMessage', + format: $format, + ); $this->validator->validate($dateTime, $constraint); @@ -133,9 +131,7 @@ public function testInvalidDateTimeNamed() public function testDateTimeWithTrailingData() { - $this->validator->validate('1995-05-10 00:00:00', new DateTime([ - 'format' => 'Y-m-d+', - ])); + $this->validator->validate('1995-05-10 00:00:00', new DateTime(format: 'Y-m-d+')); $this->assertNoViolation(); } } diff --git a/Tests/Constraints/DateValidatorTest.php b/Tests/Constraints/DateValidatorTest.php index 93dab41f2..65909ef83 100644 --- a/Tests/Constraints/DateValidatorTest.php +++ b/Tests/Constraints/DateValidatorTest.php @@ -58,7 +58,7 @@ public function testValidDates($date) */ public function testValidDatesWithNewLine(string $date) { - $this->validator->validate($date."\n", new Date(['message' => 'myMessage'])); + $this->validator->validate($date."\n", new Date(message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$date."\n\"") @@ -80,9 +80,7 @@ public static function getValidDates() */ public function testInvalidDates($date, $code) { - $constraint = new Date([ - 'message' => 'myMessage', - ]); + $constraint = new Date(message: 'myMessage'); $this->validator->validate($date, $constraint); diff --git a/Tests/Constraints/DisableAutoMappingTest.php b/Tests/Constraints/DisableAutoMappingTest.php index 709334e36..e7b6a8db7 100644 --- a/Tests/Constraints/DisableAutoMappingTest.php +++ b/Tests/Constraints/DisableAutoMappingTest.php @@ -23,6 +23,9 @@ */ class DisableAutoMappingTest extends TestCase { + /** + * @group legacy + */ public function testGroups() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/DivisibleByValidatorTest.php b/Tests/Constraints/DivisibleByValidatorTest.php index 22dc683fb..be96ad2b4 100644 --- a/Tests/Constraints/DivisibleByValidatorTest.php +++ b/Tests/Constraints/DivisibleByValidatorTest.php @@ -32,7 +32,11 @@ protected function createValidator(): DivisibleByValidator protected static function createConstraint(?array $options = null): Constraint { - return new DivisibleBy($options); + if (null !== $options) { + return new DivisibleBy(...$options); + } + + return new DivisibleBy(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/EmailTest.php b/Tests/Constraints/EmailTest.php index 8489f9cfe..9436b4bd6 100644 --- a/Tests/Constraints/EmailTest.php +++ b/Tests/Constraints/EmailTest.php @@ -21,14 +21,14 @@ class EmailTest extends TestCase { public function testConstructorStrict() { - $subject = new Email(['mode' => Email::VALIDATION_MODE_STRICT]); + $subject = new Email(mode: Email::VALIDATION_MODE_STRICT); $this->assertEquals(Email::VALIDATION_MODE_STRICT, $subject->mode); } public function testConstructorHtml5AllowNoTld() { - $subject = new Email(['mode' => Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD]); + $subject = new Email(mode: Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD); $this->assertEquals(Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, $subject->mode); } @@ -37,7 +37,7 @@ public function testUnknownModesTriggerException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The "mode" parameter value is not valid.'); - new Email(['mode' => 'Unknown Mode']); + new Email(mode: 'Unknown Mode'); } public function testUnknownModeArgumentsTriggerException() @@ -49,11 +49,14 @@ public function testUnknownModeArgumentsTriggerException() public function testNormalizerCanBeSet() { - $email = new Email(['normalizer' => 'trim']); + $email = new Email(normalizer: 'trim'); $this->assertEquals('trim', $email->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -61,6 +64,9 @@ public function testInvalidNormalizerThrowsException() new Email(['normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/EmailValidatorTest.php b/Tests/Constraints/EmailValidatorTest.php index 197490ce7..483b534e6 100644 --- a/Tests/Constraints/EmailValidatorTest.php +++ b/Tests/Constraints/EmailValidatorTest.php @@ -97,7 +97,7 @@ public static function getValidEmails() */ public function testValidNormalizedEmails($email) { - $this->validator->validate($email, new Email(['normalizer' => 'trim'])); + $this->validator->validate($email, new Email(normalizer: 'trim')); $this->assertNoViolation(); } @@ -115,7 +115,7 @@ public static function getValidEmailsWithWhitespaces() */ public function testValidEmailsHtml5($email) { - $this->validator->validate($email, new Email(['mode' => Email::VALIDATION_MODE_HTML5])); + $this->validator->validate($email, new Email(mode: Email::VALIDATION_MODE_HTML5)); $this->assertNoViolation(); } @@ -135,9 +135,7 @@ public static function getValidEmailsHtml5() */ public function testInvalidEmails($email) { - $constraint = new Email([ - 'message' => 'myMessage', - ]); + $constraint = new Email(message: 'myMessage'); $this->validator->validate($email, $constraint); @@ -162,10 +160,10 @@ public static function getInvalidEmails() */ public function testInvalidHtml5Emails($email) { - $constraint = new Email([ - 'message' => 'myMessage', - 'mode' => Email::VALIDATION_MODE_HTML5, - ]); + $constraint = new Email( + message: 'myMessage', + mode: Email::VALIDATION_MODE_HTML5, + ); $this->validator->validate($email, $constraint); @@ -202,10 +200,10 @@ public static function getInvalidHtml5Emails() */ public function testInvalidAllowNoTldEmails($email) { - $constraint = new Email([ - 'message' => 'myMessage', - 'mode' => Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, - ]); + $constraint = new Email( + message: 'myMessage', + mode: Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, + ); $this->validator->validate($email, $constraint); @@ -228,7 +226,7 @@ public static function getInvalidAllowNoTldEmails() public function testModeStrict() { - $constraint = new Email(['mode' => Email::VALIDATION_MODE_STRICT]); + $constraint = new Email(mode: Email::VALIDATION_MODE_STRICT); $this->validator->validate('example@mywebsite.tld', $constraint); @@ -237,7 +235,7 @@ public function testModeStrict() public function testModeHtml5() { - $constraint = new Email(['mode' => Email::VALIDATION_MODE_HTML5]); + $constraint = new Email(mode: Email::VALIDATION_MODE_HTML5); $this->validator->validate('example@example..com', $constraint); @@ -249,7 +247,7 @@ public function testModeHtml5() public function testModeHtml5AllowNoTld() { - $constraint = new Email(['mode' => Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD]); + $constraint = new Email(mode: Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD); $this->validator->validate('example@example', $constraint); @@ -272,10 +270,10 @@ public function testUnknownModesOnValidateTriggerException() */ public function testStrictWithInvalidEmails($email) { - $constraint = new Email([ - 'message' => 'myMessage', - 'mode' => Email::VALIDATION_MODE_STRICT, - ]); + $constraint = new Email( + message: 'myMessage', + mode: Email::VALIDATION_MODE_STRICT, + ); $this->validator->validate($email, $constraint); diff --git a/Tests/Constraints/EnableAutoMappingTest.php b/Tests/Constraints/EnableAutoMappingTest.php index 66ab42cdf..525a62ed5 100644 --- a/Tests/Constraints/EnableAutoMappingTest.php +++ b/Tests/Constraints/EnableAutoMappingTest.php @@ -23,6 +23,9 @@ */ class EnableAutoMappingTest extends TestCase { + /** + * @group legacy + */ public function testGroups() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/EqualToValidatorTest.php b/Tests/Constraints/EqualToValidatorTest.php index b1af4ed18..c9a24ac4d 100644 --- a/Tests/Constraints/EqualToValidatorTest.php +++ b/Tests/Constraints/EqualToValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): EqualToValidator protected static function createConstraint(?array $options = null): Constraint { - return new EqualTo($options); + if (null !== $options) { + return new EqualTo(...$options); + } + + return new EqualTo(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/ExpressionSyntaxTest.php b/Tests/Constraints/ExpressionSyntaxTest.php index 3f77cace2..8731a5d85 100644 --- a/Tests/Constraints/ExpressionSyntaxTest.php +++ b/Tests/Constraints/ExpressionSyntaxTest.php @@ -36,8 +36,6 @@ public function testValidatedByService(ExpressionSyntax $constraint) public static function provideServiceValidatedConstraints(): iterable { - yield 'Doctrine style' => [new ExpressionSyntax(['service' => 'my_service'])]; - yield 'named arguments' => [new ExpressionSyntax(service: 'my_service')]; $metadata = new ClassMetadata(ExpressionSyntaxDummy::class); @@ -46,6 +44,16 @@ public static function provideServiceValidatedConstraints(): iterable yield 'attribute' => [$metadata->properties['b']->constraints[0]]; } + /** + * @group legacy + */ + public function testValidatedByServiceDoctrineStyle() + { + $constraint = new ExpressionSyntax(['service' => 'my_service']); + + self::assertSame('my_service', $constraint->validatedBy()); + } + public function testAttributes() { $metadata = new ClassMetadata(ExpressionSyntaxDummy::class); diff --git a/Tests/Constraints/ExpressionSyntaxValidatorTest.php b/Tests/Constraints/ExpressionSyntaxValidatorTest.php index 65be7fb2a..3ca4e655b 100644 --- a/Tests/Constraints/ExpressionSyntaxValidatorTest.php +++ b/Tests/Constraints/ExpressionSyntaxValidatorTest.php @@ -40,49 +40,47 @@ public function testEmptyStringIsValid() public function testExpressionValid() { - $this->validator->validate('1 + 1', new ExpressionSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => [], - ])); + $this->validator->validate('1 + 1', new ExpressionSyntax( + message: 'myMessage', + allowedVariables: [], + )); $this->assertNoViolation(); } public function testStringableExpressionValid() { - $this->validator->validate(new StringableValue('1 + 1'), new ExpressionSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => [], - ])); + $this->validator->validate(new StringableValue('1 + 1'), new ExpressionSyntax( + message: 'myMessage', + allowedVariables: [], + )); $this->assertNoViolation(); } public function testExpressionWithoutNames() { - $this->validator->validate('1 + 1', new ExpressionSyntax([ - 'message' => 'myMessage', - ], null, null, [])); + $this->validator->validate('1 + 1', new ExpressionSyntax(null, 'myMessage', null, [])); $this->assertNoViolation(); } public function testExpressionWithAllowedVariableName() { - $this->validator->validate('a + 1', new ExpressionSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => ['a'], - ])); + $this->validator->validate('a + 1', new ExpressionSyntax( + message: 'myMessage', + allowedVariables: ['a'], + )); $this->assertNoViolation(); } public function testExpressionIsNotValid() { - $this->validator->validate('a + 1', new ExpressionSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => [], - ])); + $this->validator->validate('a + 1', new ExpressionSyntax( + message: 'myMessage', + allowedVariables: [], + )); $this->buildViolation('myMessage') ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."') @@ -93,10 +91,10 @@ public function testExpressionIsNotValid() public function testStringableExpressionIsNotValid() { - $this->validator->validate(new StringableValue('a + 1'), new ExpressionSyntax([ - 'message' => 'myMessage', - 'allowedVariables' => [], - ])); + $this->validator->validate(new StringableValue('a + 1'), new ExpressionSyntax( + message: 'myMessage', + allowedVariables: [], + )); $this->buildViolation('myMessage') ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."') diff --git a/Tests/Constraints/ExpressionValidatorTest.php b/Tests/Constraints/ExpressionValidatorTest.php index c237c793f..21c9eb630 100644 --- a/Tests/Constraints/ExpressionValidatorTest.php +++ b/Tests/Constraints/ExpressionValidatorTest.php @@ -31,10 +31,10 @@ protected function createValidator(): ExpressionValidator public function testExpressionIsEvaluatedWithNullValue() { - $constraint = new Expression([ - 'expression' => 'false', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'false', + message: 'myMessage', + ); $this->validator->validate(null, $constraint); @@ -46,10 +46,10 @@ public function testExpressionIsEvaluatedWithNullValue() public function testExpressionIsEvaluatedWithEmptyStringValue() { - $constraint = new Expression([ - 'expression' => 'false', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'false', + message: 'myMessage', + ); $this->validator->validate('', $constraint); @@ -75,10 +75,10 @@ public function testSucceedingExpressionAtObjectLevel() public function testFailingExpressionAtObjectLevel() { - $constraint = new Expression([ - 'expression' => 'this.data == 1', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'this.data == 1', + message: 'myMessage', + ); $object = new Entity(); $object->data = '2'; @@ -109,10 +109,10 @@ public function testSucceedingExpressionAtObjectLevelWithToString() public function testFailingExpressionAtObjectLevelWithToString() { - $constraint = new Expression([ - 'expression' => 'this.data == 1', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'this.data == 1', + message: 'myMessage', + ); $object = new ToString(); $object->data = '2'; @@ -145,10 +145,10 @@ public function testSucceedingExpressionAtPropertyLevel() public function testFailingExpressionAtPropertyLevel() { - $constraint = new Expression([ - 'expression' => 'value == this.data', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'value == this.data', + message: 'myMessage', + ); $object = new Entity(); $object->data = '1'; @@ -187,10 +187,10 @@ public function testSucceedingExpressionAtNestedPropertyLevel() public function testFailingExpressionAtNestedPropertyLevel() { - $constraint = new Expression([ - 'expression' => 'value == this.data', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'value == this.data', + message: 'myMessage', + ); $object = new Entity(); $object->data = '1'; @@ -234,10 +234,10 @@ public function testSucceedingExpressionAtPropertyLevelWithoutRoot() */ public function testFailingExpressionAtPropertyLevelWithoutRoot() { - $constraint = new Expression([ - 'expression' => 'value == "1"', - 'message' => 'myMessage', - ]); + $constraint = new Expression( + expression: 'value == "1"', + message: 'myMessage', + ); $this->setRoot('2'); $this->setPropertyPath(''); @@ -254,9 +254,7 @@ public function testFailingExpressionAtPropertyLevelWithoutRoot() public function testExpressionLanguageUsage() { - $constraint = new Expression([ - 'expression' => 'false', - ]); + $constraint = new Expression(expression: 'false'); $expressionLanguage = $this->createMock(ExpressionLanguage::class); @@ -278,12 +276,12 @@ public function testExpressionLanguageUsage() public function testPassingCustomValues() { - $constraint = new Expression([ - 'expression' => 'value + custom == 2', - 'values' => [ + $constraint = new Expression( + expression: 'value + custom == 2', + values: [ 'custom' => 1, ], - ]); + ); $this->validator->validate(1, $constraint); @@ -292,13 +290,13 @@ public function testPassingCustomValues() public function testViolationOnPass() { - $constraint = new Expression([ - 'expression' => 'value + custom != 2', - 'values' => [ + $constraint = new Expression( + expression: 'value + custom != 2', + values: [ 'custom' => 1, ], - 'negate' => false, - ]); + negate: false, + ); $this->validator->validate(2, $constraint); @@ -311,10 +309,11 @@ public function testViolationOnPass() public function testIsValidExpression() { - $constraints = [new NotNull(), new Range(['min' => 2])]; + $constraints = [new NotNull(), new Range(min: 2)]; $constraint = new Expression( - ['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]] + expression: 'is_valid(this.data, a)', + values: ['a' => $constraints], ); $object = new Entity(); @@ -331,10 +330,11 @@ public function testIsValidExpression() public function testIsValidExpressionInvalid() { - $constraints = [new Range(['min' => 2, 'max' => 5])]; + $constraints = [new Range(min: 2, max: 5)]; $constraint = new Expression( - ['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]] + expression: 'is_valid(this.data, a)', + values: ['a' => $constraints], ); $object = new Entity(); diff --git a/Tests/Constraints/FileTest.php b/Tests/Constraints/FileTest.php index e8c27b4b1..e4e30a581 100644 --- a/Tests/Constraints/FileTest.php +++ b/Tests/Constraints/FileTest.php @@ -24,7 +24,7 @@ class FileTest extends TestCase */ public function testMaxSize($maxSize, $bytes, $binaryFormat) { - $file = new File(['maxSize' => $maxSize]); + $file = new File(maxSize: $maxSize); $this->assertSame($bytes, $file->maxSize); $this->assertSame($binaryFormat, $file->binaryFormat); @@ -33,7 +33,7 @@ public function testMaxSize($maxSize, $bytes, $binaryFormat) public function testMagicIsset() { - $file = new File(['maxSize' => 1]); + $file = new File(maxSize: 1); $this->assertTrue($file->__isset('maxSize')); $this->assertTrue($file->__isset('groups')); @@ -57,7 +57,7 @@ public function testMaxSizeCanBeSetAfterInitialization($maxSize, $bytes, $binary */ public function testInvalidValueForMaxSizeThrowsExceptionAfterInitialization($maxSize) { - $file = new File(['maxSize' => 1000]); + $file = new File(maxSize: 1000); $this->expectException(ConstraintDefinitionException::class); @@ -69,7 +69,7 @@ public function testInvalidValueForMaxSizeThrowsExceptionAfterInitialization($ma */ public function testMaxSizeCannotBeSetToInvalidValueAfterInitialization($maxSize) { - $file = new File(['maxSize' => 1000]); + $file = new File(maxSize: 1000); try { $file->maxSize = $maxSize; @@ -85,7 +85,7 @@ public function testMaxSizeCannotBeSetToInvalidValueAfterInitialization($maxSize public function testInvalidMaxSize($maxSize) { $this->expectException(ConstraintDefinitionException::class); - new File(['maxSize' => $maxSize]); + new File(maxSize: $maxSize); } public static function provideValidSizes() @@ -125,7 +125,7 @@ public static function provideInvalidSizes() */ public function testBinaryFormat($maxSize, $guessedFormat, $binaryFormat) { - $file = new File(['maxSize' => $maxSize, 'binaryFormat' => $guessedFormat]); + $file = new File(maxSize: $maxSize, binaryFormat: $guessedFormat); $this->assertSame($binaryFormat, $file->binaryFormat); } diff --git a/Tests/Constraints/FileValidatorPathTest.php b/Tests/Constraints/FileValidatorPathTest.php index 9a8968801..37557aa1a 100644 --- a/Tests/Constraints/FileValidatorPathTest.php +++ b/Tests/Constraints/FileValidatorPathTest.php @@ -22,9 +22,9 @@ protected function getFile($filename) public function testFileNotFound() { - $constraint = new File([ - 'notFoundMessage' => 'myMessage', - ]); + $constraint = new File( + notFoundMessage: 'myMessage', + ); $this->validator->validate('foobar', $constraint); diff --git a/Tests/Constraints/FileValidatorTestCase.php b/Tests/Constraints/FileValidatorTestCase.php index b5d05801e..81e833b27 100644 --- a/Tests/Constraints/FileValidatorTestCase.php +++ b/Tests/Constraints/FileValidatorTestCase.php @@ -168,10 +168,10 @@ public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limit fwrite($this->file, '0'); fclose($this->file); - $constraint = new File([ - 'maxSize' => $limit, - 'maxSizeMessage' => 'myMessage', - ]); + $constraint = new File( + maxSize: $limit, + maxSizeMessage: 'myMessage', + ); $this->validator->validate($this->getFile($this->path), $constraint); @@ -220,10 +220,10 @@ public function testMaxSizeNotExceeded($bytesWritten, $limit) fwrite($this->file, '0'); fclose($this->file); - $constraint = new File([ - 'maxSize' => $limit, - 'maxSizeMessage' => 'myMessage', - ]); + $constraint = new File( + maxSize: $limit, + maxSizeMessage: 'myMessage', + ); $this->validator->validate($this->getFile($this->path), $constraint); @@ -233,9 +233,7 @@ public function testMaxSizeNotExceeded($bytesWritten, $limit) public function testInvalidMaxSize() { $this->expectException(ConstraintDefinitionException::class); - new File([ - 'maxSize' => '1abc', - ]); + new File(maxSize: '1abc'); } public static function provideBinaryFormatTests() @@ -269,11 +267,11 @@ public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsSt fwrite($this->file, '0'); fclose($this->file); - $constraint = new File([ - 'maxSize' => $limit, - 'binaryFormat' => $binaryFormat, - 'maxSizeMessage' => 'myMessage', - ]); + $constraint = new File( + maxSize: $limit, + binaryFormat: $binaryFormat, + maxSizeMessage: 'myMessage', + ); $this->validator->validate($this->getFile($this->path), $constraint); @@ -322,9 +320,7 @@ public function testValidMimeType() ->method('getMimeType') ->willReturn('image/jpg'); - $constraint = new File([ - 'mimeTypes' => ['image/png', 'image/jpg'], - ]); + $constraint = new File(mimeTypes: ['image/png', 'image/jpg']); $this->validator->validate($file, $constraint); @@ -346,19 +342,14 @@ public function testValidWildcardMimeType() ->method('getMimeType') ->willReturn('image/jpg'); - $constraint = new File([ - 'mimeTypes' => ['image/*'], - ]); + $constraint = new File(mimeTypes: ['image/*']); $this->validator->validate($file, $constraint); $this->assertNoViolation(); } - /** - * @dataProvider provideMimeTypeConstraints - */ - public function testInvalidMimeType(File $constraint) + public function testInvalidMimeType() { $file = $this ->getMockBuilder(\Symfony\Component\HttpFoundation\File\File::class) @@ -373,7 +364,7 @@ public function testInvalidMimeType(File $constraint) ->method('getMimeType') ->willReturn('application/pdf'); - $this->validator->validate($file, $constraint); + $this->validator->validate($file, new File(mimeTypes: ['image/png', 'image/jpg'], mimeTypesMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ type }}', '"application/pdf"') @@ -384,15 +375,36 @@ public function testInvalidMimeType(File $constraint) ->assertRaised(); } - public static function provideMimeTypeConstraints(): iterable + /** + * @group legacy + */ + public function testInvalidMimeTypeDoctrineStyle() { - yield 'Doctrine style' => [new File([ + $file = $this + ->getMockBuilder(\Symfony\Component\HttpFoundation\File\File::class) + ->setConstructorArgs([__DIR__.'/Fixtures/foo']) + ->getMock(); + $file + ->expects($this->once()) + ->method('getPathname') + ->willReturn($this->path); + $file + ->expects($this->once()) + ->method('getMimeType') + ->willReturn('application/pdf'); + + $this->validator->validate($file, new File([ 'mimeTypes' => ['image/png', 'image/jpg'], 'mimeTypesMessage' => 'myMessage', - ])]; - yield 'named arguments' => [ - new File(mimeTypes: ['image/png', 'image/jpg'], mimeTypesMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ type }}', '"application/pdf"') + ->setParameter('{{ types }}', '"image/png", "image/jpg"') + ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setParameter('{{ name }}', '"'.basename($this->path).'"') + ->setCode(File::INVALID_MIME_TYPE_ERROR) + ->assertRaised(); } public function testInvalidWildcardMimeType() @@ -410,10 +422,10 @@ public function testInvalidWildcardMimeType() ->method('getMimeType') ->willReturn('application/pdf'); - $constraint = new File([ - 'mimeTypes' => ['image/*', 'image/jpg'], - 'mimeTypesMessage' => 'myMessage', - ]); + $constraint = new File( + mimeTypes: ['image/*', 'image/jpg'], + mimeTypesMessage: 'myMessage', + ); $this->validator->validate($file, $constraint); @@ -426,14 +438,11 @@ public function testInvalidWildcardMimeType() ->assertRaised(); } - /** - * @dataProvider provideDisallowEmptyConstraints - */ - public function testDisallowEmpty(File $constraint) + public function testDisallowEmpty() { ftruncate($this->file, 0); - $this->validator->validate($this->getFile($this->path), $constraint); + $this->validator->validate($this->getFile($this->path), new File(disallowEmptyMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ file }}', '"'.$this->path.'"') @@ -442,14 +451,22 @@ public function testDisallowEmpty(File $constraint) ->assertRaised(); } - public static function provideDisallowEmptyConstraints(): iterable + /** + * @group legacy + */ + public function testDisallowEmptyDoctrineStyle() { - yield 'Doctrine style' => [new File([ + ftruncate($this->file, 0); + + $this->validator->validate($this->getFile($this->path), new File([ 'disallowEmptyMessage' => 'myMessage', - ])]; - yield 'named arguments' => [ - new File(disallowEmptyMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setParameter('{{ name }}', '"'.basename($this->path).'"') + ->setCode(File::EMPTY_ERROR) + ->assertRaised(); } /** @@ -459,7 +476,7 @@ public function testUploadedFileError($error, $message, array $params = [], $max { $file = new UploadedFile(tempnam(sys_get_temp_dir(), 'file-validator-test-'), 'originalName', 'mime', $error); - $constraint = new File([ + $constraint = new File(...[ $message => 'myMessage', 'maxSize' => $maxSize, ]); diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorTest.php index 1bd4b6538..ae9f2034c 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): GreaterThanOrEqualValidator protected static function createConstraint(?array $options = null): Constraint { - return new GreaterThanOrEqual($options); + if (null !== $options) { + return new GreaterThanOrEqual(...$options); + } + + return new GreaterThanOrEqual(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php index b05ba53a5..47f190851 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php @@ -61,6 +61,9 @@ public static function provideInvalidComparisons(): array ]; } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -69,6 +72,9 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new PositiveOrZero(['propertyPath' => 'field']); } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/GreaterThanValidatorTest.php b/Tests/Constraints/GreaterThanValidatorTest.php index 4cc64396d..0e74da15f 100644 --- a/Tests/Constraints/GreaterThanValidatorTest.php +++ b/Tests/Constraints/GreaterThanValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): GreaterThanValidator protected static function createConstraint(?array $options = null): Constraint { - return new GreaterThan($options); + if (null !== $options) { + return new GreaterThan(...$options); + } + + return new GreaterThan(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php index 0aa8aee93..6b58bff85 100644 --- a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php +++ b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php @@ -58,6 +58,9 @@ public static function provideInvalidComparisons(): array ]; } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -66,6 +69,9 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new Positive(['propertyPath' => 'field']); } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/HostnameValidatorTest.php b/Tests/Constraints/HostnameValidatorTest.php index f0b03e019..2471fe0b5 100644 --- a/Tests/Constraints/HostnameValidatorTest.php +++ b/Tests/Constraints/HostnameValidatorTest.php @@ -57,7 +57,7 @@ public function testValidTldDomainsPassValidationIfTldRequired($domain) */ public function testValidTldDomainsPassValidationIfTldNotRequired($domain) { - $this->validator->validate($domain, new Hostname(['requireTld' => false])); + $this->validator->validate($domain, new Hostname(requireTld: false)); $this->assertNoViolation(); } @@ -81,9 +81,7 @@ public static function getValidMultilevelDomains() */ public function testInvalidDomainsRaiseViolationIfTldRequired($domain) { - $this->validator->validate($domain, new Hostname([ - 'message' => 'myMessage', - ])); + $this->validator->validate($domain, new Hostname(message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$domain.'"') @@ -96,10 +94,10 @@ public function testInvalidDomainsRaiseViolationIfTldRequired($domain) */ public function testInvalidDomainsRaiseViolationIfTldNotRequired($domain) { - $this->validator->validate($domain, new Hostname([ - 'message' => 'myMessage', - 'requireTld' => false, - ])); + $this->validator->validate($domain, new Hostname( + message: 'myMessage', + requireTld: false, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$domain.'"') @@ -123,7 +121,7 @@ public static function getInvalidDomains() */ public function testReservedDomainsPassValidationIfTldNotRequired($domain) { - $this->validator->validate($domain, new Hostname(['requireTld' => false])); + $this->validator->validate($domain, new Hostname(requireTld: false)); $this->assertNoViolation(); } @@ -133,10 +131,10 @@ public function testReservedDomainsPassValidationIfTldNotRequired($domain) */ public function testReservedDomainsRaiseViolationIfTldRequired($domain) { - $this->validator->validate($domain, new Hostname([ - 'message' => 'myMessage', - 'requireTld' => true, - ])); + $this->validator->validate($domain, new Hostname( + message: 'myMessage', + requireTld: true, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$domain.'"') @@ -176,7 +174,7 @@ public function testReservedDomainsRaiseViolationIfTldRequiredNamed() */ public function testTopLevelDomainsPassValidationIfTldNotRequired($domain) { - $this->validator->validate($domain, new Hostname(['requireTld' => false])); + $this->validator->validate($domain, new Hostname(requireTld: false)); $this->assertNoViolation(); } @@ -186,10 +184,10 @@ public function testTopLevelDomainsPassValidationIfTldNotRequired($domain) */ public function testTopLevelDomainsRaiseViolationIfTldRequired($domain) { - $this->validator->validate($domain, new Hostname([ - 'message' => 'myMessage', - 'requireTld' => true, - ])); + $this->validator->validate($domain, new Hostname( + message: 'myMessage', + requireTld: true, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$domain.'"') diff --git a/Tests/Constraints/IbanValidatorTest.php b/Tests/Constraints/IbanValidatorTest.php index 2a8156914..184924d5e 100644 --- a/Tests/Constraints/IbanValidatorTest.php +++ b/Tests/Constraints/IbanValidatorTest.php @@ -488,9 +488,7 @@ public static function getIbansWithInvalidCountryCode() private function assertViolationRaised($iban, $code) { - $constraint = new Iban([ - 'message' => 'myMessage', - ]); + $constraint = new Iban(message: 'myMessage'); $this->validator->validate($iban, $constraint); diff --git a/Tests/Constraints/IdenticalToValidatorTest.php b/Tests/Constraints/IdenticalToValidatorTest.php index 66390a6de..97164b74c 100644 --- a/Tests/Constraints/IdenticalToValidatorTest.php +++ b/Tests/Constraints/IdenticalToValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): IdenticalToValidator protected static function createConstraint(?array $options = null): Constraint { - return new IdenticalTo($options); + if (null !== $options) { + return new IdenticalTo(...$options); + } + + return new IdenticalTo(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index d18d81eea..8811c5774 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -72,12 +72,10 @@ public function testValidImage() /** * Checks that the logic from FileValidator still works. - * - * @dataProvider provideConstraintsWithNotFoundMessage */ - public function testFileNotFound(Image $constraint) + public function testFileNotFound() { - $this->validator->validate('foobar', $constraint); + $this->validator->validate('foobar', new Image(notFoundMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ file }}', '"foobar"') @@ -85,36 +83,40 @@ public function testFileNotFound(Image $constraint) ->assertRaised(); } - public static function provideConstraintsWithNotFoundMessage(): iterable + /** + * Checks that the logic from FileValidator still works. + * + * @group legacy + */ + public function testFileNotFoundDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate('foobar', new Image([ 'notFoundMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(notFoundMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ file }}', '"foobar"') + ->setCode(Image::NOT_FOUND_ERROR) + ->assertRaised(); } public function testValidSize() { - $constraint = new Image([ - 'minWidth' => 1, - 'maxWidth' => 2, - 'minHeight' => 1, - 'maxHeight' => 2, - ]); + $constraint = new Image( + minWidth: 1, + maxWidth: 2, + minHeight: 1, + maxHeight: 2, + ); $this->validator->validate($this->image, $constraint); $this->assertNoViolation(); } - /** - * @dataProvider provideMinWidthConstraints - */ - public function testWidthTooSmall(Image $constraint) + public function testWidthTooSmall() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(minWidth: 3, minWidthMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ width }}', '2') @@ -123,23 +125,26 @@ public function testWidthTooSmall(Image $constraint) ->assertRaised(); } - public static function provideMinWidthConstraints(): iterable + /** + * @group legacy + */ + public function testWidthTooSmallDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'minWidth' => 3, 'minWidthMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(minWidth: 3, minWidthMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', '2') + ->setParameter('{{ min_width }}', '3') + ->setCode(Image::TOO_NARROW_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMaxWidthConstraints - */ - public function testWidthTooBig(Image $constraint) + public function testWidthTooBig() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(maxWidth: 1, maxWidthMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ width }}', '2') @@ -148,23 +153,26 @@ public function testWidthTooBig(Image $constraint) ->assertRaised(); } - public static function provideMaxWidthConstraints(): iterable + /** + * @group legacy + */ + public function testWidthTooBigDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'maxWidth' => 1, 'maxWidthMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(maxWidth: 1, maxWidthMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', '2') + ->setParameter('{{ max_width }}', '1') + ->setCode(Image::TOO_WIDE_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMinHeightConstraints - */ - public function testHeightTooSmall(Image $constraint) + public function testHeightTooSmall() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(minHeight: 3, minHeightMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ height }}', '2') @@ -173,23 +181,26 @@ public function testHeightTooSmall(Image $constraint) ->assertRaised(); } - public static function provideMinHeightConstraints(): iterable + /** + * @group legacy + */ + public function testHeightTooSmallDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'minHeight' => 3, 'minHeightMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(minHeight: 3, minHeightMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ min_height }}', '3') + ->setCode(Image::TOO_LOW_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMaxHeightConstraints - */ - public function testHeightTooBig(Image $constraint) + public function testHeightTooBig() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(maxHeight: 1, maxHeightMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ height }}', '2') @@ -198,23 +209,26 @@ public function testHeightTooBig(Image $constraint) ->assertRaised(); } - public static function provideMaxHeightConstraints(): iterable + /** + * @group legacy + */ + public function testHeightTooBigDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'maxHeight' => 1, 'maxHeightMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(maxHeight: 1, maxHeightMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ max_height }}', '1') + ->setCode(Image::TOO_HIGH_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMinPixelsConstraints - */ - public function testPixelsTooFew(Image $constraint) + public function testPixelsTooFew() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(minPixels: 5, minPixelsMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ pixels }}', '4') @@ -225,23 +239,28 @@ public function testPixelsTooFew(Image $constraint) ->assertRaised(); } - public static function provideMinPixelsConstraints(): iterable + /** + * @group legacy + */ + public function testPixelsTooFewDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'minPixels' => 5, 'minPixelsMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(minPixels: 5, minPixelsMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ pixels }}', '4') + ->setParameter('{{ min_pixels }}', '5') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ width }}', '2') + ->setCode(Image::TOO_FEW_PIXEL_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMaxPixelsConstraints - */ - public function testPixelsTooMany(Image $constraint) + public function testPixelsTooMany() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(maxPixels: 3, maxPixelsMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ pixels }}', '4') @@ -252,23 +271,28 @@ public function testPixelsTooMany(Image $constraint) ->assertRaised(); } - public static function provideMaxPixelsConstraints(): iterable + /** + * @group legacy + */ + public function testPixelsTooManyDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'maxPixels' => 3, 'maxPixelsMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(maxPixels: 3, maxPixelsMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ pixels }}', '4') + ->setParameter('{{ max_pixels }}', '3') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ width }}', '2') + ->setCode(Image::TOO_MANY_PIXEL_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMinRatioConstraints - */ - public function testRatioTooSmall(Image $constraint) + public function testRatioTooSmall() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(minRatio: 2, minRatioMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ ratio }}', 1) @@ -277,23 +301,26 @@ public function testRatioTooSmall(Image $constraint) ->assertRaised(); } - public static function provideMinRatioConstraints(): iterable + /** + * @group legacy + */ + public function testRatioTooSmallDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'minRatio' => 2, 'minRatioMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(minRatio: 2, minRatioMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ ratio }}', 1) + ->setParameter('{{ min_ratio }}', 2) + ->setCode(Image::RATIO_TOO_SMALL_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideMaxRatioConstraints - */ - public function testRatioTooBig(Image $constraint) + public function testRatioTooBig() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(maxRatio: 0.5, maxRatioMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ ratio }}', 1) @@ -302,22 +329,26 @@ public function testRatioTooBig(Image $constraint) ->assertRaised(); } - public static function provideMaxRatioConstraints(): iterable + /** + * @group legacy + */ + public function testRatioTooBigDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'maxRatio' => 0.5, 'maxRatioMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(maxRatio: 0.5, maxRatioMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ ratio }}', 1) + ->setParameter('{{ max_ratio }}', 0.5) + ->setCode(Image::RATIO_TOO_BIG_ERROR) + ->assertRaised(); } public function testMaxRatioUsesTwoDecimalsOnly() { - $constraint = new Image([ - 'maxRatio' => 1.33, - ]); + $constraint = new Image(maxRatio: 1.33); $this->validator->validate($this->image4By3, $constraint); @@ -326,9 +357,7 @@ public function testMaxRatioUsesTwoDecimalsOnly() public function testMinRatioUsesInputMoreDecimals() { - $constraint = new Image([ - 'minRatio' => 4 / 3, - ]); + $constraint = new Image(minRatio: 4 / 3); $this->validator->validate($this->image4By3, $constraint); @@ -337,21 +366,16 @@ public function testMinRatioUsesInputMoreDecimals() public function testMaxRatioUsesInputMoreDecimals() { - $constraint = new Image([ - 'maxRatio' => 16 / 9, - ]); + $constraint = new Image(maxRatio: 16 / 9); $this->validator->validate($this->image16By9, $constraint); $this->assertNoViolation(); } - /** - * @dataProvider provideAllowSquareConstraints - */ - public function testSquareNotAllowed(Image $constraint) + public function testSquareNotAllowed() { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(allowSquare: false, allowSquareMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ width }}', 2) @@ -360,23 +384,26 @@ public function testSquareNotAllowed(Image $constraint) ->assertRaised(); } - public static function provideAllowSquareConstraints(): iterable + /** + * @group legacy + */ + public function testSquareNotAllowedDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'allowSquare' => false, 'allowSquareMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(allowSquare: false, allowSquareMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 2) + ->setParameter('{{ height }}', 2) + ->setCode(Image::SQUARE_NOT_ALLOWED_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideAllowLandscapeConstraints - */ - public function testLandscapeNotAllowed(Image $constraint) + public function testLandscapeNotAllowed() { - $this->validator->validate($this->imageLandscape, $constraint); + $this->validator->validate($this->imageLandscape, new Image(allowLandscape: false, allowLandscapeMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ width }}', 2) @@ -385,23 +412,26 @@ public function testLandscapeNotAllowed(Image $constraint) ->assertRaised(); } - public static function provideAllowLandscapeConstraints(): iterable + /** + * @group legacy + */ + public function testLandscapeNotAllowedDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->imageLandscape, new Image([ 'allowLandscape' => false, 'allowLandscapeMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(allowLandscape: false, allowLandscapeMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 2) + ->setParameter('{{ height }}', 1) + ->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR) + ->assertRaised(); } - /** - * @dataProvider provideAllowPortraitConstraints - */ - public function testPortraitNotAllowed(Image $constraint) + public function testPortraitNotAllowed() { - $this->validator->validate($this->imagePortrait, $constraint); + $this->validator->validate($this->imagePortrait, new Image(allowPortrait: false, allowPortraitMessage: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ width }}', 1) @@ -410,26 +440,56 @@ public function testPortraitNotAllowed(Image $constraint) ->assertRaised(); } - public static function provideAllowPortraitConstraints(): iterable + /** + * @group legacy + */ + public function testPortraitNotAllowedDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->imagePortrait, new Image([ 'allowPortrait' => false, 'allowPortraitMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(allowPortrait: false, allowPortraitMessage: 'myMessage'), - ]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 1) + ->setParameter('{{ height }}', 2) + ->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR) + ->assertRaised(); + } + + public function testCorrupted() + { + if (!\function_exists('imagecreatefromstring')) { + $this->markTestSkipped('This test require GD extension'); + } + + $constraint = new Image(detectCorrupted: true, corruptedMessage: 'myMessage'); + + $this->validator->validate($this->image, $constraint); + + $this->assertNoViolation(); + + $this->validator->validate($this->imageCorrupted, $constraint); + + $this->buildViolation('myMessage') + ->setCode(Image::CORRUPTED_IMAGE_ERROR) + ->assertRaised(); } /** - * @dataProvider provideDetectCorruptedConstraints + * @group legacy */ - public function testCorrupted(Image $constraint) + public function testCorruptedDoctrineStyle() { if (!\function_exists('imagecreatefromstring')) { $this->markTestSkipped('This test require GD extension'); } + $constraint = new Image([ + 'detectCorrupted' => true, + 'corruptedMessage' => 'myMessage', + ]); + $this->validator->validate($this->image, $constraint); $this->assertNoViolation(); @@ -456,23 +516,12 @@ public function testInvalidMimeType() ->assertRaised(); } - public static function provideDetectCorruptedConstraints(): iterable + public function testInvalidMimeTypeWithNarrowedSet() { - yield 'Doctrine style' => [new Image([ - 'detectCorrupted' => true, - 'corruptedMessage' => 'myMessage', - ])]; - yield 'Named arguments' => [ - new Image(detectCorrupted: true, corruptedMessage: 'myMessage'), - ]; - } - - /** - * @dataProvider provideInvalidMimeTypeWithNarrowedSet - */ - public function testInvalidMimeTypeWithNarrowedSet(Image $constraint) - { - $this->validator->validate($this->image, $constraint); + $this->validator->validate($this->image, new Image(mimeTypes: [ + 'image/jpeg', + 'image/png', + ])); $this->buildViolation('The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.') ->setParameter('{{ file }}', \sprintf('"%s"', $this->image)) @@ -483,20 +532,25 @@ public function testInvalidMimeTypeWithNarrowedSet(Image $constraint) ->assertRaised(); } - public static function provideInvalidMimeTypeWithNarrowedSet() + /** + * @group legacy + */ + public function testInvalidMimeTypeWithNarrowedSetDoctrineStyle() { - yield 'Doctrine style' => [new Image([ + $this->validator->validate($this->image, new Image([ 'mimeTypes' => [ 'image/jpeg', 'image/png', ], - ])]; - yield 'Named arguments' => [ - new Image(mimeTypes: [ - 'image/jpeg', - 'image/png', - ]), - ]; + ])); + + $this->buildViolation('The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.') + ->setParameter('{{ file }}', sprintf('"%s"', $this->image)) + ->setParameter('{{ type }}', '"image/gif"') + ->setParameter('{{ types }}', '"image/jpeg", "image/png"') + ->setParameter('{{ name }}', '"test.gif"') + ->setCode(Image::INVALID_MIME_TYPE_ERROR) + ->assertRaised(); } /** @dataProvider provideSvgWithViolation */ diff --git a/Tests/Constraints/IpTest.php b/Tests/Constraints/IpTest.php index 7f391153f..2d740ae88 100644 --- a/Tests/Constraints/IpTest.php +++ b/Tests/Constraints/IpTest.php @@ -24,11 +24,14 @@ class IpTest extends TestCase { public function testNormalizerCanBeSet() { - $ip = new Ip(['normalizer' => 'trim']); + $ip = new Ip(normalizer: 'trim'); $this->assertEquals('trim', $ip->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -36,6 +39,9 @@ public function testInvalidNormalizerThrowsException() new Ip(['normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/IpValidatorTest.php b/Tests/Constraints/IpValidatorTest.php index a2277a3d8..e37d61bb6 100644 --- a/Tests/Constraints/IpValidatorTest.php +++ b/Tests/Constraints/IpValidatorTest.php @@ -47,9 +47,7 @@ public function testExpectsStringCompatibleType() public function testInvalidValidatorVersion() { $this->expectException(ConstraintDefinitionException::class); - new Ip([ - 'version' => 666, - ]); + new Ip(version: 666); } /** @@ -57,9 +55,7 @@ public function testInvalidValidatorVersion() */ public function testValidIpsV4($ip) { - $this->validator->validate($ip, new Ip([ - 'version' => Ip::V4, - ])); + $this->validator->validate($ip, new Ip(version: Ip::V4)); $this->assertNoViolation(); } @@ -83,10 +79,10 @@ public static function getValidIpsV4() */ public function testValidIpsV4WithWhitespaces($ip) { - $this->validator->validate($ip, new Ip([ - 'version' => Ip::V4, - 'normalizer' => 'trim', - ])); + $this->validator->validate($ip, new Ip( + version: Ip::V4, + normalizer: 'trim', + )); $this->assertNoViolation(); } @@ -118,9 +114,7 @@ public static function getValidIpsV4WithWhitespaces() */ public function testValidIpsV6($ip) { - $this->validator->validate($ip, new Ip([ - 'version' => Ip::V6, - ])); + $this->validator->validate($ip, new Ip(version: Ip::V6)); $this->assertNoViolation(); } @@ -155,9 +149,7 @@ public static function getValidIpsV6() */ public function testValidIpsAll($ip) { - $this->validator->validate($ip, new Ip([ - 'version' => Ip::ALL, - ])); + $this->validator->validate($ip, new Ip(version: Ip::ALL)); $this->assertNoViolation(); } @@ -172,10 +164,10 @@ public static function getValidIpsAll() */ public function testInvalidIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -190,10 +182,10 @@ public function testInvalidIpsV4($ip) */ public function testInvalidNoPublicIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4_NO_PUBLIC, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4_NO_PUBLIC, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -232,9 +224,7 @@ public static function getInvalidIpsV4() */ public function testValidPrivateIpsV4($ip) { - $this->validator->validate($ip, new Ip([ - 'version' => Ip::V4_ONLY_PRIVATE, - ])); + $this->validator->validate($ip, new Ip(version: Ip::V4_ONLY_PRIVATE)); $this->assertNoViolation(); } @@ -244,10 +234,10 @@ public function testValidPrivateIpsV4($ip) */ public function testInvalidPrivateIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4_NO_PRIVATE, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4_NO_PRIVATE, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -262,10 +252,10 @@ public function testInvalidPrivateIpsV4($ip) */ public function testInvalidOnlyPrivateIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4_ONLY_PRIVATE, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4_ONLY_PRIVATE, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -294,9 +284,7 @@ public static function getInvalidPrivateIpsV4() */ public function testValidReservedIpsV4($ip) { - $this->validator->validate($ip, new Ip([ - 'version' => Ip::V4_ONLY_RESERVED, - ])); + $this->validator->validate($ip, new Ip(version: Ip::V4_ONLY_RESERVED)); $this->assertNoViolation(); } @@ -306,10 +294,10 @@ public function testValidReservedIpsV4($ip) */ public function testInvalidReservedIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4_NO_RESERVED, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4_NO_RESERVED, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -324,10 +312,10 @@ public function testInvalidReservedIpsV4($ip) */ public function testInvalidOnlyReservedIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4_ONLY_RESERVED, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4_ONLY_RESERVED, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -356,10 +344,10 @@ public static function getInvalidReservedIpsV4() */ public function testInvalidPublicIpsV4($ip) { - $constraint = new Ip([ - 'version' => Ip::V4_ONLY_PUBLIC, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V4_ONLY_PUBLIC, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -379,10 +367,10 @@ public static function getInvalidPublicIpsV4() */ public function testInvalidIpsV6($ip) { - $constraint = new Ip([ - 'version' => Ip::V6, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V6, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -416,10 +404,10 @@ public static function getInvalidIpsV6() */ public function testInvalidPrivateIpsV6($ip) { - $constraint = new Ip([ - 'version' => Ip::V6_NO_PRIVATE, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V6_NO_PRIVATE, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -443,10 +431,10 @@ public static function getInvalidPrivateIpsV6() */ public function testInvalidReservedIpsV6($ip) { - $constraint = new Ip([ - 'version' => Ip::V6_NO_RESERVED, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V6_NO_RESERVED, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -469,10 +457,10 @@ public static function getInvalidReservedIpsV6() */ public function testInvalidPublicIpsV6($ip) { - $constraint = new Ip([ - 'version' => Ip::V6_ONLY_PUBLIC, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::V6_ONLY_PUBLIC, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -492,10 +480,10 @@ public static function getInvalidPublicIpsV6() */ public function testInvalidIpsAll($ip) { - $constraint = new Ip([ - 'version' => Ip::ALL, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::ALL, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -515,10 +503,10 @@ public static function getInvalidIpsAll() */ public function testInvalidPrivateIpsAll($ip) { - $constraint = new Ip([ - 'version' => Ip::ALL_NO_PRIVATE, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::ALL_NO_PRIVATE, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -538,10 +526,10 @@ public static function getInvalidPrivateIpsAll() */ public function testInvalidReservedIpsAll($ip) { - $constraint = new Ip([ - 'version' => Ip::ALL_NO_RESERVED, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::ALL_NO_RESERVED, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); @@ -561,10 +549,10 @@ public static function getInvalidReservedIpsAll() */ public function testInvalidPublicIpsAll($ip) { - $constraint = new Ip([ - 'version' => Ip::ALL_ONLY_PUBLIC, - 'message' => 'myMessage', - ]); + $constraint = new Ip( + version: Ip::ALL_ONLY_PUBLIC, + message: 'myMessage', + ); $this->validator->validate($ip, $constraint); diff --git a/Tests/Constraints/IsFalseValidatorTest.php b/Tests/Constraints/IsFalseValidatorTest.php index e59764764..c6e2ccef6 100644 --- a/Tests/Constraints/IsFalseValidatorTest.php +++ b/Tests/Constraints/IsFalseValidatorTest.php @@ -36,12 +36,9 @@ public function testFalseIsValid() $this->assertNoViolation(); } - /** - * @dataProvider provideInvalidConstraints - */ - public function testTrueIsInvalid(IsFalse $constraint) + public function testTrueIsInvalid() { - $this->validator->validate(true, $constraint); + $this->validator->validate(true, new IsFalse(message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'true') @@ -49,11 +46,20 @@ public function testTrueIsInvalid(IsFalse $constraint) ->assertRaised(); } - public static function provideInvalidConstraints(): iterable + /** + * @group legacy + */ + public function testTrueIsInvalidDoctrineStyle() { - yield 'Doctrine style' => [new IsFalse([ + $constraint = new IsFalse([ 'message' => 'myMessage', - ])]; - yield 'named parameters' => [new IsFalse(message: 'myMessage')]; + ]); + + $this->validator->validate(true, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'true') + ->setCode(IsFalse::NOT_FALSE_ERROR) + ->assertRaised(); } } diff --git a/Tests/Constraints/IsNullValidatorTest.php b/Tests/Constraints/IsNullValidatorTest.php index f0ff58f34..ed6beffc4 100644 --- a/Tests/Constraints/IsNullValidatorTest.php +++ b/Tests/Constraints/IsNullValidatorTest.php @@ -34,9 +34,7 @@ public function testNullIsValid() */ public function testInvalidValues($value, $valueAsString) { - $constraint = new IsNull([ - 'message' => 'myMessage', - ]); + $constraint = new IsNull(message: 'myMessage'); $this->validator->validate($value, $constraint); diff --git a/Tests/Constraints/IsTrueValidatorTest.php b/Tests/Constraints/IsTrueValidatorTest.php index 1dc47f4b0..4a9eb7702 100644 --- a/Tests/Constraints/IsTrueValidatorTest.php +++ b/Tests/Constraints/IsTrueValidatorTest.php @@ -36,12 +36,9 @@ public function testTrueIsValid() $this->assertNoViolation(); } - /** - * @dataProvider provideInvalidConstraints - */ - public function testFalseIsInvalid(IsTrue $constraint) + public function testFalseIsInvalid() { - $this->validator->validate(false, $constraint); + $this->validator->validate(false, new IsTrue(message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'false') @@ -49,11 +46,18 @@ public function testFalseIsInvalid(IsTrue $constraint) ->assertRaised(); } - public static function provideInvalidConstraints(): iterable + /** + * @group legacy + */ + public function testFalseIsInvalidDoctrineStyle() { - yield 'Doctrine style' => [new IsTrue([ + $this->validator->validate(false, new IsTrue([ 'message' => 'myMessage', - ])]; - yield 'named parameters' => [new IsTrue(message: 'myMessage')]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'false') + ->setCode(IsTrue::NOT_TRUE_ERROR) + ->assertRaised(); } } diff --git a/Tests/Constraints/IsbnValidatorTest.php b/Tests/Constraints/IsbnValidatorTest.php index df985137c..9d2336f7f 100644 --- a/Tests/Constraints/IsbnValidatorTest.php +++ b/Tests/Constraints/IsbnValidatorTest.php @@ -150,9 +150,7 @@ public function testExpectsStringCompatibleType() */ public function testValidIsbn10($isbn) { - $constraint = new Isbn([ - 'type' => 'isbn10', - ]); + $constraint = new Isbn(type: 'isbn10'); $this->validator->validate($isbn, $constraint); @@ -160,6 +158,7 @@ public function testValidIsbn10($isbn) } /** + * @group legacy * @dataProvider getInvalidIsbn10 */ public function testInvalidIsbn10($isbn, $code) @@ -195,7 +194,7 @@ public function testInvalidIsbn10Named() */ public function testValidIsbn13($isbn) { - $constraint = new Isbn(['type' => 'isbn13']); + $constraint = new Isbn(type: 'isbn13'); $this->validator->validate($isbn, $constraint); @@ -203,6 +202,7 @@ public function testValidIsbn13($isbn) } /** + * @group legacy * @dataProvider getInvalidIsbn13 */ public function testInvalidIsbn13($isbn, $code) @@ -220,16 +220,21 @@ public function testInvalidIsbn13($isbn, $code) ->assertRaised(); } - public function testInvalidIsbn13Named() + /** + * @dataProvider getInvalidIsbn13 + */ + public function testInvalidIsbn13Named($isbn, $code) { - $this->validator->validate( - '2723442284', - new Isbn(type: Isbn::ISBN_13, isbn13Message: 'myMessage') + $constraint = new Isbn( + type: Isbn::ISBN_13, + isbn13Message: 'myMessage', ); + $this->validator->validate($isbn, $constraint); + $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"2723442284"') - ->setCode(Isbn::TOO_SHORT_ERROR) + ->setParameter('{{ value }}', '"'.$isbn.'"') + ->setCode($code) ->assertRaised(); } @@ -250,9 +255,7 @@ public function testValidIsbnAny($isbn) */ public function testInvalidIsbnAnyIsbn10($isbn, $code) { - $constraint = new Isbn([ - 'bothIsbnMessage' => 'myMessage', - ]); + $constraint = new Isbn(bothIsbnMessage: 'myMessage'); $this->validator->validate($isbn, $constraint); @@ -272,9 +275,7 @@ public function testInvalidIsbnAnyIsbn10($isbn, $code) */ public function testInvalidIsbnAnyIsbn13($isbn, $code) { - $constraint = new Isbn([ - 'bothIsbnMessage' => 'myMessage', - ]); + $constraint = new Isbn(bothIsbnMessage: 'myMessage'); $this->validator->validate($isbn, $constraint); diff --git a/Tests/Constraints/IsinValidatorTest.php b/Tests/Constraints/IsinValidatorTest.php index dca4a423f..b1ac3be20 100644 --- a/Tests/Constraints/IsinValidatorTest.php +++ b/Tests/Constraints/IsinValidatorTest.php @@ -130,9 +130,7 @@ public static function getIsinWithValidFormatButIncorrectChecksum() private function assertViolationRaised($isin, $code) { - $constraint = new Isin([ - 'message' => 'myMessage', - ]); + $constraint = new Isin(message: 'myMessage'); $this->validator->validate($isin, $constraint); diff --git a/Tests/Constraints/IssnValidatorTest.php b/Tests/Constraints/IssnValidatorTest.php index 9eece3eb9..6351ab620 100644 --- a/Tests/Constraints/IssnValidatorTest.php +++ b/Tests/Constraints/IssnValidatorTest.php @@ -119,10 +119,10 @@ public function testExpectsStringCompatibleType() */ public function testCaseSensitiveIssns($issn) { - $constraint = new Issn([ - 'caseSensitive' => true, - 'message' => 'myMessage', - ]); + $constraint = new Issn( + caseSensitive: true, + message: 'myMessage', + ); $this->validator->validate($issn, $constraint); @@ -137,10 +137,10 @@ public function testCaseSensitiveIssns($issn) */ public function testRequireHyphenIssns($issn) { - $constraint = new Issn([ - 'requireHyphen' => true, - 'message' => 'myMessage', - ]); + $constraint = new Issn( + requireHyphen: true, + message: 'myMessage', + ); $this->validator->validate($issn, $constraint); @@ -167,9 +167,7 @@ public function testValidIssn($issn) */ public function testInvalidIssn($issn, $code) { - $constraint = new Issn([ - 'message' => 'myMessage', - ]); + $constraint = new Issn(message: 'myMessage'); $this->validator->validate($issn, $constraint); diff --git a/Tests/Constraints/JsonValidatorTest.php b/Tests/Constraints/JsonValidatorTest.php index 92d8a20a7..123cb95fe 100644 --- a/Tests/Constraints/JsonValidatorTest.php +++ b/Tests/Constraints/JsonValidatorTest.php @@ -37,9 +37,7 @@ public function testJsonIsValid($value) */ public function testInvalidValues($value) { - $constraint = new Json([ - 'message' => 'myMessageTest', - ]); + $constraint = new Json(message: 'myMessageTest'); $this->validator->validate($value, $constraint); diff --git a/Tests/Constraints/LanguageValidatorTest.php b/Tests/Constraints/LanguageValidatorTest.php index 9abb9cfc4..d7c01a478 100644 --- a/Tests/Constraints/LanguageValidatorTest.php +++ b/Tests/Constraints/LanguageValidatorTest.php @@ -83,9 +83,7 @@ public static function getValidLanguages() */ public function testInvalidLanguages($language) { - $constraint = new Language([ - 'message' => 'myMessage', - ]); + $constraint = new Language(message: 'myMessage'); $this->validator->validate($language, $constraint); @@ -108,9 +106,7 @@ public static function getInvalidLanguages() */ public function testValidAlpha3Languages($language) { - $this->validator->validate($language, new Language([ - 'alpha3' => true, - ])); + $this->validator->validate($language, new Language(alpha3: true)); $this->assertNoViolation(); } @@ -129,10 +125,10 @@ public static function getValidAlpha3Languages() */ public function testInvalidAlpha3Languages($language) { - $constraint = new Language([ - 'alpha3' => true, - 'message' => 'myMessage', - ]); + $constraint = new Language( + alpha3: true, + message: 'myMessage', + ); $this->validator->validate($language, $constraint); @@ -172,9 +168,7 @@ public function testValidateUsingCountrySpecificLocale() \Locale::setDefault('fr_FR'); $existingLanguage = 'en'; - $this->validator->validate($existingLanguage, new Language([ - 'message' => 'aMessage', - ])); + $this->validator->validate($existingLanguage, new Language(message: 'aMessage')); $this->assertNoViolation(); } diff --git a/Tests/Constraints/LengthTest.php b/Tests/Constraints/LengthTest.php index 6af8a7bc1..6e292cb35 100644 --- a/Tests/Constraints/LengthTest.php +++ b/Tests/Constraints/LengthTest.php @@ -24,11 +24,18 @@ class LengthTest extends TestCase { public function testNormalizerCanBeSet() { - $length = new Length(['min' => 0, 'max' => 10, 'normalizer' => 'trim']); + $length = new Length( + min: 0, + max: 10, + normalizer: 'trim', + ); $this->assertEquals('trim', $length->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -36,6 +43,9 @@ public function testInvalidNormalizerThrowsException() new Length(['min' => 0, 'max' => 10, 'normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -45,13 +55,13 @@ public function testInvalidNormalizerObjectThrowsException() public function testDefaultCountUnitIsUsed() { - $length = new Length(['min' => 0, 'max' => 10]); + $length = new Length(min: 0, max: 10); $this->assertSame(Length::COUNT_CODEPOINTS, $length->countUnit); } public function testNonDefaultCountUnitCanBeSet() { - $length = new Length(['min' => 0, 'max' => 10, 'countUnit' => Length::COUNT_GRAPHEMES]); + $length = new Length(min: 0, max: 10, countUnit: Length::COUNT_GRAPHEMES); $this->assertSame(Length::COUNT_GRAPHEMES, $length->countUnit); } @@ -59,7 +69,7 @@ public function testInvalidCountUnitThrowsException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage(\sprintf('The "countUnit" option must be one of the "%s"::COUNT_* constants ("%s" given).', Length::class, 'nonExistentCountUnit')); - new Length(['min' => 0, 'max' => 10, 'countUnit' => 'nonExistentCountUnit']); + new Length(min: 0, max: 10, countUnit: 'nonExistentCountUnit'); } public function testConstraintDefaultOption() @@ -72,7 +82,7 @@ public function testConstraintDefaultOption() public function testConstraintAttributeDefaultOption() { - $constraint = new Length(['value' => 5, 'exactMessage' => 'message']); + $constraint = new Length(exactly: 5, exactMessage: 'message'); self::assertEquals(5, $constraint->min); self::assertEquals(5, $constraint->max); diff --git a/Tests/Constraints/LengthValidatorTest.php b/Tests/Constraints/LengthValidatorTest.php index 0afc9e6e1..0c228f25d 100644 --- a/Tests/Constraints/LengthValidatorTest.php +++ b/Tests/Constraints/LengthValidatorTest.php @@ -25,17 +25,17 @@ protected function createValidator(): LengthValidator public function testNullIsValid() { - $this->validator->validate(null, new Length(['value' => 6])); + $this->validator->validate(null, new Length(exactly: 6)); $this->assertNoViolation(); } public function testEmptyStringIsInvalid() { - $this->validator->validate('', new Length([ - 'value' => $limit = 6, - 'exactMessage' => 'myMessage', - ])); + $this->validator->validate('', new Length( + exactly: $limit = 6, + exactMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '""') @@ -50,7 +50,7 @@ public function testEmptyStringIsInvalid() public function testExpectsStringCompatibleType() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate(new \stdClass(), new Length(['value' => 5])); + $this->validator->validate(new \stdClass(), new Length(exactly: 5)); } public static function getThreeOrLessCharacters() @@ -118,7 +118,7 @@ public static function getThreeCharactersWithWhitespaces() */ public function testValidValuesMin(int|string $value) { - $constraint = new Length(['min' => 5]); + $constraint = new Length(min: 5); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -129,7 +129,7 @@ public function testValidValuesMin(int|string $value) */ public function testValidValuesMax(int|string $value) { - $constraint = new Length(['max' => 3]); + $constraint = new Length(max: 3); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -151,7 +151,7 @@ public function testValidValuesExact(int|string $value) */ public function testValidNormalizedValues($value) { - $constraint = new Length(['min' => 3, 'max' => 3, 'normalizer' => 'trim']); + $constraint = new Length(min: 3, max: 3, normalizer: 'trim'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -186,10 +186,10 @@ public function testValidBytesValues() */ public function testInvalidValuesMin(int|string $value, int $valueLength) { - $constraint = new Length([ - 'min' => 4, - 'minMessage' => 'myMessage', - ]); + $constraint = new Length( + min: 4, + minMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -227,10 +227,10 @@ public function testInvalidValuesMinNamed(int|string $value, int $valueLength) */ public function testInvalidValuesMax(int|string $value, int $valueLength) { - $constraint = new Length([ - 'max' => 4, - 'maxMessage' => 'myMessage', - ]); + $constraint = new Length( + max: 4, + maxMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -268,11 +268,11 @@ public function testInvalidValuesMaxNamed(int|string $value, int $valueLength) */ public function testInvalidValuesExactLessThanFour(int|string $value, int $valueLength) { - $constraint = new Length([ - 'min' => 4, - 'max' => 4, - 'exactMessage' => 'myMessage', - ]); + $constraint = new Length( + min: 4, + max: 4, + exactMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -310,11 +310,11 @@ public function testInvalidValuesExactLessThanFourNamed(int|string $value, int $ */ public function testInvalidValuesExactMoreThanFour(int|string $value, int $valueLength) { - $constraint = new Length([ - 'min' => 4, - 'max' => 4, - 'exactMessage' => 'myMessage', - ]); + $constraint = new Length( + min: 4, + max: 4, + exactMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -333,12 +333,12 @@ public function testInvalidValuesExactMoreThanFour(int|string $value, int $value */ public function testOneCharset($value, $charset, $isValid) { - $constraint = new Length([ - 'min' => 1, - 'max' => 1, - 'charset' => $charset, - 'charsetMessage' => 'myMessage', - ]); + $constraint = new Length( + min: 1, + max: 1, + charset: $charset, + charsetMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); diff --git a/Tests/Constraints/LessThanOrEqualValidatorTest.php b/Tests/Constraints/LessThanOrEqualValidatorTest.php index 4c0aa5349..9a84043ca 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): LessThanOrEqualValidator protected static function createConstraint(?array $options = null): Constraint { - return new LessThanOrEqual($options); + if (null !== $options) { + return new LessThanOrEqual(...$options); + } + + return new LessThanOrEqual(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php index 43bca5121..685bb58a6 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php @@ -59,6 +59,9 @@ public static function provideInvalidComparisons(): array ]; } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -67,6 +70,9 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new NegativeOrZero(['propertyPath' => 'field']); } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/LessThanValidatorTest.php b/Tests/Constraints/LessThanValidatorTest.php index c6918942d..da7f929cd 100644 --- a/Tests/Constraints/LessThanValidatorTest.php +++ b/Tests/Constraints/LessThanValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): LessThanValidator protected static function createConstraint(?array $options = null): Constraint { - return new LessThan($options); + if (null !== $options) { + return new LessThan(...$options); + } + + return new LessThan(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php index fa820c19b..5174a951d 100644 --- a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php +++ b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php @@ -58,6 +58,9 @@ public static function provideInvalidComparisons(): array ]; } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -66,6 +69,9 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new Negative(['propertyPath' => 'field']); } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/LocaleValidatorTest.php b/Tests/Constraints/LocaleValidatorTest.php index 1626eecef..a9429f188 100644 --- a/Tests/Constraints/LocaleValidatorTest.php +++ b/Tests/Constraints/LocaleValidatorTest.php @@ -71,9 +71,7 @@ public static function getValidLocales() */ public function testInvalidLocales($locale) { - $constraint = new Locale([ - 'message' => 'myMessage', - ]); + $constraint = new Locale(message: 'myMessage'); $this->validator->validate($locale, $constraint); @@ -96,9 +94,7 @@ public static function getInvalidLocales() */ public function testValidLocalesWithCanonicalization(string $locale) { - $constraint = new Locale([ - 'message' => 'myMessage', - ]); + $constraint = new Locale(message: 'myMessage'); $this->validator->validate($locale, $constraint); @@ -110,10 +106,10 @@ public function testValidLocalesWithCanonicalization(string $locale) */ public function testValidLocalesWithoutCanonicalization(string $locale) { - $constraint = new Locale([ - 'message' => 'myMessage', - 'canonicalize' => false, - ]); + $constraint = new Locale( + message: 'myMessage', + canonicalize: false, + ); $this->validator->validate($locale, $constraint); @@ -125,10 +121,10 @@ public function testValidLocalesWithoutCanonicalization(string $locale) */ public function testInvalidLocalesWithoutCanonicalization(string $locale) { - $constraint = new Locale([ - 'message' => 'myMessage', - 'canonicalize' => false, - ]); + $constraint = new Locale( + message: 'myMessage', + canonicalize: false, + ); $this->validator->validate($locale, $constraint); diff --git a/Tests/Constraints/LuhnValidatorTest.php b/Tests/Constraints/LuhnValidatorTest.php index b0571ebd0..9eb33bde6 100644 --- a/Tests/Constraints/LuhnValidatorTest.php +++ b/Tests/Constraints/LuhnValidatorTest.php @@ -76,9 +76,7 @@ public static function getValidNumbers() */ public function testInvalidNumbers($number, $code) { - $constraint = new Luhn([ - 'message' => 'myMessage', - ]); + $constraint = new Luhn(message: 'myMessage'); $this->validator->validate($number, $constraint); diff --git a/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php b/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php index d15e41660..c38a431f5 100644 --- a/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php +++ b/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php @@ -32,7 +32,7 @@ protected function createValidator(): NoSuspiciousCharactersValidator */ public function testNonSuspiciousStrings(string $string, array $options = []) { - $this->validator->validate($string, new NoSuspiciousCharacters($options)); + $this->validator->validate($string, new NoSuspiciousCharacters(...$options)); $this->assertNoViolation(); } @@ -58,7 +58,7 @@ public static function provideNonSuspiciousStrings(): iterable */ public function testSuspiciousStrings(string $string, array $options, array $errors) { - $this->validator->validate($string, new NoSuspiciousCharacters($options)); + $this->validator->validate($string, new NoSuspiciousCharacters(...$options)); $violations = null; diff --git a/Tests/Constraints/NotBlankTest.php b/Tests/Constraints/NotBlankTest.php index 77435a37a..d04a65f1c 100644 --- a/Tests/Constraints/NotBlankTest.php +++ b/Tests/Constraints/NotBlankTest.php @@ -24,7 +24,7 @@ class NotBlankTest extends TestCase { public function testNormalizerCanBeSet() { - $notBlank = new NotBlank(['normalizer' => 'trim']); + $notBlank = new NotBlank(normalizer: 'trim'); $this->assertEquals('trim', $notBlank->normalizer); } @@ -45,6 +45,9 @@ public function testAttributes() self::assertSame('myMessage', $bConstraint->message); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -52,6 +55,9 @@ public function testInvalidNormalizerThrowsException() new NotBlank(['normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/NotBlankValidatorTest.php b/Tests/Constraints/NotBlankValidatorTest.php index 8d1ba3d0f..42d5f3a60 100644 --- a/Tests/Constraints/NotBlankValidatorTest.php +++ b/Tests/Constraints/NotBlankValidatorTest.php @@ -45,9 +45,7 @@ public static function getValidValues() public function testNullIsInvalid() { - $constraint = new NotBlank([ - 'message' => 'myMessage', - ]); + $constraint = new NotBlank(message: 'myMessage'); $this->validator->validate(null, $constraint); @@ -59,9 +57,7 @@ public function testNullIsInvalid() public function testBlankIsInvalid() { - $constraint = new NotBlank([ - 'message' => 'myMessage', - ]); + $constraint = new NotBlank(message: 'myMessage'); $this->validator->validate('', $constraint); @@ -73,9 +69,7 @@ public function testBlankIsInvalid() public function testFalseIsInvalid() { - $constraint = new NotBlank([ - 'message' => 'myMessage', - ]); + $constraint = new NotBlank(message: 'myMessage'); $this->validator->validate(false, $constraint); @@ -87,9 +81,7 @@ public function testFalseIsInvalid() public function testEmptyArrayIsInvalid() { - $constraint = new NotBlank([ - 'message' => 'myMessage', - ]); + $constraint = new NotBlank(message: 'myMessage'); $this->validator->validate([], $constraint); @@ -101,10 +93,10 @@ public function testEmptyArrayIsInvalid() public function testAllowNullTrue() { - $constraint = new NotBlank([ - 'message' => 'myMessage', - 'allowNull' => true, - ]); + $constraint = new NotBlank( + message: 'myMessage', + allowNull: true, + ); $this->validator->validate(null, $constraint); $this->assertNoViolation(); @@ -112,10 +104,10 @@ public function testAllowNullTrue() public function testAllowNullFalse() { - $constraint = new NotBlank([ - 'message' => 'myMessage', - 'allowNull' => false, - ]); + $constraint = new NotBlank( + message: 'myMessage', + allowNull: false, + ); $this->validator->validate(null, $constraint); @@ -130,10 +122,10 @@ public function testAllowNullFalse() */ public function testNormalizedStringIsInvalid($value) { - $constraint = new NotBlank([ - 'message' => 'myMessage', - 'normalizer' => 'trim', - ]); + $constraint = new NotBlank( + message: 'myMessage', + normalizer: 'trim', + ); $this->validator->validate($value, $constraint); diff --git a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php index 3ff24eb94..11c325d53 100644 --- a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php +++ b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php @@ -87,7 +87,7 @@ public function testInvalidPassword() public function testThresholdReached() { - $constraint = new NotCompromisedPassword(['threshold' => 3]); + $constraint = new NotCompromisedPassword(threshold: 3); $this->validator->validate(self::PASSWORD_LEAKED, $constraint); $this->buildViolation($constraint->message) @@ -95,20 +95,21 @@ public function testThresholdReached() ->assertRaised(); } - /** - * @dataProvider provideConstraintsWithThreshold - */ - public function testThresholdNotReached(NotCompromisedPassword $constraint) + public function testThresholdNotReached() { - $this->validator->validate(self::PASSWORD_LEAKED, $constraint); + $this->validator->validate(self::PASSWORD_LEAKED, new NotCompromisedPassword(threshold: 10)); $this->assertNoViolation(); } - public static function provideConstraintsWithThreshold(): iterable + /** + * @group legacy + */ + public function testThresholdNotReachedDoctrineStyle() { - yield 'Doctrine style' => [new NotCompromisedPassword(['threshold' => 10])]; - yield 'named arguments' => [new NotCompromisedPassword(threshold: 10)]; + $this->validator->validate(self::PASSWORD_LEAKED, new NotCompromisedPassword(['threshold' => 10])); + + $this->assertNoViolation(); } public function testValidPassword() @@ -208,20 +209,21 @@ public function testApiError() $this->validator->validate(self::PASSWORD_TRIGGERING_AN_ERROR, new NotCompromisedPassword()); } - /** - * @dataProvider provideErrorSkippingConstraints - */ - public function testApiErrorSkipped(NotCompromisedPassword $constraint) + public function testApiErrorSkipped() { $this->expectNotToPerformAssertions(); - $this->validator->validate(self::PASSWORD_TRIGGERING_AN_ERROR, $constraint); + $this->validator->validate(self::PASSWORD_TRIGGERING_AN_ERROR, new NotCompromisedPassword(skipOnError: true)); } - public static function provideErrorSkippingConstraints(): iterable + /** + * @group legacy + */ + public function testApiErrorSkippedDoctrineStyle() { - yield 'Doctrine style' => [new NotCompromisedPassword(['skipOnError' => true])]; - yield 'named arguments' => [new NotCompromisedPassword(skipOnError: true)]; + $this->expectNotToPerformAssertions(); + + $this->validator->validate(self::PASSWORD_TRIGGERING_AN_ERROR, new NotCompromisedPassword(['skipOnError' => true])); } private function createHttpClientStub(?string $returnValue = null): HttpClientInterface diff --git a/Tests/Constraints/NotEqualToValidatorTest.php b/Tests/Constraints/NotEqualToValidatorTest.php index 52e25a8db..2f6948db9 100644 --- a/Tests/Constraints/NotEqualToValidatorTest.php +++ b/Tests/Constraints/NotEqualToValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): NotEqualToValidator protected static function createConstraint(?array $options = null): Constraint { - return new NotEqualTo($options); + if (null !== $options) { + return new NotEqualTo(...$options); + } + + return new NotEqualTo(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/NotIdenticalToValidatorTest.php b/Tests/Constraints/NotIdenticalToValidatorTest.php index 825a5ff9f..9831d26cb 100644 --- a/Tests/Constraints/NotIdenticalToValidatorTest.php +++ b/Tests/Constraints/NotIdenticalToValidatorTest.php @@ -34,7 +34,11 @@ protected function createValidator(): NotIdenticalToValidator protected static function createConstraint(?array $options = null): Constraint { - return new NotIdenticalTo($options); + if (null !== $options) { + return new NotIdenticalTo(...$options); + } + + return new NotIdenticalTo(); } protected function getErrorCode(): ?string diff --git a/Tests/Constraints/NotNullValidatorTest.php b/Tests/Constraints/NotNullValidatorTest.php index 82156e326..fec2ec12a 100644 --- a/Tests/Constraints/NotNullValidatorTest.php +++ b/Tests/Constraints/NotNullValidatorTest.php @@ -42,12 +42,9 @@ public static function getValidValues() ]; } - /** - * @dataProvider provideInvalidConstraints - */ - public function testNullIsInvalid(NotNull $constraint) + public function testNullIsInvalid() { - $this->validator->validate(null, $constraint); + $this->validator->validate(null, new NotNull(message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'null') @@ -55,11 +52,18 @@ public function testNullIsInvalid(NotNull $constraint) ->assertRaised(); } - public static function provideInvalidConstraints(): iterable + /** + * @group legacy + */ + public function testNullIsInvalidDoctrineStyle() { - yield 'Doctrine style' => [new NotNull([ + $this->validator->validate(null, new NotNull([ 'message' => 'myMessage', - ])]; - yield 'named parameters' => [new NotNull(message: 'myMessage')]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'null') + ->setCode(NotNull::IS_NULL_ERROR) + ->assertRaised(); } } diff --git a/Tests/Constraints/RangeTest.php b/Tests/Constraints/RangeTest.php index a306b104a..ae7e07077 100644 --- a/Tests/Constraints/RangeTest.php +++ b/Tests/Constraints/RangeTest.php @@ -18,6 +18,9 @@ class RangeTest extends TestCase { + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfBothMinLimitAndPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -35,6 +38,9 @@ public function testThrowsConstraintExceptionIfBothMinLimitAndPropertyPathNamed( new Range(min: 'min', minPropertyPath: 'minPropertyPath'); } + /** + * @group legacy + */ public function testThrowsConstraintExceptionIfBothMaxLimitAndPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -86,6 +92,9 @@ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMaxMess new Range(min: 'min', max: 'max', maxMessage: 'maxMessage'); } + /** + * @group legacy + */ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMessageAndMaxMessageOptions() { $this->expectException(ConstraintDefinitionException::class); @@ -98,6 +107,9 @@ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMess ]); } + /** + * @group legacy + */ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMessageOptions() { $this->expectException(ConstraintDefinitionException::class); @@ -109,6 +121,9 @@ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMess ]); } + /** + * @group legacy + */ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMaxMessageOptions() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/RangeValidatorTest.php b/Tests/Constraints/RangeValidatorTest.php index e0fff6f85..f8765af18 100644 --- a/Tests/Constraints/RangeValidatorTest.php +++ b/Tests/Constraints/RangeValidatorTest.php @@ -30,7 +30,7 @@ protected function createValidator(): RangeValidator public function testNullIsValid() { - $this->validator->validate(null, new Range(['min' => 10, 'max' => 20])); + $this->validator->validate(null, new Range(min: 10, max: 20)); $this->assertNoViolation(); } @@ -70,6 +70,7 @@ public static function getMoreThanTwenty(): array } /** + * @group legacy * @dataProvider getTenToTwenty */ public function testValidValuesMin($value) @@ -92,6 +93,7 @@ public function testValidValuesMinNamed($value) } /** + * @group legacy * @dataProvider getTenToTwenty */ public function testValidValuesMax($value) @@ -114,6 +116,7 @@ public function testValidValuesMaxNamed($value) } /** + * @group legacy * @dataProvider getTenToTwenty */ public function testValidValuesMinMax($value) @@ -136,6 +139,7 @@ public function testValidValuesMinMaxNamed($value) } /** + * @group legacy * @dataProvider getLessThanTen */ public function testInvalidValuesMin($value, $formattedValue) @@ -171,6 +175,7 @@ public function testInvalidValuesMinNamed($value, $formattedValue) } /** + * @group legacy * @dataProvider getMoreThanTwenty */ public function testInvalidValuesMax($value, $formattedValue) @@ -206,6 +211,7 @@ public function testInvalidValuesMaxNamed($value, $formattedValue) } /** + * @group legacy * @dataProvider getMoreThanTwenty */ public function testInvalidValuesCombinedMax($value, $formattedValue) @@ -244,6 +250,7 @@ public function testInvalidValuesCombinedMaxNamed($value, $formattedValue) } /** + * @group legacy * @dataProvider getLessThanTen */ public function testInvalidValuesCombinedMin($value, $formattedValue) @@ -345,7 +352,7 @@ public static function getLaterThanTwentiethMarch2014(): array */ public function testValidDatesMin($value) { - $constraint = new Range(['min' => 'March 10, 2014']); + $constraint = new Range(min: 'March 10, 2014'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -356,7 +363,7 @@ public function testValidDatesMin($value) */ public function testValidDatesMax($value) { - $constraint = new Range(['max' => 'March 20, 2014']); + $constraint = new Range(max: 'March 20, 2014'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -367,7 +374,7 @@ public function testValidDatesMax($value) */ public function testValidDatesMinMax($value) { - $constraint = new Range(['min' => 'March 10, 2014', 'max' => 'March 20, 2014']); + $constraint = new Range(min: 'March 10, 2014', max: 'March 20, 2014'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -382,10 +389,10 @@ public function testInvalidDatesMin(\DateTimeInterface $value, string $dateTimeA // Make sure we have the correct version loaded IntlTestHelper::requireIntl($this, '57.1'); - $constraint = new Range([ - 'min' => 'March 10, 2014', - 'minMessage' => 'myMessage', - ]); + $constraint = new Range( + min: 'March 10, 2014', + minMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -405,10 +412,10 @@ public function testInvalidDatesMax(\DateTimeInterface $value, string $dateTimeA // Make sure we have the correct version loaded IntlTestHelper::requireIntl($this, '57.1'); - $constraint = new Range([ - 'max' => 'March 20, 2014', - 'maxMessage' => 'myMessage', - ]); + $constraint = new Range( + max: 'March 20, 2014', + maxMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -428,11 +435,11 @@ public function testInvalidDatesCombinedMax(\DateTimeInterface $value, string $d // Make sure we have the correct version loaded IntlTestHelper::requireIntl($this, '57.1'); - $constraint = new Range([ - 'min' => 'March 10, 2014', - 'max' => 'March 20, 2014', - 'notInRangeMessage' => 'myNotInRangeMessage', - ]); + $constraint = new Range( + min: 'March 10, 2014', + max: 'March 20, 2014', + notInRangeMessage: 'myNotInRangeMessage', + ); $this->validator->validate($value, $constraint); @@ -453,11 +460,11 @@ public function testInvalidDatesCombinedMin($value, $dateTimeAsString) // Make sure we have the correct version loaded IntlTestHelper::requireIntl($this, '57.1'); - $constraint = new Range([ - 'min' => 'March 10, 2014', - 'max' => 'March 20, 2014', - 'notInRangeMessage' => 'myNotInRangeMessage', - ]); + $constraint = new Range( + min: 'March 10, 2014', + max: 'March 20, 2014', + notInRangeMessage: 'myNotInRangeMessage', + ); $this->validator->validate($value, $constraint); @@ -482,10 +489,10 @@ public function getInvalidValues(): array public function testNonNumeric() { - $constraint = new Range([ - 'min' => 10, - 'max' => 20, - ]); + $constraint = new Range( + min: 10, + max: 20, + ); $this->validator->validate('abcd', $constraint); @@ -497,9 +504,9 @@ public function testNonNumeric() public function testNonNumericWithParsableDatetimeMinAndMaxNull() { - $constraint = new Range([ - 'min' => 'March 10, 2014', - ]); + $constraint = new Range( + min: 'March 10, 2014', + ); $this->validator->validate('abcd', $constraint); @@ -511,9 +518,9 @@ public function testNonNumericWithParsableDatetimeMinAndMaxNull() public function testNonNumericWithParsableDatetimeMaxAndMinNull() { - $constraint = new Range([ - 'max' => 'March 20, 2014', - ]); + $constraint = new Range( + max: 'March 20, 2014', + ); $this->validator->validate('abcd', $constraint); @@ -525,10 +532,10 @@ public function testNonNumericWithParsableDatetimeMaxAndMinNull() public function testNonNumericWithParsableDatetimeMinAndMax() { - $constraint = new Range([ - 'min' => 'March 10, 2014', - 'max' => 'March 20, 2014', - ]); + $constraint = new Range( + min: 'March 10, 2014', + max: 'March 20, 2014', + ); $this->validator->validate('abcd', $constraint); @@ -540,10 +547,10 @@ public function testNonNumericWithParsableDatetimeMinAndMax() public function testNonNumericWithNonParsableDatetimeMin() { - $constraint = new Range([ - 'min' => 'March 40, 2014', - 'max' => 'March 20, 2014', - ]); + $constraint = new Range( + min: 'March 40, 2014', + max: 'March 20, 2014', + ); $this->validator->validate('abcd', $constraint); @@ -555,10 +562,10 @@ public function testNonNumericWithNonParsableDatetimeMin() public function testNonNumericWithNonParsableDatetimeMax() { - $constraint = new Range([ - 'min' => 'March 10, 2014', - 'max' => 'March 50, 2014', - ]); + $constraint = new Range( + min: 'March 10, 2014', + max: 'March 50, 2014', + ); $this->validator->validate('abcd', $constraint); @@ -570,10 +577,10 @@ public function testNonNumericWithNonParsableDatetimeMax() public function testNonNumericWithNonParsableDatetimeMinAndMax() { - $constraint = new Range([ - 'min' => 'March 40, 2014', - 'max' => 'March 50, 2014', - ]); + $constraint = new Range( + min: 'March 40, 2014', + max: 'March 50, 2014', + ); $this->validator->validate('abcd', $constraint); @@ -591,10 +598,10 @@ public function testThrowsOnInvalidStringDates($expectedMessage, $value, $min, $ $this->expectException(ConstraintDefinitionException::class); $this->expectExceptionMessage($expectedMessage); - $this->validator->validate($value, new Range([ - 'min' => $min, - 'max' => $max, - ])); + $this->validator->validate($value, new Range( + min: $min, + max: $max, + )); } public static function throwsOnInvalidStringDatesProvider(): array @@ -612,15 +619,16 @@ public function testNoViolationOnNullObjectWithPropertyPaths() { $this->setObject(null); - $this->validator->validate(1, new Range([ - 'minPropertyPath' => 'minPropertyPath', - 'maxPropertyPath' => 'maxPropertyPath', - ])); + $this->validator->validate(1, new Range( + minPropertyPath: 'minPropertyPath', + maxPropertyPath: 'maxPropertyPath', + )); $this->assertNoViolation(); } /** + * @group legacy * @dataProvider getTenToTwenty */ public function testValidValuesMinPropertyPath($value) @@ -653,9 +661,9 @@ public function testValidValuesMaxPropertyPath($value) { $this->setObject(new Limit(20)); - $this->validator->validate($value, new Range([ - 'maxPropertyPath' => 'value', - ])); + $this->validator->validate($value, new Range( + maxPropertyPath: 'value', + )); $this->assertNoViolation(); } @@ -679,10 +687,10 @@ public function testValidValuesMinMaxPropertyPath($value) { $this->setObject(new MinMax(10, 20)); - $this->validator->validate($value, new Range([ - 'minPropertyPath' => 'min', - 'maxPropertyPath' => 'max', - ])); + $this->validator->validate($value, new Range( + minPropertyPath: 'min', + maxPropertyPath: 'max', + )); $this->assertNoViolation(); } @@ -694,10 +702,10 @@ public function testInvalidValuesMinPropertyPath($value, $formattedValue) { $this->setObject(new Limit(10)); - $constraint = new Range([ - 'minPropertyPath' => 'value', - 'minMessage' => 'myMessage', - ]); + $constraint = new Range( + minPropertyPath: 'value', + minMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -716,10 +724,10 @@ public function testInvalidValuesMaxPropertyPath($value, $formattedValue) { $this->setObject(new Limit(20)); - $constraint = new Range([ - 'maxPropertyPath' => 'value', - 'maxMessage' => 'myMessage', - ]); + $constraint = new Range( + maxPropertyPath: 'value', + maxMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -732,6 +740,7 @@ public function testInvalidValuesMaxPropertyPath($value, $formattedValue) } /** + * @group legacy * @dataProvider getMoreThanTwenty */ public function testInvalidValuesCombinedMaxPropertyPath($value, $formattedValue) @@ -782,6 +791,7 @@ public function testInvalidValuesCombinedMaxPropertyPathNamed($value, $formatted } /** + * @group legacy * @dataProvider getLessThanTen */ public function testInvalidValuesCombinedMinPropertyPath($value, $formattedValue) @@ -838,11 +848,11 @@ public function testViolationOnNullObjectWithDefinedMin($value, $formattedValue) { $this->setObject(null); - $this->validator->validate($value, new Range([ - 'min' => 10, - 'maxPropertyPath' => 'max', - 'minMessage' => 'myMessage', - ])); + $this->validator->validate($value, new Range( + min: 10, + maxPropertyPath: 'max', + minMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', $formattedValue) @@ -859,11 +869,11 @@ public function testViolationOnNullObjectWithDefinedMax($value, $formattedValue) { $this->setObject(null); - $this->validator->validate($value, new Range([ - 'minPropertyPath' => 'min', - 'max' => 20, - 'maxMessage' => 'myMessage', - ])); + $this->validator->validate($value, new Range( + minPropertyPath: 'min', + max: 20, + maxMessage: 'myMessage', + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', $formattedValue) @@ -880,7 +890,7 @@ public function testValidDatesMinPropertyPath($value) { $this->setObject(new Limit('March 10, 2014')); - $this->validator->validate($value, new Range(['minPropertyPath' => 'value'])); + $this->validator->validate($value, new Range(minPropertyPath: 'value')); $this->assertNoViolation(); } @@ -892,7 +902,7 @@ public function testValidDatesMaxPropertyPath($value) { $this->setObject(new Limit('March 20, 2014')); - $constraint = new Range(['maxPropertyPath' => 'value']); + $constraint = new Range(maxPropertyPath: 'value'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -905,7 +915,7 @@ public function testValidDatesMinMaxPropertyPath($value) { $this->setObject(new MinMax('March 10, 2014', 'March 20, 2014')); - $constraint = new Range(['minPropertyPath' => 'min', 'maxPropertyPath' => 'max']); + $constraint = new Range(minPropertyPath: 'min', maxPropertyPath: 'max'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); @@ -922,10 +932,10 @@ public function testInvalidDatesMinPropertyPath($value, $dateTimeAsString) $this->setObject(new Limit('March 10, 2014')); - $constraint = new Range([ - 'minPropertyPath' => 'value', - 'minMessage' => 'myMessage', - ]); + $constraint = new Range( + minPropertyPath: 'value', + minMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -948,10 +958,10 @@ public function testInvalidDatesMaxPropertyPath($value, $dateTimeAsString) $this->setObject(new Limit('March 20, 2014')); - $constraint = new Range([ - 'maxPropertyPath' => 'value', - 'maxMessage' => 'myMessage', - ]); + $constraint = new Range( + maxPropertyPath: 'value', + maxMessage: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -974,11 +984,11 @@ public function testInvalidDatesCombinedMaxPropertyPath($value, $dateTimeAsStrin $this->setObject(new MinMax('March 10, 2014', 'March 20, 2014')); - $constraint = new Range([ - 'minPropertyPath' => 'min', - 'maxPropertyPath' => 'max', - 'notInRangeMessage' => 'myNotInRangeMessage', - ]); + $constraint = new Range( + minPropertyPath: 'min', + maxPropertyPath: 'max', + notInRangeMessage: 'myNotInRangeMessage', + ); $this->validator->validate($value, $constraint); @@ -1003,11 +1013,11 @@ public function testInvalidDatesCombinedMinPropertyPath($value, $dateTimeAsStrin $this->setObject(new MinMax('March 10, 2014', 'March 20, 2014')); - $constraint = new Range([ - 'minPropertyPath' => 'min', - 'maxPropertyPath' => 'max', - 'notInRangeMessage' => 'myNotInRangeMessage', - ]); + $constraint = new Range( + minPropertyPath: 'min', + maxPropertyPath: 'max', + notInRangeMessage: 'myNotInRangeMessage', + ); $this->validator->validate($value, $constraint); @@ -1027,7 +1037,7 @@ public function testMinPropertyPathReferencingUninitializedProperty() $object->max = 5; $this->setObject($object); - $this->validator->validate(5, new Range(['minPropertyPath' => 'min', 'maxPropertyPath' => 'max'])); + $this->validator->validate(5, new Range(minPropertyPath: 'min', maxPropertyPath: 'max')); $this->assertNoViolation(); } @@ -1038,14 +1048,14 @@ public function testMaxPropertyPathReferencingUninitializedProperty() $object->min = 5; $this->setObject($object); - $this->validator->validate(5, new Range(['minPropertyPath' => 'min', 'maxPropertyPath' => 'max'])); + $this->validator->validate(5, new Range(minPropertyPath: 'min', maxPropertyPath: 'max')); $this->assertNoViolation(); } public static function provideMessageIfMinAndMaxSet(): array { - $notInRangeMessage = (new Range(['min' => '']))->notInRangeMessage; + $notInRangeMessage = (new Range(min: ''))->notInRangeMessage; return [ [ @@ -1068,7 +1078,7 @@ public static function provideMessageIfMinAndMaxSet(): array */ public function testMessageIfMinAndMaxSet(array $constraintExtraOptions, int $value, string $expectedMessage, string $expectedCode) { - $constraint = new Range(array_merge(['min' => 1, 'max' => 10], $constraintExtraOptions)); + $constraint = new Range(...array_merge(['min' => 1, 'max' => 10], $constraintExtraOptions)); $this->validator->validate($value, $constraint); $this diff --git a/Tests/Constraints/RegexTest.php b/Tests/Constraints/RegexTest.php index ba24fb0cb..96b5d5fc4 100644 --- a/Tests/Constraints/RegexTest.php +++ b/Tests/Constraints/RegexTest.php @@ -69,10 +69,10 @@ public static function provideHtmlPatterns() */ public function testGetHtmlPattern($pattern, $htmlPattern, $match = true) { - $constraint = new Regex([ - 'pattern' => $pattern, - 'match' => $match, - ]); + $constraint = new Regex( + pattern: $pattern, + match: $match, + ); $this->assertSame($pattern, $constraint->pattern); $this->assertSame($htmlPattern, $constraint->getHtmlPattern()); @@ -80,10 +80,10 @@ public function testGetHtmlPattern($pattern, $htmlPattern, $match = true) public function testGetCustomHtmlPattern() { - $constraint = new Regex([ - 'pattern' => '((?![0-9]$|[a-z]+).)*', - 'htmlPattern' => 'foobar', - ]); + $constraint = new Regex( + pattern: '((?![0-9]$|[a-z]+).)*', + htmlPattern: 'foobar', + ); $this->assertSame('((?![0-9]$|[a-z]+).)*', $constraint->pattern); $this->assertSame('foobar', $constraint->getHtmlPattern()); @@ -91,11 +91,14 @@ public function testGetCustomHtmlPattern() public function testNormalizerCanBeSet() { - $regex = new Regex(['pattern' => '/^[0-9]+$/', 'normalizer' => 'trim']); + $regex = new Regex(pattern: '/^[0-9]+$/', normalizer: 'trim'); $this->assertEquals('trim', $regex->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -103,6 +106,9 @@ public function testInvalidNormalizerThrowsException() new Regex(['pattern' => '/^[0-9]+$/', 'normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/RegexValidatorTest.php b/Tests/Constraints/RegexValidatorTest.php index 82739f0e3..576df3748 100644 --- a/Tests/Constraints/RegexValidatorTest.php +++ b/Tests/Constraints/RegexValidatorTest.php @@ -25,14 +25,14 @@ protected function createValidator(): RegexValidator public function testNullIsValid() { - $this->validator->validate(null, new Regex(['pattern' => '/^[0-9]+$/'])); + $this->validator->validate(null, new Regex(pattern: '/^[0-9]+$/')); $this->assertNoViolation(); } public function testEmptyStringIsValid() { - $this->validator->validate('', new Regex(['pattern' => '/^[0-9]+$/'])); + $this->validator->validate('', new Regex(pattern: '/^[0-9]+$/')); $this->assertNoViolation(); } @@ -40,7 +40,7 @@ public function testEmptyStringIsValid() public function testExpectsStringCompatibleType() { $this->expectException(UnexpectedValueException::class); - $this->validator->validate(new \stdClass(), new Regex(['pattern' => '/^[0-9]+$/'])); + $this->validator->validate(new \stdClass(), new Regex(pattern: '/^[0-9]+$/')); } /** @@ -48,13 +48,14 @@ public function testExpectsStringCompatibleType() */ public function testValidValues($value) { - $constraint = new Regex(['pattern' => '/^[0-9]+$/']); + $constraint = new Regex(pattern: '/^[0-9]+$/'); $this->validator->validate($value, $constraint); $this->assertNoViolation(); } /** + * @group legacy * @dataProvider getValidValuesWithWhitespaces */ public function testValidValuesWithWhitespaces($value) @@ -105,6 +106,7 @@ public static function getValidValuesWithWhitespaces() } /** + * @group legacy * @dataProvider getInvalidValues */ public function testInvalidValues($value) diff --git a/Tests/Constraints/SequentiallyValidatorTest.php b/Tests/Constraints/SequentiallyValidatorTest.php index 657ff2637..4c8a48e10 100644 --- a/Tests/Constraints/SequentiallyValidatorTest.php +++ b/Tests/Constraints/SequentiallyValidatorTest.php @@ -33,7 +33,7 @@ public function testWalkThroughConstraints() { $constraints = [ new Type('number'), - new Range(['min' => 4]), + new Range(min: 4), ]; $value = 6; @@ -50,7 +50,7 @@ public function testStopsAtFirstConstraintWithViolations() { $constraints = [ new Type('string'), - new Regex(['pattern' => '[a-z]']), + new Regex(pattern: '[a-z]'), new NotEqualTo('Foo'), ]; @@ -68,20 +68,20 @@ public function testNestedConstraintsAreNotExecutedWhenGroupDoesNotMatch() { $validator = Validation::createValidator(); - $violations = $validator->validate(50, new Sequentially([ - 'constraints' => [ - new GreaterThan([ - 'groups' => 'senior', - 'value' => 55, - ]), - new Range([ - 'groups' => 'adult', - 'min' => 18, - 'max' => 55, - ]), + $violations = $validator->validate(50, new Sequentially( + constraints: [ + new GreaterThan( + groups: ['senior'], + value: 55, + ), + new Range( + groups: ['adult'], + min: 18, + max: 55, + ), ], - 'groups' => ['adult', 'senior'], - ]), 'adult'); + groups: ['adult', 'senior'], + ), 'adult'); $this->assertCount(0, $violations); } diff --git a/Tests/Constraints/TimeValidatorTest.php b/Tests/Constraints/TimeValidatorTest.php index 5d9027a17..7c1a9feb9 100644 --- a/Tests/Constraints/TimeValidatorTest.php +++ b/Tests/Constraints/TimeValidatorTest.php @@ -87,9 +87,7 @@ public static function getValidTimes() */ public function testValidTimesWithoutSeconds(string $time) { - $this->validator->validate($time, new Time([ - 'withSeconds' => false, - ])); + $this->validator->validate($time, new Time(withSeconds: false)); $this->assertNoViolation(); } @@ -143,9 +141,7 @@ public static function getInvalidTimesWithoutSeconds() */ public function testInvalidTimes($time, $code) { - $constraint = new Time([ - 'message' => 'myMessage', - ]); + $constraint = new Time(message: 'myMessage'); $this->validator->validate($time, $constraint); diff --git a/Tests/Constraints/TimezoneTest.php b/Tests/Constraints/TimezoneTest.php index 42a38a711..41fed2386 100644 --- a/Tests/Constraints/TimezoneTest.php +++ b/Tests/Constraints/TimezoneTest.php @@ -25,12 +25,12 @@ class TimezoneTest extends TestCase public function testValidTimezoneConstraints() { new Timezone(); - new Timezone(['zone' => \DateTimeZone::ALL]); + new Timezone(zone: \DateTimeZone::ALL); new Timezone(\DateTimeZone::ALL_WITH_BC); - new Timezone([ - 'zone' => \DateTimeZone::PER_COUNTRY, - 'countryCode' => 'AR', - ]); + new Timezone( + zone: \DateTimeZone::PER_COUNTRY, + countryCode: 'AR', + ); $this->addToAssertionCount(1); } @@ -38,16 +38,16 @@ public function testValidTimezoneConstraints() public function testExceptionForGroupedTimezonesByCountryWithWrongZone() { $this->expectException(ConstraintDefinitionException::class); - new Timezone([ - 'zone' => \DateTimeZone::ALL, - 'countryCode' => 'AR', - ]); + new Timezone( + zone: \DateTimeZone::ALL, + countryCode: 'AR', + ); } public function testExceptionForGroupedTimezonesByCountryWithoutZone() { $this->expectException(ConstraintDefinitionException::class); - new Timezone(['countryCode' => 'AR']); + new Timezone(countryCode: 'AR'); } /** @@ -56,7 +56,7 @@ public function testExceptionForGroupedTimezonesByCountryWithoutZone() public function testExceptionForInvalidGroupedTimezones(int $zone) { $this->expectException(ConstraintDefinitionException::class); - new Timezone(['zone' => $zone]); + new Timezone(zone: $zone); } public static function provideInvalidZones(): iterable diff --git a/Tests/Constraints/TimezoneValidatorTest.php b/Tests/Constraints/TimezoneValidatorTest.php index 25451c5d2..5595f872c 100644 --- a/Tests/Constraints/TimezoneValidatorTest.php +++ b/Tests/Constraints/TimezoneValidatorTest.php @@ -92,9 +92,7 @@ public static function getValidTimezones(): iterable */ public function testValidGroupedTimezones(string $timezone, int $zone) { - $constraint = new Timezone([ - 'zone' => $zone, - ]); + $constraint = new Timezone(zone: $zone); $this->validator->validate($timezone, $constraint); @@ -125,9 +123,7 @@ public static function getValidGroupedTimezones(): iterable */ public function testInvalidTimezoneWithoutZone(string $timezone) { - $constraint = new Timezone([ - 'message' => 'myMessage', - ]); + $constraint = new Timezone(message: 'myMessage'); $this->validator->validate($timezone, $constraint); @@ -150,10 +146,10 @@ public static function getInvalidTimezones(): iterable */ public function testInvalidGroupedTimezones(string $timezone, int $zone) { - $constraint = new Timezone([ - 'zone' => $zone, - 'message' => 'myMessage', - ]); + $constraint = new Timezone( + zone: $zone, + message: 'myMessage', + ); $this->validator->validate($timezone, $constraint); @@ -193,10 +189,10 @@ public function testInvalidGroupedTimezoneNamed() */ public function testValidGroupedTimezonesByCountry(string $timezone, string $country) { - $constraint = new Timezone([ - 'zone' => \DateTimeZone::PER_COUNTRY, - 'countryCode' => $country, - ]); + $constraint = new Timezone( + zone: \DateTimeZone::PER_COUNTRY, + countryCode: $country, + ); $this->validator->validate($timezone, $constraint); @@ -230,11 +226,11 @@ public static function getValidGroupedTimezonesByCountry(): iterable */ public function testInvalidGroupedTimezonesByCountry(string $timezone, string $countryCode) { - $constraint = new Timezone([ - 'message' => 'myMessage', - 'zone' => \DateTimeZone::PER_COUNTRY, - 'countryCode' => $countryCode, - ]); + $constraint = new Timezone( + message: 'myMessage', + zone: \DateTimeZone::PER_COUNTRY, + countryCode: $countryCode, + ); $this->validator->validate($timezone, $constraint); @@ -255,11 +251,11 @@ public static function getInvalidGroupedTimezonesByCountry(): iterable public function testGroupedTimezonesWithInvalidCountry() { - $constraint = new Timezone([ - 'message' => 'myMessage', - 'zone' => \DateTimeZone::PER_COUNTRY, - 'countryCode' => 'foobar', - ]); + $constraint = new Timezone( + message: 'myMessage', + zone: \DateTimeZone::PER_COUNTRY, + countryCode: 'foobar', + ); $this->validator->validate('Europe/Amsterdam', $constraint); @@ -286,9 +282,7 @@ public function testDeprecatedTimezonesAreValidWithBC(string $timezone) */ public function testDeprecatedTimezonesAreInvalidWithoutBC(string $timezone) { - $constraint = new Timezone([ - 'message' => 'myMessage', - ]); + $constraint = new Timezone(message: 'myMessage'); $this->validator->validate($timezone, $constraint); @@ -332,10 +326,10 @@ public function testIntlCompatibility() $this->markTestSkipped('"Europe/Saratov" is expired until 2017, current version is '.$tzDbVersion); } - $constraint = new Timezone([ - 'message' => 'myMessage', - 'intlCompatible' => true, - ]); + $constraint = new Timezone( + message: 'myMessage', + intlCompatible: true, + ); $this->validator->validate('Europe/Saratov', $constraint); diff --git a/Tests/Constraints/TypeValidatorTest.php b/Tests/Constraints/TypeValidatorTest.php index 99714407f..8e9e1aa3b 100644 --- a/Tests/Constraints/TypeValidatorTest.php +++ b/Tests/Constraints/TypeValidatorTest.php @@ -26,7 +26,7 @@ protected function createValidator(): TypeValidator public function testNullIsValid() { - $constraint = new Type(['type' => 'integer']); + $constraint = new Type(type: 'integer'); $this->validator->validate(null, $constraint); @@ -35,7 +35,7 @@ public function testNullIsValid() public function testEmptyIsValidIfString() { - $constraint = new Type(['type' => 'string']); + $constraint = new Type(type: 'string'); $this->validator->validate('', $constraint); @@ -44,10 +44,10 @@ public function testEmptyIsValidIfString() public function testEmptyIsInvalidIfNoString() { - $constraint = new Type([ - 'type' => 'integer', - 'message' => 'myMessage', - ]); + $constraint = new Type( + type: 'integer', + message: 'myMessage', + ); $this->validator->validate('', $constraint); @@ -63,7 +63,7 @@ public function testEmptyIsInvalidIfNoString() */ public function testValidValues($value, $type) { - $constraint = new Type(['type' => $type]); + $constraint = new Type(type: $type); $this->validator->validate($value, $constraint); @@ -123,10 +123,10 @@ public static function getValidValues() */ public function testInvalidValues($value, $type, $valueAsString) { - $constraint = new Type([ - 'type' => $type, - 'message' => 'myMessage', - ]); + $constraint = new Type( + type: $type, + message: 'myMessage', + ); $this->validator->validate($value, $constraint); @@ -195,7 +195,7 @@ public static function getInvalidValues() */ public function testValidValuesMultipleTypes($value, array $types) { - $constraint = new Type(['type' => $types]); + $constraint = new Type(type: $types); $this->validator->validate($value, $constraint); @@ -210,12 +210,9 @@ public static function getValidValuesMultipleTypes() ]; } - /** - * @dataProvider provideConstraintsWithMultipleTypes - */ - public function testInvalidValuesMultipleTypes(Type $constraint) + public function testInvalidValuesMultipleTypes() { - $this->validator->validate('12345', $constraint); + $this->validator->validate('12345', new Type(type: ['boolean', 'array'], message: 'myMessage')); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"12345"') @@ -224,13 +221,21 @@ public function testInvalidValuesMultipleTypes(Type $constraint) ->assertRaised(); } - public static function provideConstraintsWithMultipleTypes() + /** + * @group legacy + */ + public function testInvalidValuesMultipleTypesDoctrineStyle() { - yield 'Doctrine style' => [new Type([ + $this->validator->validate('12345', new Type([ 'type' => ['boolean', 'array'], 'message' => 'myMessage', - ])]; - yield 'named arguments' => [new Type(type: ['boolean', 'array'], message: 'myMessage')]; + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"12345"') + ->setParameter('{{ type }}', implode('|', ['boolean', 'array'])) + ->setCode(Type::INVALID_TYPE_ERROR) + ->assertRaised(); } protected static function createFile() diff --git a/Tests/Constraints/UlidValidatorTest.php b/Tests/Constraints/UlidValidatorTest.php index abacdfdc5..172ace189 100644 --- a/Tests/Constraints/UlidValidatorTest.php +++ b/Tests/Constraints/UlidValidatorTest.php @@ -72,9 +72,7 @@ public function testValidUlidAsRfc4122() */ public function testInvalidUlid(string $ulid, string $code) { - $constraint = new Ulid([ - 'message' => 'testMessage', - ]); + $constraint = new Ulid(message: 'testMessage'); $this->validator->validate($ulid, $constraint); diff --git a/Tests/Constraints/UniqueTest.php b/Tests/Constraints/UniqueTest.php index 7d882a9c3..9fe2599fd 100644 --- a/Tests/Constraints/UniqueTest.php +++ b/Tests/Constraints/UniqueTest.php @@ -37,6 +37,9 @@ public function testAttributes() self::assertSame('intval', $dConstraint->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -44,6 +47,9 @@ public function testInvalidNormalizerThrowsException() new Unique(['normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/UniqueValidatorTest.php b/Tests/Constraints/UniqueValidatorTest.php index f81621d65..ac43ed2b8 100644 --- a/Tests/Constraints/UniqueValidatorTest.php +++ b/Tests/Constraints/UniqueValidatorTest.php @@ -63,9 +63,7 @@ public static function getValidValues() */ public function testInvalidValues($value, $expectedMessageParam) { - $constraint = new Unique([ - 'message' => 'myMessage', - ]); + $constraint = new Unique(message: 'myMessage'); $this->validator->validate($value, $constraint); $this->buildViolation('myMessage') @@ -118,9 +116,7 @@ public function testExpectsUniqueObjects($callback) $value = [$object1, $object2, $object3]; - $this->validator->validate($value, new Unique([ - 'normalizer' => $callback, - ])); + $this->validator->validate($value, new Unique(normalizer: $callback)); $this->assertNoViolation(); } @@ -144,10 +140,10 @@ public function testExpectsNonUniqueObjects($callback) $value = [$object1, $object2, $object3]; - $this->validator->validate($value, new Unique([ - 'message' => 'myMessage', - 'normalizer' => $callback, - ])); + $this->validator->validate($value, new Unique( + message: 'myMessage', + normalizer: $callback, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') @@ -168,10 +164,10 @@ public static function getCallback(): array public function testExpectsInvalidNonStrictComparison() { - $this->validator->validate([1, '1', 1.0, '1.0'], new Unique([ - 'message' => 'myMessage', - 'normalizer' => 'intval', - ])); + $this->validator->validate([1, '1', 1.0, '1.0'], new Unique( + message: 'myMessage', + normalizer: 'intval', + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '1') @@ -183,9 +179,7 @@ public function testExpectsValidNonStrictComparison() { $callback = static fn ($item) => (int) $item; - $this->validator->validate([1, '2', 3, '4.0'], new Unique([ - 'normalizer' => $callback, - ])); + $this->validator->validate([1, '2', 3, '4.0'], new Unique(normalizer: $callback)); $this->assertNoViolation(); } @@ -194,10 +188,10 @@ public function testExpectsInvalidCaseInsensitiveComparison() { $callback = static fn ($item) => mb_strtolower($item); - $this->validator->validate(['Hello', 'hello', 'HELLO', 'hellO'], new Unique([ - 'message' => 'myMessage', - 'normalizer' => $callback, - ])); + $this->validator->validate(['Hello', 'hello', 'HELLO', 'hellO'], new Unique( + message: 'myMessage', + normalizer: $callback, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"hello"') @@ -209,9 +203,7 @@ public function testExpectsValidCaseInsensitiveComparison() { $callback = static fn ($item) => mb_strtolower($item); - $this->validator->validate(['Hello', 'World'], new Unique([ - 'normalizer' => $callback, - ])); + $this->validator->validate(['Hello', 'World'], new Unique(normalizer: $callback)); $this->assertNoViolation(); } @@ -248,9 +240,10 @@ public static function getInvalidFieldNames(): array */ public function testInvalidCollectionValues(array $value, array $fields, string $expectedMessageParam) { - $this->validator->validate($value, new Unique([ - 'message' => 'myMessage', - ], fields: $fields)); + $this->validator->validate($value, new Unique( + message: 'myMessage', + fields: $fields, + )); $this->buildViolation('myMessage') ->setParameter('{{ value }}', $expectedMessageParam) diff --git a/Tests/Constraints/UrlTest.php b/Tests/Constraints/UrlTest.php index 1d641aa92..8dd593d2e 100644 --- a/Tests/Constraints/UrlTest.php +++ b/Tests/Constraints/UrlTest.php @@ -24,11 +24,14 @@ class UrlTest extends TestCase { public function testNormalizerCanBeSet() { - $url = new Url(['normalizer' => 'trim', 'requireTld' => true]); + $url = new Url(normalizer: 'trim', requireTld: true); $this->assertEquals('trim', $url->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -36,6 +39,9 @@ public function testInvalidNormalizerThrowsException() new Url(['normalizer' => 'Unknown Callable', 'requireTld' => true]); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/UrlValidatorTest.php b/Tests/Constraints/UrlValidatorTest.php index 5fdbb28b6..b1322eac7 100644 --- a/Tests/Constraints/UrlValidatorTest.php +++ b/Tests/Constraints/UrlValidatorTest.php @@ -78,10 +78,10 @@ public function testValidUrlsWithNewLine($url) */ public function testValidUrlsWithWhitespaces($url) { - $this->validator->validate($url, new Url([ - 'normalizer' => 'trim', - 'requireTld' => true, - ])); + $this->validator->validate($url, new Url( + normalizer: 'trim', + requireTld: true, + )); $this->assertNoViolation(); } @@ -92,10 +92,10 @@ public function testValidUrlsWithWhitespaces($url) */ public function testValidRelativeUrl($url) { - $constraint = new Url([ - 'relativeProtocol' => true, - 'requireTld' => false, - ]); + $constraint = new Url( + relativeProtocol: true, + requireTld: false, + ); $this->validator->validate($url, $constraint); @@ -229,10 +229,10 @@ public static function getValidUrlsWithWhitespaces() */ public function testInvalidUrls($url) { - $constraint = new Url([ - 'message' => 'myMessage', - 'requireTld' => false, - ]); + $constraint = new Url( + message: 'myMessage', + requireTld: false, + ); $this->validator->validate($url, $constraint); @@ -248,11 +248,11 @@ public function testInvalidUrls($url) */ public function testInvalidRelativeUrl($url) { - $constraint = new Url([ - 'message' => 'myMessage', - 'relativeProtocol' => true, - 'requireTld' => false, - ]); + $constraint = new Url( + message: 'myMessage', + relativeProtocol: true, + requireTld: false, + ); $this->validator->validate($url, $constraint); @@ -331,10 +331,10 @@ public static function getInvalidUrls() */ public function testCustomProtocolIsValid($url, $requireTld) { - $constraint = new Url([ - 'protocols' => ['ftp', 'file', 'git'], - 'requireTld' => $requireTld, - ]); + $constraint = new Url( + protocols: ['ftp', 'file', 'git'], + requireTld: $requireTld, + ); $this->validator->validate($url, $constraint); @@ -355,9 +355,7 @@ public static function getValidCustomUrls() */ public function testRequiredTld(string $url, bool $requireTld, bool $isValid) { - $constraint = new Url([ - 'requireTld' => $requireTld, - ]); + $constraint = new Url(requireTld: $requireTld); $this->validator->validate($url, $constraint); diff --git a/Tests/Constraints/UuidTest.php b/Tests/Constraints/UuidTest.php index 3da8b8133..22901a9db 100644 --- a/Tests/Constraints/UuidTest.php +++ b/Tests/Constraints/UuidTest.php @@ -24,11 +24,14 @@ class UuidTest extends TestCase { public function testNormalizerCanBeSet() { - $uuid = new Uuid(['normalizer' => 'trim']); + $uuid = new Uuid(normalizer: 'trim'); $this->assertEquals('trim', $uuid->normalizer); } + /** + * @group legacy + */ public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -36,6 +39,9 @@ public function testInvalidNormalizerThrowsException() new Uuid(['normalizer' => 'Unknown Callable']); } + /** + * @group legacy + */ public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/UuidValidatorTest.php b/Tests/Constraints/UuidValidatorTest.php index 23bfe8383..84edc6612 100644 --- a/Tests/Constraints/UuidValidatorTest.php +++ b/Tests/Constraints/UuidValidatorTest.php @@ -93,7 +93,7 @@ public static function getValidStrictUuids() */ public function testValidStrictUuidsWithWhitespaces($uuid, $versions = null) { - $constraint = new Uuid(['normalizer' => 'trim']); + $constraint = new Uuid(normalizer: 'trim'); if (null !== $versions) { $constraint->versions = $versions; @@ -131,9 +131,7 @@ public function testValidStrictUuidWithWhitespacesNamed() */ public function testInvalidStrictUuids($uuid, $code, $versions = null) { - $constraint = new Uuid([ - 'message' => 'testMessage', - ]); + $constraint = new Uuid(message: 'testMessage'); if (null !== $versions) { $constraint->versions = $versions; @@ -195,9 +193,7 @@ public static function getInvalidStrictUuids() */ public function testValidNonStrictUuids($uuid) { - $constraint = new Uuid([ - 'strict' => false, - ]); + $constraint = new Uuid(strict: false); $this->validator->validate($uuid, $constraint); @@ -226,10 +222,10 @@ public static function getValidNonStrictUuids() */ public function testInvalidNonStrictUuids($uuid, $code) { - $constraint = new Uuid([ - 'strict' => false, - 'message' => 'myMessage', - ]); + $constraint = new Uuid( + strict: false, + message: 'myMessage', + ); $this->validator->validate($uuid, $constraint); @@ -270,9 +266,7 @@ public function testInvalidNonStrictUuidNamed() */ public function testTimeBasedUuid(string $uid, bool $expectedTimeBased) { - $constraint = new Uuid([ - 'versions' => Uuid::TIME_BASED_VERSIONS, - ]); + $constraint = new Uuid(versions: Uuid::TIME_BASED_VERSIONS); $this->validator->validate($uid, $constraint); diff --git a/Tests/Constraints/ValidTest.php b/Tests/Constraints/ValidTest.php index c56cdedd5..a862171f1 100644 --- a/Tests/Constraints/ValidTest.php +++ b/Tests/Constraints/ValidTest.php @@ -23,7 +23,7 @@ class ValidTest extends TestCase { public function testGroupsCanBeSet() { - $constraint = new Valid(['groups' => 'foo']); + $constraint = new Valid(groups: ['foo']); $this->assertSame(['foo'], $constraint->groups); } diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 7cfe13f2f..fa71de02e 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -24,6 +24,9 @@ final class WhenTest extends TestCase { + /** + * @group legacy + */ public function testMissingOptionsExceptionIsThrown() { $this->expectException(MissingOptionsException::class); @@ -51,10 +54,10 @@ public function testAttributes() self::assertInstanceOf(When::class, $classConstraint); self::assertSame('true', $classConstraint->expression); self::assertEquals([ - new Callback([ - 'callback' => 'callback', - 'groups' => ['Default', 'WhenTestWithAttributes'], - ]), + new Callback( + callback: 'callback', + groups: ['Default', 'WhenTestWithAttributes'], + ), ], $classConstraint->constraints); [$fooConstraint] = $metadata->properties['foo']->getConstraints(); @@ -62,12 +65,8 @@ public function testAttributes() self::assertInstanceOf(When::class, $fooConstraint); self::assertSame('true', $fooConstraint->expression); self::assertEquals([ - new NotNull([ - 'groups' => ['Default', 'WhenTestWithAttributes'], - ]), - new NotBlank([ - 'groups' => ['Default', 'WhenTestWithAttributes'], - ]), + new NotNull(groups: ['Default', 'WhenTestWithAttributes']), + new NotBlank(groups: ['Default', 'WhenTestWithAttributes']), ], $fooConstraint->constraints); self::assertSame(['Default', 'WhenTestWithAttributes'], $fooConstraint->groups); @@ -76,12 +75,8 @@ public function testAttributes() self::assertInstanceOf(When::class, $fooConstraint); self::assertSame('false', $barConstraint->expression); self::assertEquals([ - new NotNull([ - 'groups' => ['foo'], - ]), - new NotBlank([ - 'groups' => ['foo'], - ]), + new NotNull(groups: ['foo']), + new NotBlank(groups: ['foo']), ], $barConstraint->constraints); self::assertSame(['foo'], $barConstraint->groups); @@ -89,11 +84,7 @@ public function testAttributes() self::assertInstanceOf(When::class, $quxConstraint); self::assertSame('true', $quxConstraint->expression); - self::assertEquals([ - new NotNull([ - 'groups' => ['foo'], - ]), - ], $quxConstraint->constraints); + self::assertEquals([new NotNull(groups: ['foo'])], $quxConstraint->constraints); self::assertSame(['foo'], $quxConstraint->groups); [$bazConstraint] = $metadata->getters['baz']->getConstraints(); @@ -101,12 +92,8 @@ public function testAttributes() self::assertInstanceOf(When::class, $bazConstraint); self::assertSame('true', $bazConstraint->expression); self::assertEquals([ - new NotNull([ - 'groups' => ['Default', 'WhenTestWithAttributes'], - ]), - new NotBlank([ - 'groups' => ['Default', 'WhenTestWithAttributes'], - ]), + new NotNull(groups: ['Default', 'WhenTestWithAttributes']), + new NotBlank(groups: ['Default', 'WhenTestWithAttributes']), ], $bazConstraint->constraints); self::assertSame(['Default', 'WhenTestWithAttributes'], $bazConstraint->groups); } diff --git a/Tests/Constraints/WhenValidatorTest.php b/Tests/Constraints/WhenValidatorTest.php index f79d53031..019ec828f 100644 --- a/Tests/Constraints/WhenValidatorTest.php +++ b/Tests/Constraints/WhenValidatorTest.php @@ -33,10 +33,10 @@ public function testConstraintsAreExecuted() $this->expectValidateValue(0, 'Foo', $constraints); - $this->validator->validate('Foo', new When([ - 'expression' => 'true', - 'constraints' => $constraints, - ])); + $this->validator->validate('Foo', new When( + expression: 'true', + constraints: $constraints, + )); } public function testConstraintIsExecuted() @@ -44,10 +44,10 @@ public function testConstraintIsExecuted() $constraint = new NotNull(); $this->expectValidateValue(0, 'Foo', [$constraint]); - $this->validator->validate('Foo', new When([ - 'expression' => 'true', - 'constraints' => $constraint, - ])); + $this->validator->validate('Foo', new When( + expression: 'true', + constraints: $constraint, + )); } public function testConstraintsAreExecutedWithNull() @@ -58,10 +58,10 @@ public function testConstraintsAreExecutedWithNull() $this->expectValidateValue(0, null, $constraints); - $this->validator->validate(null, new When([ - 'expression' => 'true', - 'constraints' => $constraints, - ])); + $this->validator->validate(null, new When( + expression: 'true', + constraints: $constraints, + )); } public function testConstraintsAreExecutedWithObject() @@ -79,10 +79,10 @@ public function testConstraintsAreExecutedWithObject() $this->expectValidateValue(0, $number->value, $constraints); - $this->validator->validate($number->value, new When([ - 'expression' => 'this.type === "positive"', - 'constraints' => $constraints, - ])); + $this->validator->validate($number->value, new When( + expression: 'this.type === "positive"', + constraints: $constraints, + )); } public function testConstraintsAreExecutedWithNestedObject() @@ -104,10 +104,10 @@ public function testConstraintsAreExecutedWithNestedObject() $this->expectValidateValue(0, $number->value, $constraints); - $this->validator->validate($number->value, new When([ - 'expression' => 'context.getRoot().ok === true', - 'constraints' => $constraints, - ])); + $this->validator->validate($number->value, new When( + expression: 'context.getRoot().ok === true', + constraints: $constraints, + )); } public function testConstraintsAreExecutedWithValue() @@ -118,10 +118,10 @@ public function testConstraintsAreExecutedWithValue() $this->expectValidateValue(0, 'foo', $constraints); - $this->validator->validate('foo', new When([ - 'expression' => 'value === "foo"', - 'constraints' => $constraints, - ])); + $this->validator->validate('foo', new When( + expression: 'value === "foo"', + constraints: $constraints, + )); } public function testConstraintsAreExecutedWithExpressionValues() @@ -132,14 +132,14 @@ public function testConstraintsAreExecutedWithExpressionValues() $this->expectValidateValue(0, 'foo', $constraints); - $this->validator->validate('foo', new When([ - 'expression' => 'activated && value === compared_value', - 'constraints' => $constraints, - 'values' => [ + $this->validator->validate('foo', new When( + expression: 'activated && value === compared_value', + constraints: $constraints, + values: [ 'activated' => true, 'compared_value' => 'foo', ], - ])); + )); } public function testConstraintsNotExecuted() @@ -151,10 +151,10 @@ public function testConstraintsNotExecuted() $this->expectNoValidate(); - $this->validator->validate('', new When([ - 'expression' => 'false', - 'constraints' => $constraints, - ])); + $this->validator->validate('', new When( + expression: 'false', + constraints: $constraints, + )); $this->assertNoViolation(); } @@ -174,10 +174,10 @@ public function testConstraintsNotExecutedWithObject() $this->expectNoValidate(); - $this->validator->validate($number->value, new When([ - 'expression' => 'this.type !== "positive"', - 'constraints' => $constraints, - ])); + $this->validator->validate($number->value, new When( + expression: 'this.type !== "positive"', + constraints: $constraints, + )); $this->assertNoViolation(); } @@ -190,10 +190,10 @@ public function testConstraintsNotExecutedWithValue() $this->expectNoValidate(); - $this->validator->validate('foo', new When([ - 'expression' => 'value === null', - 'constraints' => $constraints, - ])); + $this->validator->validate('foo', new When( + expression: 'value === null', + constraints: $constraints, + )); $this->assertNoViolation(); } @@ -206,14 +206,14 @@ public function testConstraintsNotExecutedWithExpressionValues() $this->expectNoValidate(); - $this->validator->validate('foo', new When([ - 'expression' => 'activated && value === compared_value', - 'constraints' => $constraints, - 'values' => [ + $this->validator->validate('foo', new When( + expression: 'activated && value === compared_value', + constraints: $constraints, + values: [ 'activated' => true, 'compared_value' => 'bar', ], - ])); + )); $this->assertNoViolation(); } @@ -221,9 +221,7 @@ public function testConstraintsNotExecutedWithExpressionValues() public function testConstraintViolations() { $constraints = [ - new Blank([ - 'message' => 'my_message', - ]), + new Blank(message: 'my_message'), ]; $this->expectFailingValueValidation( 0, diff --git a/Tests/Fixtures/DummyCompoundConstraint.php b/Tests/Fixtures/DummyCompoundConstraint.php index 87253f25d..e4f7bafaa 100644 --- a/Tests/Fixtures/DummyCompoundConstraint.php +++ b/Tests/Fixtures/DummyCompoundConstraint.php @@ -22,7 +22,7 @@ protected function getConstraints(array $options): array { return [ new NotBlank(), - new Length(['max' => 3]), + new Length(max: 3), new Regex('/[a-z]+/'), new Regex('/[0-9]+/'), ]; diff --git a/Tests/Fixtures/DummyEntityConstraintWithoutNamedArguments.php b/Tests/Fixtures/DummyEntityConstraintWithoutNamedArguments.php new file mode 100644 index 000000000..880f73cae --- /dev/null +++ b/Tests/Fixtures/DummyEntityConstraintWithoutNamedArguments.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +class DummyEntityConstraintWithoutNamedArguments +{ +} diff --git a/Tests/Fixtures/EntityStaticCar.php b/Tests/Fixtures/EntityStaticCar.php index af90ddc74..3afaaf843 100644 --- a/Tests/Fixtures/EntityStaticCar.php +++ b/Tests/Fixtures/EntityStaticCar.php @@ -18,6 +18,6 @@ class EntityStaticCar extends EntityStaticVehicle { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('wheels', new Length(['max' => 99])); + $metadata->addPropertyConstraint('wheels', new Length(max: 99)); } } diff --git a/Tests/Fixtures/EntityStaticCarTurbo.php b/Tests/Fixtures/EntityStaticCarTurbo.php index d559074db..cb0efe281 100644 --- a/Tests/Fixtures/EntityStaticCarTurbo.php +++ b/Tests/Fixtures/EntityStaticCarTurbo.php @@ -18,6 +18,6 @@ class EntityStaticCarTurbo extends EntityStaticCar { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('wheels', new Length(['max' => 99])); + $metadata->addPropertyConstraint('wheels', new Length(max: 99)); } } diff --git a/Tests/Fixtures/EntityStaticVehicle.php b/Tests/Fixtures/EntityStaticVehicle.php index 1190318fa..429bffdeb 100644 --- a/Tests/Fixtures/EntityStaticVehicle.php +++ b/Tests/Fixtures/EntityStaticVehicle.php @@ -20,6 +20,6 @@ class EntityStaticVehicle public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('wheels', new Length(['max' => 99])); + $metadata->addPropertyConstraint('wheels', new Length(max: 99)); } } diff --git a/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index d2250114f..68f279ecf 100644 --- a/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -38,8 +38,8 @@ public function testLoadClassMetadataWithInterface() $metadata = $factory->getMetadataFor(self::PARENT_CLASS); $constraints = [ - new ConstraintA(['groups' => ['Default', 'EntityParent']]), - new ConstraintA(['groups' => ['Default', 'EntityInterfaceA', 'EntityParent']]), + new ConstraintA(groups: ['Default', 'EntityParent']), + new ConstraintA(groups: ['Default', 'EntityInterfaceA', 'EntityParent']), ]; $this->assertEquals($constraints, $metadata->getConstraints()); @@ -51,31 +51,31 @@ public function testMergeParentConstraints() $metadata = $factory->getMetadataFor(self::CLASS_NAME); $constraints = [ - new ConstraintA(['groups' => [ + new ConstraintA(groups: [ 'Default', 'Entity', - ]]), - new ConstraintA(['groups' => [ + ]), + new ConstraintA(groups: [ 'Default', 'EntityParent', 'Entity', - ]]), - new ConstraintA(['groups' => [ + ]), + new ConstraintA(groups: [ 'Default', 'EntityInterfaceA', 'EntityParent', 'Entity', - ]]), - new ConstraintA(['groups' => [ + ]), + new ConstraintA(groups: [ 'Default', 'EntityInterfaceB', 'Entity', - ]]), - new ConstraintA(['groups' => [ + ]), + new ConstraintA(groups: [ 'Default', 'EntityParentInterface', 'Entity', - ]]), + ]), ]; $this->assertEquals($constraints, $metadata->getConstraints()); @@ -87,8 +87,8 @@ public function testCachedMetadata() $factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache); $expectedConstraints = [ - new ConstraintA(['groups' => ['Default', 'EntityParent']]), - new ConstraintA(['groups' => ['Default', 'EntityInterfaceA', 'EntityParent']]), + new ConstraintA(groups: ['Default', 'EntityParent']), + new ConstraintA(groups: ['Default', 'EntityInterfaceA', 'EntityParent']), ]; $metadata = $factory->getMetadataFor(self::PARENT_CLASS); diff --git a/Tests/Mapping/Loader/AttributeLoaderTest.php b/Tests/Mapping/Loader/AttributeLoaderTest.php index ca025431c..9285117f9 100644 --- a/Tests/Mapping/Loader/AttributeLoaderTest.php +++ b/Tests/Mapping/Loader/AttributeLoaderTest.php @@ -66,29 +66,29 @@ public function testLoadClassMetadata() $expected->addConstraint(new Sequentially([ new Expression('this.getFirstName() != null'), ])); - $expected->addConstraint(new Callback(['callback' => 'validateMe', 'payload' => 'foo'])); + $expected->addConstraint(new Callback(callback: 'validateMe', payload: 'foo')); $expected->addConstraint(new Callback('validateMeStatic')); $expected->addPropertyConstraint('firstName', new NotNull()); - $expected->addPropertyConstraint('firstName', new Range(['min' => 3])); - $expected->addPropertyConstraint('firstName', new All([new NotNull(), new Range(['min' => 3])])); - $expected->addPropertyConstraint('firstName', new All(['constraints' => [new NotNull(), new Range(['min' => 3])]])); - $expected->addPropertyConstraint('firstName', new Collection([ - 'foo' => [new NotNull(), new Range(['min' => 3])], - 'bar' => new Range(['min' => 5]), + $expected->addPropertyConstraint('firstName', new Range(min: 3)); + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new Collection(fields: [ + 'foo' => [new NotNull(), new Range(min: 3)], + 'bar' => new Range(min: 5), 'baz' => new Required([new Email()]), 'qux' => new Optional([new NotBlank()]), - ], null, null, true)); - $expected->addPropertyConstraint('firstName', new Choice([ - 'message' => 'Must be one of %choices%', - 'choices' => ['A', 'B'], - ])); + ], allowExtraFields: true)); + $expected->addPropertyConstraint('firstName', new Choice( + message: 'Must be one of %choices%', + choices: ['A', 'B'], + )); $expected->addPropertyConstraint('firstName', new AtLeastOneOf([ new NotNull(), - new Range(['min' => 3]), + new Range(min: 3), ], null, null, 'foo', null, false)); $expected->addPropertyConstraint('firstName', new Sequentially([ new NotBlank(), - new Range(['min' => 5]), + new Range(min: 5), ])); $expected->addPropertyConstraint('childA', new Valid()); $expected->addPropertyConstraint('childB', new Valid()); @@ -152,29 +152,29 @@ public function testLoadClassMetadataAndMerge() $expected->addConstraint(new Sequentially([ new Expression('this.getFirstName() != null'), ])); - $expected->addConstraint(new Callback(['callback' => 'validateMe', 'payload' => 'foo'])); + $expected->addConstraint(new Callback(callback: 'validateMe', payload: 'foo')); $expected->addConstraint(new Callback('validateMeStatic')); $expected->addPropertyConstraint('firstName', new NotNull()); - $expected->addPropertyConstraint('firstName', new Range(['min' => 3])); - $expected->addPropertyConstraint('firstName', new All([new NotNull(), new Range(['min' => 3])])); - $expected->addPropertyConstraint('firstName', new All(['constraints' => [new NotNull(), new Range(['min' => 3])]])); - $expected->addPropertyConstraint('firstName', new Collection([ - 'foo' => [new NotNull(), new Range(['min' => 3])], - 'bar' => new Range(['min' => 5]), + $expected->addPropertyConstraint('firstName', new Range(min: 3)); + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new Collection(fields: [ + 'foo' => [new NotNull(), new Range(min: 3)], + 'bar' => new Range(min: 5), 'baz' => new Required([new Email()]), 'qux' => new Optional([new NotBlank()]), - ], null, null, true)); - $expected->addPropertyConstraint('firstName', new Choice([ - 'message' => 'Must be one of %choices%', - 'choices' => ['A', 'B'], - ])); + ], allowExtraFields: true)); + $expected->addPropertyConstraint('firstName', new Choice( + message: 'Must be one of %choices%', + choices: ['A', 'B'], + )); $expected->addPropertyConstraint('firstName', new AtLeastOneOf([ new NotNull(), - new Range(['min' => 3]), + new Range(min: 3), ], null, null, 'foo', null, false)); $expected->addPropertyConstraint('firstName', new Sequentially([ new NotBlank(), - new Range(['min' => 5]), + new Range(min: 5), ])); $expected->addPropertyConstraint('childA', new Valid()); $expected->addPropertyConstraint('childB', new Valid()); diff --git a/Tests/Mapping/Loader/Fixtures/ConstraintWithoutNamedArguments.php b/Tests/Mapping/Loader/Fixtures/ConstraintWithoutNamedArguments.php new file mode 100644 index 000000000..035a1a837 --- /dev/null +++ b/Tests/Mapping/Loader/Fixtures/ConstraintWithoutNamedArguments.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures; + +use Symfony\Component\Validator\Constraint; + +class ConstraintWithoutNamedArguments extends Constraint +{ + public function getTargets(): string + { + return self::CLASS_CONSTRAINT; + } +} diff --git a/Tests/Mapping/Loader/XmlFileLoaderTest.php b/Tests/Mapping/Loader/XmlFileLoaderTest.php index 2385dc888..bc8e4e72e 100644 --- a/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Choice; @@ -29,6 +30,7 @@ use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; use Symfony\Component\Validator\Tests\Fixtures\ConstraintB; use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithRequiredArgument; +use Symfony\Component\Validator\Tests\Fixtures\DummyEntityConstraintWithoutNamedArguments; use Symfony\Component\Validator\Tests\Fixtures\Entity_81; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\GroupSequenceProviderEntity; @@ -37,6 +39,8 @@ class XmlFileLoaderTest extends TestCase { + use ExpectUserDeprecationMessageTrait; + public function testLoadClassMetadataReturnsTrueIfSuccessful() { $loader = new XmlFileLoader(__DIR__.'/constraint-mapping.xml'); @@ -72,18 +76,18 @@ public function testLoadClassMetadata() $expected->addConstraint(new ConstraintWithNamedArguments(['foo', 'bar'])); $expected->addConstraint(new ConstraintWithoutValueWithNamedArguments(['foo'])); $expected->addPropertyConstraint('firstName', new NotNull()); - $expected->addPropertyConstraint('firstName', new Range(['min' => 3])); + $expected->addPropertyConstraint('firstName', new Range(min: 3)); $expected->addPropertyConstraint('firstName', new Choice(['A', 'B'])); - $expected->addPropertyConstraint('firstName', new All([new NotNull(), new Range(['min' => 3])])); - $expected->addPropertyConstraint('firstName', new All(['constraints' => [new NotNull(), new Range(['min' => 3])]])); - $expected->addPropertyConstraint('firstName', new Collection(['fields' => [ - 'foo' => [new NotNull(), new Range(['min' => 3])], - 'bar' => [new Range(['min' => 5])], - ]])); - $expected->addPropertyConstraint('firstName', new Choice([ - 'message' => 'Must be one of %choices%', - 'choices' => ['A', 'B'], + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new Collection(fields: [ + 'foo' => [new NotNull(), new Range(min: 3)], + 'bar' => [new Range(min: 5)], ])); + $expected->addPropertyConstraint('firstName', new Choice( + message: 'Must be one of %choices%', + choices: ['A', 'B'], + )); $expected->addGetterConstraint('lastName', new NotNull()); $expected->addGetterConstraint('valid', new IsTrue()); $expected->addGetterConstraint('permissions', new IsTrue()); @@ -99,7 +103,7 @@ public function testLoadClassMetadataWithNonStrings() $loader->loadClassMetadata($metadata); $expected = new ClassMetadata(Entity::class); - $expected->addPropertyConstraint('firstName', new Regex(['pattern' => '/^1/', 'match' => false])); + $expected->addPropertyConstraint('firstName', new Regex(pattern: '/^1/', match: false)); $properties = $metadata->getPropertyMetadata('firstName'); $constraints = $properties[0]->getConstraints(); @@ -171,4 +175,17 @@ public function testDoNotModifyStateIfExceptionIsThrown() $loader->loadClassMetadata($metadata); } } + + /** + * @group legacy + */ + public function testLoadConstraintWithoutNamedArgumentsSupport() + { + $loader = new XmlFileLoader(__DIR__.'/constraint-without-named-arguments-support.xml'); + $metadata = new ClassMetadata(DummyEntityConstraintWithoutNamedArguments::class); + + $this->expectUserDeprecationMessage('Since symfony/validator 7.2: Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments.'); + + $loader->loadClassMetadata($metadata); + } } diff --git a/Tests/Mapping/Loader/YamlFileLoaderTest.php b/Tests/Mapping/Loader/YamlFileLoaderTest.php index 75955c09f..b496663f1 100644 --- a/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Choice; @@ -26,6 +27,7 @@ use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; use Symfony\Component\Validator\Tests\Fixtures\ConstraintB; use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithRequiredArgument; +use Symfony\Component\Validator\Tests\Fixtures\DummyEntityConstraintWithoutNamedArguments; use Symfony\Component\Validator\Tests\Fixtures\Entity_81; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\GroupSequenceProviderEntity; @@ -34,6 +36,8 @@ class YamlFileLoaderTest extends TestCase { + use ExpectUserDeprecationMessageTrait; + public function testLoadClassMetadataReturnsFalseIfEmpty() { $loader = new YamlFileLoader(__DIR__.'/empty-mapping.yml'); @@ -116,18 +120,18 @@ public function testLoadClassMetadata() $expected->addConstraint(new ConstraintWithNamedArguments('foo')); $expected->addConstraint(new ConstraintWithNamedArguments(['foo', 'bar'])); $expected->addPropertyConstraint('firstName', new NotNull()); - $expected->addPropertyConstraint('firstName', new Range(['min' => 3])); + $expected->addPropertyConstraint('firstName', new Range(min: 3)); $expected->addPropertyConstraint('firstName', new Choice(['A', 'B'])); - $expected->addPropertyConstraint('firstName', new All([new NotNull(), new Range(['min' => 3])])); - $expected->addPropertyConstraint('firstName', new All(['constraints' => [new NotNull(), new Range(['min' => 3])]])); - $expected->addPropertyConstraint('firstName', new Collection(['fields' => [ - 'foo' => [new NotNull(), new Range(['min' => 3])], - 'bar' => [new Range(['min' => 5])], - ]])); - $expected->addPropertyConstraint('firstName', new Choice([ - 'message' => 'Must be one of %choices%', - 'choices' => ['A', 'B'], + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)])); + $expected->addPropertyConstraint('firstName', new Collection(fields: [ + 'foo' => [new NotNull(), new Range(min: 3)], + 'bar' => [new Range(min: 5)], ])); + $expected->addPropertyConstraint('firstName', new Choice( + message: 'Must be one of %choices%', + choices: ['A', 'B'], + )); $expected->addGetterConstraint('lastName', new NotNull()); $expected->addGetterConstraint('valid', new IsTrue()); $expected->addGetterConstraint('permissions', new IsTrue()); @@ -143,7 +147,7 @@ public function testLoadClassMetadataWithConstants() $loader->loadClassMetadata($metadata); $expected = new ClassMetadata(Entity::class); - $expected->addPropertyConstraint('firstName', new Range(['max' => \PHP_INT_MAX])); + $expected->addPropertyConstraint('firstName', new Range(max: \PHP_INT_MAX)); $this->assertEquals($expected, $metadata); } @@ -187,4 +191,17 @@ public function testLoadGroupProvider() $this->assertEquals($expected, $metadata); } + + /** + * @group legacy + */ + public function testLoadConstraintWithoutNamedArgumentsSupport() + { + $loader = new YamlFileLoader(__DIR__.'/constraint-without-named-arguments-support.yml'); + $metadata = new ClassMetadata(DummyEntityConstraintWithoutNamedArguments::class); + + $this->expectUserDeprecationMessage('Since symfony/validator 7.2: Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments.'); + + $loader->loadClassMetadata($metadata); + } } diff --git a/Tests/Mapping/Loader/constraint-without-named-arguments-support.xml b/Tests/Mapping/Loader/constraint-without-named-arguments-support.xml new file mode 100644 index 000000000..48321b174 --- /dev/null +++ b/Tests/Mapping/Loader/constraint-without-named-arguments-support.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/Tests/Mapping/Loader/constraint-without-named-arguments-support.yml b/Tests/Mapping/Loader/constraint-without-named-arguments-support.yml new file mode 100644 index 000000000..3e25b78e4 --- /dev/null +++ b/Tests/Mapping/Loader/constraint-without-named-arguments-support.yml @@ -0,0 +1,4 @@ +Symfony\Component\Validator\Tests\Fixtures\DummyEntityConstraintWithoutNamedArguments: + constraints: + - Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments: + groups: foo diff --git a/Tests/Mapping/MemberMetadataTest.php b/Tests/Mapping/MemberMetadataTest.php index cd429e676..84d047f10 100644 --- a/Tests/Mapping/MemberMetadataTest.php +++ b/Tests/Mapping/MemberMetadataTest.php @@ -84,7 +84,7 @@ public function testSerialize() public function testSerializeCollectionCascaded() { - $this->metadata->addConstraint(new Valid(['traverse' => true])); + $this->metadata->addConstraint(new Valid(traverse: true)); $metadata = unserialize(serialize($this->metadata)); @@ -93,7 +93,7 @@ public function testSerializeCollectionCascaded() public function testSerializeCollectionNotCascaded() { - $this->metadata->addConstraint(new Valid(['traverse' => false])); + $this->metadata->addConstraint(new Valid(traverse: false)); $metadata = unserialize(serialize($this->metadata)); diff --git a/Tests/Validator/RecursiveValidatorTest.php b/Tests/Validator/RecursiveValidatorTest.php index ee183a1bf..91449f72e 100644 --- a/Tests/Validator/RecursiveValidatorTest.php +++ b/Tests/Validator/RecursiveValidatorTest.php @@ -113,10 +113,10 @@ public function testValidate() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $constraint = new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ]); + $constraint = new Callback( + callback: $callback, + groups: ['Group'], + ); $violations = $this->validate('Bernhard', $constraint, 'Group'); @@ -149,10 +149,10 @@ public function testClassConstraint() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -188,10 +188,10 @@ public function testPropertyConstraint() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addPropertyConstraint('firstName', new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -227,10 +227,10 @@ public function testGetterConstraint() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addGetterConstraint('lastName', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addGetterConstraint('lastName', new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -264,10 +264,10 @@ public function testArray() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($array, null, 'Group'); @@ -301,10 +301,10 @@ public function testRecursiveArray() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($array, null, 'Group'); @@ -338,10 +338,10 @@ public function testTraversable() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($traversable, null, 'Group'); @@ -377,10 +377,10 @@ public function testRecursiveTraversable() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($traversable, null, 'Group'); @@ -415,10 +415,10 @@ public function testReferenceClassConstraint() }; $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -456,10 +456,10 @@ public function testReferencePropertyConstraint() }; $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addPropertyConstraint('value', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addPropertyConstraint('value', new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -497,10 +497,10 @@ public function testReferenceGetterConstraint() }; $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addPropertyConstraint('privateValue', new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addPropertyConstraint('privateValue', new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -563,10 +563,10 @@ public function testArrayReference($constraintMethod) }; $this->metadata->$constraintMethod('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -604,10 +604,10 @@ public function testRecursiveArrayReference($constraintMethod) }; $this->metadata->$constraintMethod('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -632,14 +632,14 @@ public function testOnlyCascadedArraysAreTraversed() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('reference', new Callback([ - 'callback' => function () {}, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addPropertyConstraint('reference', new Callback( + callback: function () {}, + groups: ['Group'], + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -659,9 +659,9 @@ public function testArrayTraversalCannotBeDisabled($constraintMethod) $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->$constraintMethod('reference', new Valid([ - 'traverse' => false, - ])); + $this->metadata->$constraintMethod('reference', new Valid( + traverse: false, + )); $this->referenceMetadata->addConstraint(new Callback($callback)); $violations = $this->validate($entity); @@ -682,9 +682,9 @@ public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod) $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->$constraintMethod('reference', new Valid([ - 'traverse' => false, - ])); + $this->metadata->$constraintMethod('reference', new Valid( + traverse: false, + )); $this->referenceMetadata->addConstraint(new Callback($callback)); @@ -745,10 +745,10 @@ public function testTraversableReference() }; $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -774,9 +774,9 @@ public function testDisableTraversableTraversal() }; $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => false, - ])); + $this->metadata->addPropertyConstraint('reference', new Valid( + traverse: false, + )); $this->referenceMetadata->addConstraint(new Callback($callback)); $violations = $this->validate($entity); @@ -790,9 +790,9 @@ public function testMetadataMustExistIfTraversalIsDisabled() $entity = new Entity(); $entity->reference = new \ArrayIterator(); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => false, - ])); + $this->metadata->addPropertyConstraint('reference', new Valid( + traverse: false, + )); $this->expectException(NoSuchMetadataException::class); @@ -819,13 +819,13 @@ public function testEnableRecursiveTraversableTraversal() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => true, - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addPropertyConstraint('reference', new Valid( + traverse: true, + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, null, 'Group'); @@ -866,14 +866,14 @@ public function testValidateProperty() $context->addViolation('Other violation'); }; - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('lastName', new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); + $this->metadata->addPropertyConstraint('firstName', new Callback( + callback: $callback1, + groups: ['Group'], + )); + $this->metadata->addPropertyConstraint('lastName', new Callback( + callback: $callback2, + groups: ['Group'], + )); $violations = $this->validateProperty($entity, 'firstName', 'Group'); @@ -924,14 +924,14 @@ public function testValidatePropertyValue() $context->addViolation('Other violation'); }; - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('lastName', new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); + $this->metadata->addPropertyConstraint('firstName', new Callback( + callback: $callback1, + groups: ['Group'], + )); + $this->metadata->addPropertyConstraint('lastName', new Callback( + callback: $callback2, + groups: ['Group'], + )); $violations = $this->validatePropertyValue( $entity, @@ -973,14 +973,14 @@ public function testValidatePropertyValueWithClassName() $context->addViolation('Other violation'); }; - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('lastName', new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); + $this->metadata->addPropertyConstraint('firstName', new Callback( + callback: $callback1, + groups: ['Group'], + )); + $this->metadata->addPropertyConstraint('lastName', new Callback( + callback: $callback2, + groups: ['Group'], + )); $violations = $this->validatePropertyValue( self::ENTITY_CLASS, @@ -1060,14 +1060,14 @@ public function testValidateSingleGroup() $context->addViolation('Message'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 2', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group 1'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group 2'], + )); $violations = $this->validate($entity, null, 'Group 2'); @@ -1083,14 +1083,14 @@ public function testValidateMultipleGroups() $context->addViolation('Message'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group 2', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group 1'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group 2'], + )); $violations = $this->validate($entity, null, ['Group 1', 'Group 2']); @@ -1109,18 +1109,18 @@ public function testReplaceDefaultGroupByGroupSequenceObject() $context->addViolation('Violation in Group 3'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); + $this->metadata->addConstraint(new Callback( + callback: function () {}, + groups: ['Group 1'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group 2'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 3'], + )); $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3', 'Entity']); $this->metadata->setGroupSequence($sequence); @@ -1143,18 +1143,18 @@ public function testReplaceDefaultGroupByGroupSequenceArray() $context->addViolation('Violation in Group 3'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); + $this->metadata->addConstraint(new Callback( + callback: function () {}, + groups: ['Group 1'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group 2'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 3'], + )); $sequence = ['Group 1', 'Group 2', 'Group 3', 'Entity']; $this->metadata->setGroupSequence($sequence); @@ -1179,14 +1179,14 @@ public function testPropagateDefaultGroupToReferenceWhenReplacingDefaultGroup() }; $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Default', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 1', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Default'], + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 1'], + )); $sequence = new GroupSequence(['Group 1', 'Entity']); $this->metadata->setGroupSequence($sequence); @@ -1209,14 +1209,14 @@ public function testValidateCustomGroupWhenDefaultGroupWasReplaced() $context->addViolation('Violation in group sequence'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Other Group', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 1', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Other Group'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 1'], + )); $sequence = new GroupSequence(['Group 1', 'Entity']); $this->metadata->setGroupSequence($sequence); @@ -1243,18 +1243,18 @@ public function testReplaceDefaultGroup($sequence, array $assertViolations) }; $metadata = new ClassMetadata($entity::class); - $metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); + $metadata->addConstraint(new Callback( + callback: function () {}, + groups: ['Group 1'], + )); + $metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group 2'], + )); + $metadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 3'], + )); $metadata->setGroupSequenceProvider(true); $this->metadataFactory->addMetadata($metadata); @@ -1348,18 +1348,18 @@ public function testGroupSequenceAbortsAfterFailedGroup() $context->addViolation('Message 2'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => function () {}, - 'groups' => 'Group 1', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 2', - ])); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 3', - ])); + $this->metadata->addConstraint(new Callback( + callback: function () {}, + groups: ['Group 1'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group 2'], + )); + $this->metadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 3'], + )); $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3']); $violations = $this->validator->validate($entity, new Valid(), $sequence); @@ -1382,14 +1382,14 @@ public function testGroupSequenceIncludesReferences() }; $this->metadata->addPropertyConstraint('reference', new Valid()); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group 1', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group 2', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group 1'], + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group 2'], + )); $sequence = new GroupSequence(['Group 1', 'Entity']); $violations = $this->validator->validate($entity, new Valid(), $sequence); @@ -1442,14 +1442,14 @@ public function testValidateInSeparateContext() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group'], + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group'], + )); $violations = $this->validator->validate($entity, new Valid(), 'Group'); @@ -1498,14 +1498,14 @@ public function testValidateInContext() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group'], + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group'], + )); $violations = $this->validator->validate($entity, new Valid(), 'Group'); @@ -1561,14 +1561,14 @@ public function testValidateArrayInContext() $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback1, - 'groups' => 'Group', - ])); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback2, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback1, + groups: ['Group'], + )); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback2, + groups: ['Group'], + )); $violations = $this->validator->validate($entity, new Valid(), 'Group'); @@ -1603,10 +1603,10 @@ public function testTraverseTraversableByDefault() }; $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($traversable, new Valid(), 'Group'); @@ -1635,10 +1635,10 @@ public function testTraversalEnabledOnClass() $traversableMetadata->addConstraint(new Traverse(true)); $this->metadataFactory->addMetadata($traversableMetadata); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($traversable, new Valid(), 'Group'); @@ -1659,10 +1659,10 @@ public function testTraversalDisabledOnClass() $traversableMetadata->addConstraint(new Traverse(false)); $this->metadataFactory->addMetadata($traversableMetadata); - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($traversable, new Valid(), 'Group'); @@ -1692,10 +1692,10 @@ public function testReferenceTraversalDisabledOnClass() $traversableMetadata->addConstraint(new Traverse(false)); $this->metadataFactory->addMetadata($traversableMetadata); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $this->metadata->addPropertyConstraint('reference', new Valid()); $violations = $this->validate($entity, new Valid(), 'Group'); @@ -1717,13 +1717,13 @@ public function testReferenceTraversalEnabledOnReferenceDisabledOnClass() $traversableMetadata->addConstraint(new Traverse(false)); $this->metadataFactory->addMetadata($traversableMetadata); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => true, - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); + $this->metadata->addPropertyConstraint('reference', new Valid( + traverse: true, + )); $violations = $this->validate($entity, new Valid(), 'Group'); @@ -1744,13 +1744,13 @@ public function testReferenceTraversalDisabledOnReferenceEnabledOnClass() $traversableMetadata->addConstraint(new Traverse(true)); $this->metadataFactory->addMetadata($traversableMetadata); - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); - $this->metadata->addPropertyConstraint('reference', new Valid([ - 'traverse' => false, - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); + $this->metadata->addPropertyConstraint('reference', new Valid( + traverse: false, + )); $violations = $this->validate($entity, new Valid(), 'Group'); @@ -1767,10 +1767,10 @@ public function testReferenceCascadeDisabledByDefault() $this->fail('Should not be called'); }; - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, new Valid(), 'Group'); @@ -1789,10 +1789,10 @@ public function testReferenceCascadeEnabledIgnoresUntyped() $this->fail('Should not be called'); }; - $this->referenceMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $this->referenceMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $violations = $this->validate($entity, new Valid(), 'Group'); @@ -1816,10 +1816,10 @@ public function testTypedReferenceCascadeEnabled() $cascadingMetadata->addConstraint(new Cascade()); $cascadedMetadata = new ClassMetadata(CascadedChild::class); - $cascadedMetadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => 'Group', - ])); + $cascadedMetadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group'], + )); $this->metadataFactory->addMetadata($cascadingMetadata); $this->metadataFactory->addMetadata($cascadedMetadata); @@ -1868,10 +1868,10 @@ public function testNoDuplicateValidationIfClassConstraintInMultipleGroups() $context->addViolation('Message'); }; - $this->metadata->addConstraint(new Callback([ - 'callback' => $callback, - 'groups' => ['Group 1', 'Group 2'], - ])); + $this->metadata->addConstraint(new Callback( + callback: $callback, + groups: ['Group 1', 'Group 2'], + )); $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); @@ -1887,10 +1887,10 @@ public function testNoDuplicateValidationIfPropertyConstraintInMultipleGroups() $context->addViolation('Message'); }; - $this->metadata->addPropertyConstraint('firstName', new Callback([ - 'callback' => $callback, - 'groups' => ['Group 1', 'Group 2'], - ])); + $this->metadata->addPropertyConstraint('firstName', new Callback( + callback: $callback, + groups: ['Group 1', 'Group 2'], + )); $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); @@ -2007,8 +2007,8 @@ public function testNestedObjectIsNotValidatedIfGroupInValidConstraintIsNotValid $reference->value = ''; $entity->childA = $reference; - $this->metadata->addPropertyConstraint('firstName', new NotBlank(['groups' => 'group1'])); - $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => 'group1'])); + $this->metadata->addPropertyConstraint('firstName', new NotBlank(groups: ['group1'])); + $this->metadata->addPropertyConstraint('childA', new Valid(groups: ['group1'])); $this->referenceMetadata->addPropertyConstraint('value', new NotBlank()); $violations = $this->validator->validate($entity, null, []); @@ -2024,9 +2024,9 @@ public function testNestedObjectIsValidatedIfGroupInValidConstraintIsValidated() $reference->value = ''; $entity->childA = $reference; - $this->metadata->addPropertyConstraint('firstName', new NotBlank(['groups' => 'group1'])); - $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => 'group1'])); - $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(['groups' => 'group1'])); + $this->metadata->addPropertyConstraint('firstName', new NotBlank(groups: ['group1'])); + $this->metadata->addPropertyConstraint('childA', new Valid(groups: ['group1'])); + $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(groups: ['group1'])); $violations = $this->validator->validate($entity, null, ['Default', 'group1']); @@ -2044,10 +2044,10 @@ public function testNestedObjectIsValidatedInMultipleGroupsIfGroupInValidConstra $entity->childA = $reference; $this->metadata->addPropertyConstraint('firstName', new NotBlank()); - $this->metadata->addPropertyConstraint('childA', new Valid(['groups' => ['group1', 'group2']])); + $this->metadata->addPropertyConstraint('childA', new Valid(groups: ['group1', 'group2'])); - $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(['groups' => 'group1'])); - $this->referenceMetadata->addPropertyConstraint('value', new NotNull(['groups' => 'group2'])); + $this->referenceMetadata->addPropertyConstraint('value', new NotBlank(groups: ['group1'])); + $this->referenceMetadata->addPropertyConstraint('value', new NotNull(groups: ['group2'])); $violations = $this->validator->validate($entity, null, ['Default', 'group1', 'group2']); @@ -2136,10 +2136,10 @@ public function testRelationBetweenChildAAndChildB() public function testCollectionConstraintValidateAllGroupsForNestedConstraints() { - $this->metadata->addPropertyConstraint('data', new Collection(['fields' => [ - 'one' => [new NotBlank(['groups' => 'one']), new Length(['min' => 2, 'groups' => 'two'])], - 'two' => [new NotBlank(['groups' => 'two'])], - ]])); + $this->metadata->addPropertyConstraint('data', new Collection(fields: [ + 'one' => [new NotBlank(groups: ['one']), new Length(min: 2, groups: ['two'])], + 'two' => [new NotBlank(groups: ['two'])], + ])); $entity = new Entity(); $entity->data = ['one' => 't', 'two' => '']; @@ -2154,9 +2154,9 @@ public function testCollectionConstraintValidateAllGroupsForNestedConstraints() public function testGroupedMethodConstraintValidateInSequence() { $metadata = new ClassMetadata(EntityWithGroupedConstraintOnMethods::class); - $metadata->addPropertyConstraint('bar', new NotNull(['groups' => 'Foo'])); - $metadata->addGetterMethodConstraint('validInFoo', 'isValidInFoo', new IsTrue(['groups' => 'Foo'])); - $metadata->addGetterMethodConstraint('bar', 'getBar', new NotNull(['groups' => 'Bar'])); + $metadata->addPropertyConstraint('bar', new NotNull(groups: ['Foo'])); + $metadata->addGetterMethodConstraint('validInFoo', 'isValidInFoo', new IsTrue(groups: ['Foo'])); + $metadata->addGetterMethodConstraint('bar', 'getBar', new NotNull(groups: ['Bar'])); $this->metadataFactory->addMetadata($metadata); @@ -2197,10 +2197,13 @@ public function testNotNullConstraintOnGetterReturningNull() public function testAllConstraintValidateAllGroupsForNestedConstraints() { - $this->metadata->addPropertyConstraint('data', new All(['constraints' => [ - new NotBlank(['groups' => 'one']), - new Length(['min' => 2, 'groups' => 'two']), - ]])); + $this->metadata->addPropertyConstraint('data', new All(constraints: [ + new NotBlank(groups: ['one']), + new Length( + min: 2, + groups: ['two'], + ), + ])); $entity = new Entity(); $entity->data = ['one' => 't', 'two' => '']; @@ -2330,8 +2333,8 @@ public function testValidateWithExplicitCascade() public function testValidatedConstraintsHashesDoNotCollide() { $metadata = new ClassMetadata(Entity::class); - $metadata->addPropertyConstraint('initialized', new NotNull(['groups' => 'should_pass'])); - $metadata->addPropertyConstraint('initialized', new IsNull(['groups' => 'should_fail'])); + $metadata->addPropertyConstraint('initialized', new NotNull(groups: ['should_pass'])); + $metadata->addPropertyConstraint('initialized', new IsNull(groups: ['should_fail'])); $this->metadataFactory->addMetadata($metadata); From 388a47591870990a26b6f9c6d93be64f19242d73 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 20 Jan 2025 15:01:44 +0100 Subject: [PATCH 116/149] [Validator] Fix constraints deprecations --- Constraints/AbstractComparison.php | 4 ++-- Constraints/All.php | 2 +- Constraints/AtLeastOneOf.php | 2 +- Constraints/Bic.php | 4 ++-- Constraints/Blank.php | 4 ++-- Constraints/Callback.php | 6 +++--- Constraints/CardScheme.php | 4 ++-- Constraints/Cascade.php | 4 ++-- Constraints/Choice.php | 2 +- Constraints/Cidr.php | 4 ++-- Constraints/Collection.php | 2 +- Constraints/Count.php | 4 ++-- Constraints/Country.php | 4 ++-- Constraints/CssColor.php | 2 +- Constraints/Currency.php | 4 ++-- Constraints/Date.php | 4 ++-- Constraints/DateTime.php | 4 ++-- Constraints/DisableAutoMapping.php | 2 +- Constraints/Email.php | 4 ++-- Constraints/EnableAutoMapping.php | 2 +- Constraints/Expression.php | 4 ++-- Constraints/ExpressionSyntax.php | 4 ++-- Constraints/File.php | 4 ++-- Constraints/Hostname.php | 4 ++-- Constraints/Iban.php | 4 ++-- Constraints/Ip.php | 4 ++-- Constraints/IsFalse.php | 4 ++-- Constraints/IsNull.php | 4 ++-- Constraints/IsTrue.php | 4 ++-- Constraints/Isbn.php | 4 ++-- Constraints/Isin.php | 4 ++-- Constraints/Issn.php | 4 ++-- Constraints/Json.php | 4 ++-- Constraints/Language.php | 4 ++-- Constraints/Length.php | 4 ++-- Constraints/Locale.php | 4 ++-- Constraints/Luhn.php | 4 ++-- Constraints/NoSuspiciousCharacters.php | 4 ++-- Constraints/NotBlank.php | 4 ++-- Constraints/NotCompromisedPassword.php | 4 ++-- Constraints/NotNull.php | 4 ++-- Constraints/PasswordStrength.php | 4 ++-- Constraints/Range.php | 4 ++-- Constraints/Regex.php | 4 ++-- Constraints/Sequentially.php | 2 +- Constraints/Time.php | 4 ++-- Constraints/Timezone.php | 4 ++-- Constraints/Traverse.php | 2 +- Constraints/Type.php | 6 +++--- Constraints/Ulid.php | 4 ++-- Constraints/Unique.php | 4 ++-- Constraints/Url.php | 4 ++-- Constraints/Uuid.php | 4 ++-- Constraints/Valid.php | 4 ++-- Constraints/When.php | 4 ++-- Constraints/ZeroComparisonConstraintTrait.php | 2 +- Tests/Constraints/RangeTest.php | 2 +- 57 files changed, 105 insertions(+), 105 deletions(-) diff --git a/Constraints/AbstractComparison.php b/Constraints/AbstractComparison.php index 1b4629c43..3830da789 100644 --- a/Constraints/AbstractComparison.php +++ b/Constraints/AbstractComparison.php @@ -33,12 +33,12 @@ abstract class AbstractComparison extends Constraint public function __construct(mixed $value = null, ?string $propertyPath = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($value)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($value, $options ?? []); } elseif (null !== $value) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/All.php b/Constraints/All.php index bbaa9a9a5..9bb60ba5d 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -33,7 +33,7 @@ class All extends Composite public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { if (\is_array($constraints) && !array_is_list($constraints)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($constraints ?? [], $groups, $payload); diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index a03ca7f7a..b20ea0df0 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -42,7 +42,7 @@ class AtLeastOneOf extends Composite public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null, ?string $message = null, ?string $messageCollection = null, ?bool $includeInternalMessages = null) { if (\is_array($constraints) && !array_is_list($constraints)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($constraints ?? [], $groups, $payload); diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 34121af75..5390d5556 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -92,8 +92,8 @@ public function __construct( throw new InvalidArgumentException('The "mode" parameter value is not valid.'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Blank.php b/Constraints/Blank.php index 09c5a5fac..72fbae57a 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -37,8 +37,8 @@ class Blank extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/Callback.php b/Constraints/Callback.php index ff02183bd..44b51ac2a 100644 --- a/Constraints/Callback.php +++ b/Constraints/Callback.php @@ -36,21 +36,21 @@ public function __construct(array|string|callable|null $callback = null, ?array { // Invocation through attributes with an array parameter only if (\is_array($callback) && 1 === \count($callback) && isset($callback['value'])) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $callback = $callback['value']; } if (!\is_array($callback) || (!isset($callback['callback']) && !isset($callback['groups']) && !isset($callback['payload']))) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } $options['callback'] = $callback; } else { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($callback, $options ?? []); } diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 0944761d8..a75e9f7ab 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -56,12 +56,12 @@ class CardScheme extends Constraint public function __construct(array|string|null $schemes, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($schemes) && \is_string(key($schemes))) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($schemes, $options ?? []); } else { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index 016b7e7ef..a3ea71dbf 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -33,12 +33,12 @@ class Cascade extends Constraint public function __construct(array|string|null $exclude = null, ?array $options = null) { if (\is_array($exclude) && !array_is_list($exclude)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($exclude, $options ?? []); } else { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } $this->exclude = array_flip((array) $exclude); diff --git a/Constraints/Choice.php b/Constraints/Choice.php index d17e5f654..1435a762b 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -81,7 +81,7 @@ public function __construct( $choices ??= $options; $options = []; } elseif (\is_array($options) && [] !== $options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } if (null !== $choices) { diff --git a/Constraints/Cidr.php b/Constraints/Cidr.php index 82d52317a..a6e470177 100644 --- a/Constraints/Cidr.php +++ b/Constraints/Cidr.php @@ -86,8 +86,8 @@ public function __construct( $payload = null, ?callable $normalizer = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } $this->version = $version ?? $options['version'] ?? $this->version; diff --git a/Constraints/Collection.php b/Constraints/Collection.php index 4253697ef..eca5a4eee 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -48,7 +48,7 @@ public function __construct(mixed $fields = null, ?array $groups = null, mixed $ if (self::isFieldsOption($fields)) { $fields = ['fields' => $fields]; } else { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($fields, $groups, $payload); diff --git a/Constraints/Count.php b/Constraints/Count.php index 31479b578..108872904 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -66,12 +66,12 @@ public function __construct( ?array $options = null, ) { if (\is_array($exactly)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($exactly, $options ?? []); $exactly = $options['value'] ?? null; } elseif (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/Country.php b/Constraints/Country.php index dcbdd01a1..135f996dd 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -54,8 +54,8 @@ public function __construct( throw new LogicException('The Intl component is required to use the Country constraint. Try running "composer require symfony/intl".'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 302e779eb..793a4a576 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -75,7 +75,7 @@ public function __construct(array|string $formats = [], ?string $message = null, if (!$formats) { $options['value'] = self::$validationModes; } elseif (\is_array($formats) && \is_string(key($formats))) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($formats, $options ?? []); } elseif (\is_array($formats)) { diff --git a/Constraints/Currency.php b/Constraints/Currency.php index c9b034d6d..c8f6417b3 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -46,8 +46,8 @@ public function __construct(?array $options = null, ?string $message = null, ?ar throw new LogicException('The Intl component is required to use the Currency constraint. Try running "composer require symfony/intl".'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Date.php b/Constraints/Date.php index 98d42ad72..adb48474f 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -41,8 +41,8 @@ class Date extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/DateTime.php b/Constraints/DateTime.php index 863b5d119..6b287be75 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -46,12 +46,12 @@ class DateTime extends Constraint public function __construct(string|array|null $format = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($format)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($format, $options ?? []); } elseif (null !== $format) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/DisableAutoMapping.php b/Constraints/DisableAutoMapping.php index 2ece16a04..7cbea8b38 100644 --- a/Constraints/DisableAutoMapping.php +++ b/Constraints/DisableAutoMapping.php @@ -33,7 +33,7 @@ class DisableAutoMapping extends Constraint public function __construct(?array $options = null, mixed $payload = null) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } if (\is_array($options) && \array_key_exists('groups', $options)) { diff --git a/Constraints/Email.php b/Constraints/Email.php index 05bd6ee1b..4a66986b2 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -68,8 +68,8 @@ public function __construct( throw new InvalidArgumentException('The "mode" parameter value is not valid.'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/EnableAutoMapping.php b/Constraints/EnableAutoMapping.php index 5de158a39..873430677 100644 --- a/Constraints/EnableAutoMapping.php +++ b/Constraints/EnableAutoMapping.php @@ -33,7 +33,7 @@ class EnableAutoMapping extends Constraint public function __construct(?array $options = null, mixed $payload = null) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } if (\is_array($options) && \array_key_exists('groups', $options)) { diff --git a/Constraints/Expression.php b/Constraints/Expression.php index 355ac2610..a739acbb8 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -61,12 +61,12 @@ public function __construct( } if (\is_array($expression)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($expression, $options ?? []); } else { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index 0dcf8a4e0..5a0a09de1 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -41,8 +41,8 @@ class ExpressionSyntax extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?string $service = null, ?array $allowedVariables = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/File.php b/Constraints/File.php index 6c77559ca..8117339ba 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -118,8 +118,8 @@ public function __construct( array|string|null $extensions = null, ?string $extensionsMessage = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index 1ea588ce5..ca9bc3a32 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -44,8 +44,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Iban.php b/Constraints/Iban.php index be79d3dd3..459fb5fb0 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -49,8 +49,8 @@ class Iban extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 4930b5f82..4db552a76 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -121,8 +121,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index 42ef5aac2..bcdadeaf9 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -37,8 +37,8 @@ class IsFalse extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index b97a88660..fa04703ea 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -37,8 +37,8 @@ class IsNull extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index 849ded086..3c0345e77 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -37,8 +37,8 @@ class IsTrue extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index 36813bb7e..45ca4e4b8 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -67,12 +67,12 @@ public function __construct( ?array $options = null, ) { if (\is_array($type)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($type, $options ?? []); } elseif (null !== $type) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/Isin.php b/Constraints/Isin.php index 405175914..7bd9abe2d 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -46,8 +46,8 @@ class Isin extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Issn.php b/Constraints/Issn.php index d70b26b38..048c18f5e 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -60,8 +60,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Json.php b/Constraints/Json.php index 954487fc9..18078a2fe 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -37,8 +37,8 @@ class Json extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Language.php b/Constraints/Language.php index 38fd164d0..61ac4644b 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -52,8 +52,8 @@ public function __construct( throw new LogicException('The Intl component is required to use the Language constraint. Try running "composer require symfony/intl".'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Length.php b/Constraints/Length.php index 254487642..ce1460c6e 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -85,12 +85,12 @@ public function __construct( ?array $options = null, ) { if (\is_array($exactly)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($exactly, $options ?? []); $exactly = $options['value'] ?? null; } elseif (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 739ce79ed..0ffe4b0e8 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -52,8 +52,8 @@ public function __construct( throw new LogicException('The Intl component is required to use the Locale constraint. Try running "composer require symfony/intl".'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index 2ecb5edc7..9421fc3c7 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -47,8 +47,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/NoSuspiciousCharacters.php b/Constraints/NoSuspiciousCharacters.php index ea9ef8b6f..f0d28dba2 100644 --- a/Constraints/NoSuspiciousCharacters.php +++ b/Constraints/NoSuspiciousCharacters.php @@ -107,8 +107,8 @@ public function __construct( throw new LogicException('The intl extension is required to use the NoSuspiciousCharacters constraint.'); } - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index 18c8e533b..725e7eede 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -43,8 +43,8 @@ class NotBlank extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?bool $allowNull = null, ?callable $normalizer = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index fd0d5e185..ef1e03da9 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -47,8 +47,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 4eb57c6c9..28596925e 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -37,8 +37,8 @@ class NotNull extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/PasswordStrength.php b/Constraints/PasswordStrength.php index 7db3bb3a0..3867cfbda 100644 --- a/Constraints/PasswordStrength.php +++ b/Constraints/PasswordStrength.php @@ -47,8 +47,8 @@ final class PasswordStrength extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?int $minScore = null, ?array $groups = null, mixed $payload = null, ?string $message = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } $options['minScore'] ??= self::STRENGTH_MEDIUM; diff --git a/Constraints/Range.php b/Constraints/Range.php index 038f3bb1e..e27dc3501 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -73,8 +73,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Regex.php b/Constraints/Regex.php index f3f1fb07e..f81c731da 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -55,12 +55,12 @@ public function __construct( ?array $options = null, ) { if (\is_array($pattern)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($pattern, $options ?? []); } elseif (null !== $pattern) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index 93ae0fcdd..bdedb2a5e 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -31,7 +31,7 @@ class Sequentially extends Composite public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { if (is_array($constraints) && !array_is_list($constraints)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($constraints ?? [], $groups, $payload); diff --git a/Constraints/Time.php b/Constraints/Time.php index 9973f681d..a99702cb2 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -46,8 +46,8 @@ public function __construct( mixed $payload = null, ?bool $withSeconds = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index c92f412f8..93b0692ef 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -61,12 +61,12 @@ public function __construct( ?array $options = null, ) { if (\is_array($zone)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($zone, $options ?? []); } elseif (null !== $zone) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index 98671586d..d8546e323 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -36,7 +36,7 @@ public function __construct(bool|array|null $traverse = null, mixed $payload = n } if (\is_array($traverse)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($traverse, null, $payload); diff --git a/Constraints/Type.php b/Constraints/Type.php index 087c1a340..747c6add4 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -40,19 +40,19 @@ class Type extends Constraint public function __construct(string|array|null $type, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { if (\is_array($type) && \is_string(key($type))) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($type, $options ?? []); } elseif (null !== $type) { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } $options['value'] = $type; } elseif (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index 28b5ef25e..91d395fd2 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -59,8 +59,8 @@ public function __construct( mixed $payload = null, ?string $format = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Unique.php b/Constraints/Unique.php index a407764ff..857672daf 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -51,8 +51,8 @@ public function __construct( array|string|null $fields = null, ?string $errorPath = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Url.php b/Constraints/Url.php index ed0733ae6..b3e7256a0 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -58,8 +58,8 @@ public function __construct( ?bool $requireTld = null, ?string $tldMessage = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index 5a4de6a25..9c6526457 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -111,8 +111,8 @@ public function __construct( ?array $groups = null, mixed $payload = null, ) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Valid.php b/Constraints/Valid.php index 2a8eab1d4..48deae8ac 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -32,8 +32,8 @@ class Valid extends Constraint #[HasNamedArguments] public function __construct(?array $options = null, ?array $groups = null, $payload = null, ?bool $traverse = null) { - if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (\is_array($options)) { + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } parent::__construct($options ?? [], $groups, $payload); diff --git a/Constraints/When.php b/Constraints/When.php index 1c3113e1b..12d5e7cc3 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -44,12 +44,12 @@ public function __construct(string|Expression|array $expression, array|Constrain } if (\is_array($expression)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); $options = array_merge($expression, $options ?? []); } else { if (\is_array($options)) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } else { $options = []; } diff --git a/Constraints/ZeroComparisonConstraintTrait.php b/Constraints/ZeroComparisonConstraintTrait.php index b369a9429..d8479c20f 100644 --- a/Constraints/ZeroComparisonConstraintTrait.php +++ b/Constraints/ZeroComparisonConstraintTrait.php @@ -26,7 +26,7 @@ trait ZeroComparisonConstraintTrait public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) { if (null !== $options) { - trigger_deprecation('symfony/validator', '7.2', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } if (is_array($options) && isset($options['propertyPath'])) { diff --git a/Tests/Constraints/RangeTest.php b/Tests/Constraints/RangeTest.php index ae7e07077..01481e8bc 100644 --- a/Tests/Constraints/RangeTest.php +++ b/Tests/Constraints/RangeTest.php @@ -62,7 +62,7 @@ public function testThrowsConstraintExceptionIfNoLimitNorPropertyPath() { $this->expectException(MissingOptionsException::class); $this->expectExceptionMessage('Either option "min", "minPropertyPath", "max" or "maxPropertyPath" must be given'); - new Range([]); + new Range(); } public function testThrowsNoDefaultOptionConfiguredException() From 22d9dea39719fdd9a4326e74f31592691828e458 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 21 Jan 2025 11:50:39 +0100 Subject: [PATCH 117/149] [Validator] Fix `Url` constraint attribute assertion --- Tests/Constraints/UrlTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Constraints/UrlTest.php b/Tests/Constraints/UrlTest.php index 1d641aa92..59e626eda 100644 --- a/Tests/Constraints/UrlTest.php +++ b/Tests/Constraints/UrlTest.php @@ -68,9 +68,9 @@ public function testAttributes() self::assertFalse($cConstraint->requireTld); [$dConstraint] = $metadata->properties['d']->getConstraints(); - self::assertSame(['http', 'https'], $aConstraint->protocols); - self::assertFalse($aConstraint->relativeProtocol); - self::assertNull($aConstraint->normalizer); + self::assertSame(['http', 'https'], $dConstraint->protocols); + self::assertFalse($dConstraint->relativeProtocol); + self::assertNull($dConstraint->normalizer); self::assertTrue($dConstraint->requireTld); } From 6bb3d01513c59f5f93524be736b1eca798f8fb3a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 24 Jan 2025 13:51:14 +0100 Subject: [PATCH 118/149] add missing changelog/upgrade entries --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70468d4d3..5f4872a7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ CHANGELOG 7.3 --- + * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead + + Before: + + ```php + new NotNull([ + 'groups' => ['foo', 'bar'], + 'message' => 'a custom constraint violation message', + ]) + ``` + + After: + + ```php + new NotNull( + groups: ['foo', 'bar'], + message: 'a custom constraint violation message', + ) + ``` * Add support for ratio checks for SVG files to the `Image` constraint * Add the `Slug` constraint From bc4d9ebae0bf4ef4f5ae168e5f1725016c32e73c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 24 Jan 2025 13:57:06 +0100 Subject: [PATCH 119/149] fix the version constaints not supporting named arguments are deprecated in --- CHANGELOG.md | 31 +++++++++++++++++++++ Mapping/Loader/AbstractLoader.php | 2 +- Tests/Mapping/Loader/XmlFileLoaderTest.php | 2 +- Tests/Mapping/Loader/YamlFileLoaderTest.php | 2 +- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4872a7b..c10797cab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,37 @@ CHANGELOG 7.3 --- + * Deprecate defining custom constraints not supporting named arguments + + Before: + + ```php + use Symfony\Component\Validator\Constraint; + + class CustomConstraint extends Constraint + { + public function __construct(array $options) + { + // ... + } + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Attribute\HasNamedArguments; + use Symfony\Component\Validator\Constraint; + + class CustomConstraint extends Constraint + { + #[HasNamedArguments] + public function __construct($option1, $option2, $groups, $payload) + { + // ... + } + } + ``` * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead Before: diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index 184bca894..67b7b2010 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -105,7 +105,7 @@ protected function newConstraint(string $name, mixed $options = null): Constrain } if ($options) { - trigger_deprecation('symfony/validator', '7.2', 'Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to %s.', $className); + trigger_deprecation('symfony/validator', '7.3', 'Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to %s.', $className); return new $className($options); } diff --git a/Tests/Mapping/Loader/XmlFileLoaderTest.php b/Tests/Mapping/Loader/XmlFileLoaderTest.php index bc8e4e72e..08a4bb862 100644 --- a/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -184,7 +184,7 @@ public function testLoadConstraintWithoutNamedArgumentsSupport() $loader = new XmlFileLoader(__DIR__.'/constraint-without-named-arguments-support.xml'); $metadata = new ClassMetadata(DummyEntityConstraintWithoutNamedArguments::class); - $this->expectUserDeprecationMessage('Since symfony/validator 7.2: Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments.'); + $this->expectUserDeprecationMessage('Since symfony/validator 7.3: Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments.'); $loader->loadClassMetadata($metadata); } diff --git a/Tests/Mapping/Loader/YamlFileLoaderTest.php b/Tests/Mapping/Loader/YamlFileLoaderTest.php index b496663f1..c3bbcb18e 100644 --- a/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -200,7 +200,7 @@ public function testLoadConstraintWithoutNamedArgumentsSupport() $loader = new YamlFileLoader(__DIR__.'/constraint-without-named-arguments-support.yml'); $metadata = new ClassMetadata(DummyEntityConstraintWithoutNamedArguments::class); - $this->expectUserDeprecationMessage('Since symfony/validator 7.2: Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments.'); + $this->expectUserDeprecationMessage('Since symfony/validator 7.3: Using constraints not supporting named arguments is deprecated. Try adding the HasNamedArguments attribute to Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutNamedArguments.'); $loader->loadClassMetadata($metadata); } From 8ddcb6e95cb8d6f4e98ee5435f806090badb2642 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 28 Jan 2025 12:27:23 +0100 Subject: [PATCH 120/149] [Validator] Add support for the `otherwise` option in the `When` constraint --- CHANGELOG.md | 2 + Constraints/Composite.php | 95 ++++++++++--------- Constraints/When.php | 15 ++- Constraints/WhenValidator.php | 7 +- Tests/Constraints/CompositeTest.php | 69 +++++++++++++- .../Fixtures/WhenTestWithAttributes.php | 4 + Tests/Constraints/WhenTest.php | 14 +++ Tests/Constraints/WhenValidatorTest.php | 30 ++++++ 8 files changed, 184 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c10797cab..960195c95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,8 @@ CHANGELOG ``` * Add support for ratio checks for SVG files to the `Image` constraint * Add the `Slug` constraint + * Add support for the `otherwise` option in the `When` constraint + * Add support for multiple fields containing nested constraints in `Composite` constraints 7.2 --- diff --git a/Constraints/Composite.php b/Constraints/Composite.php index 8cd0edde7..deac22cc5 100644 --- a/Constraints/Composite.php +++ b/Constraints/Composite.php @@ -55,57 +55,58 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed $this->initializeNestedConstraints(); - /* @var Constraint[] $nestedConstraints */ - $compositeOption = $this->getCompositeOption(); - $nestedConstraints = $this->$compositeOption; + foreach ((array) $this->getCompositeOption() as $option) { + /* @var Constraint[] $nestedConstraints */ + $nestedConstraints = $this->$option; - if (!\is_array($nestedConstraints)) { - $nestedConstraints = [$nestedConstraints]; - } - - foreach ($nestedConstraints as $constraint) { - if (!$constraint instanceof Constraint) { - if (\is_object($constraint)) { - $constraint = $constraint::class; - } - - throw new ConstraintDefinitionException(\sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, static::class)); + if (!\is_array($nestedConstraints)) { + $nestedConstraints = [$nestedConstraints]; } - if ($constraint instanceof Valid) { - throw new ConstraintDefinitionException(\sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', static::class)); - } - } + foreach ($nestedConstraints as $constraint) { + if (!$constraint instanceof Constraint) { + if (\is_object($constraint)) { + $constraint = get_debug_type($constraint); + } - if (!isset(((array) $this)['groups'])) { - $mergedGroups = []; + throw new ConstraintDefinitionException(\sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, get_debug_type($this))); + } - foreach ($nestedConstraints as $constraint) { - foreach ($constraint->groups as $group) { - $mergedGroups[$group] = true; + if ($constraint instanceof Valid) { + throw new ConstraintDefinitionException(\sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', get_debug_type($this))); } } - // prevent empty composite constraint to have empty groups - $this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP]; - $this->$compositeOption = $nestedConstraints; + if (!isset(((array) $this)['groups'])) { + $mergedGroups = []; - return; - } + foreach ($nestedConstraints as $constraint) { + foreach ($constraint->groups as $group) { + $mergedGroups[$group] = true; + } + } + + // prevent empty composite constraint to have empty groups + $this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP]; + $this->$option = $nestedConstraints; - foreach ($nestedConstraints as $constraint) { - if (isset(((array) $constraint)['groups'])) { - $excessGroups = array_diff($constraint->groups, $this->groups); + continue; + } - if (\count($excessGroups) > 0) { - throw new ConstraintDefinitionException(\sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), static::class)); + foreach ($nestedConstraints as $constraint) { + if (isset(((array) $constraint)['groups'])) { + $excessGroups = array_diff($constraint->groups, $this->groups); + + if (\count($excessGroups) > 0) { + throw new ConstraintDefinitionException(\sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), get_debug_type($this))); + } + } else { + $constraint->groups = $this->groups; } - } else { - $constraint->groups = $this->groups; } - } - $this->$compositeOption = $nestedConstraints; + $this->$option = $nestedConstraints; + } } /** @@ -115,18 +116,20 @@ public function addImplicitGroupName(string $group): void { parent::addImplicitGroupName($group); - /** @var Constraint[] $nestedConstraints */ - $nestedConstraints = $this->{$this->getCompositeOption()}; + foreach ((array) $this->getCompositeOption() as $option) { + /* @var Constraint[] $nestedConstraints */ + $nestedConstraints = (array) $this->$option; - foreach ($nestedConstraints as $constraint) { - $constraint->addImplicitGroupName($group); + foreach ($nestedConstraints as $constraint) { + $constraint->addImplicitGroupName($group); + } } } /** * Returns the name of the property that contains the nested constraints. */ - abstract protected function getCompositeOption(): string; + abstract protected function getCompositeOption(): array|string; /** * @internal Used by metadata @@ -135,8 +138,12 @@ abstract protected function getCompositeOption(): string; */ public function getNestedConstraints(): array { - /* @var Constraint[] $nestedConstraints */ - return $this->{$this->getCompositeOption()}; + $constraints = []; + foreach ((array) $this->getCompositeOption() as $option) { + $constraints = array_merge($constraints, (array) $this->$option); + } + + return $constraints; } /** diff --git a/Constraints/When.php b/Constraints/When.php index 12d5e7cc3..5fe83ab53 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -28,6 +28,7 @@ class When extends Composite public string|Expression $expression; public array|Constraint $constraints = []; public array $values = []; + public array|Constraint $otherwise = []; /** * @param string|Expression|array $expression The condition to evaluate, written with the ExpressionLanguage syntax @@ -35,9 +36,10 @@ class When extends Composite * @param array|null $values The values of the custom variables used in the expression (defaults to []) * @param string[]|null $groups * @param array|null $options + * @param Constraint[]|Constraint $otherwise One or multiple constraints that are applied if the expression returns false */ #[HasNamedArguments] - public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null) + public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null, array|Constraint $otherwise = []) { if (!class_exists(ExpressionLanguage::class)) { throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); @@ -56,12 +58,17 @@ public function __construct(string|Expression|array $expression, array|Constrain $options['expression'] = $expression; $options['constraints'] = $constraints; + $options['otherwise'] = $otherwise; } - if (isset($options['constraints']) && !\is_array($options['constraints'])) { + if (!\is_array($options['constraints'] ?? [])) { $options['constraints'] = [$options['constraints']]; } + if (!\is_array($options['otherwise'] ?? [])) { + $options['otherwise'] = [$options['otherwise']]; + } + if (null !== $groups) { $options['groups'] = $groups; } @@ -85,8 +92,8 @@ public function getTargets(): string|array return [self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT]; } - protected function getCompositeOption(): string + protected function getCompositeOption(): array|string { - return 'constraints'; + return ['constraints', 'otherwise']; } } diff --git a/Constraints/WhenValidator.php b/Constraints/WhenValidator.php index b41ba83ff..272bd86e9 100644 --- a/Constraints/WhenValidator.php +++ b/Constraints/WhenValidator.php @@ -35,9 +35,14 @@ public function validate(mixed $value, Constraint $constraint): void $variables['this'] = $context->getObject(); $variables['context'] = $context; - if ($this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { + $result = $this->getExpressionLanguage()->evaluate($constraint->expression, $variables); + + if ($result) { $context->getValidator()->inContext($context) ->validate($value, $constraint->constraints); + } elseif ($constraint->otherwise) { + $context->getValidator()->inContext($context) + ->validate($value, $constraint->otherwise); } } diff --git a/Tests/Constraints/CompositeTest.php b/Tests/Constraints/CompositeTest.php index a769a68e4..9329ef1a2 100644 --- a/Tests/Constraints/CompositeTest.php +++ b/Tests/Constraints/CompositeTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Composite; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Valid; @@ -23,9 +24,14 @@ class ConcreteComposite extends Composite { public array|Constraint $constraints = []; - protected function getCompositeOption(): string + public function __construct(mixed $options = null, public array|Constraint $otherNested = []) { - return 'constraints'; + parent::__construct($options); + } + + protected function getCompositeOption(): array + { + return ['constraints', 'otherNested']; } public function getDefaultOption(): ?string @@ -44,11 +50,14 @@ public function testConstraintHasDefaultGroup() $constraint = new ConcreteComposite([ new NotNull(), new NotBlank(), + ], [ + new Length(exactly: 10), ]); $this->assertEquals(['Default'], $constraint->groups); $this->assertEquals(['Default'], $constraint->constraints[0]->groups); $this->assertEquals(['Default'], $constraint->constraints[1]->groups); + $this->assertEquals(['Default'], $constraint->otherNested[0]->groups); } public function testNestedCompositeConstraintHasDefaultGroup() @@ -68,11 +77,14 @@ public function testMergeNestedGroupsIfNoExplicitParentGroup() $constraint = new ConcreteComposite([ new NotNull(groups: ['Default']), new NotBlank(groups: ['Default', 'Strict']), + ], [ + new Length(exactly: 10, groups: ['Default', 'Strict']), ]); $this->assertEquals(['Default', 'Strict'], $constraint->groups); $this->assertEquals(['Default'], $constraint->constraints[0]->groups); $this->assertEquals(['Default', 'Strict'], $constraint->constraints[1]->groups); + $this->assertEquals(['Default', 'Strict'], $constraint->otherNested[0]->groups); } public function testSetImplicitNestedGroupsIfExplicitParentGroup() @@ -82,12 +94,16 @@ public function testSetImplicitNestedGroupsIfExplicitParentGroup() new NotNull(), new NotBlank(), ], + 'otherNested' => [ + new Length(exactly: 10), + ], 'groups' => ['Default', 'Strict'], ]); $this->assertEquals(['Default', 'Strict'], $constraint->groups); $this->assertEquals(['Default', 'Strict'], $constraint->constraints[0]->groups); $this->assertEquals(['Default', 'Strict'], $constraint->constraints[1]->groups); + $this->assertEquals(['Default', 'Strict'], $constraint->otherNested[0]->groups); } public function testExplicitNestedGroupsMustBeSubsetOfExplicitParentGroups() @@ -97,12 +113,16 @@ public function testExplicitNestedGroupsMustBeSubsetOfExplicitParentGroups() new NotNull(groups: ['Default']), new NotBlank(groups: ['Strict']), ], + 'otherNested' => [ + new Length(exactly: 10, groups: ['Strict']), + ], 'groups' => ['Default', 'Strict'], ]); $this->assertEquals(['Default', 'Strict'], $constraint->groups); $this->assertEquals(['Default'], $constraint->constraints[0]->groups); $this->assertEquals(['Strict'], $constraint->constraints[1]->groups); + $this->assertEquals(['Strict'], $constraint->otherNested[0]->groups); } public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroups() @@ -116,11 +136,27 @@ public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroups() ]); } + public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroupsInOtherNested() + { + $this->expectException(ConstraintDefinitionException::class); + new ConcreteComposite([ + 'constraints' => [ + new NotNull(groups: ['Default']), + ], + 'otherNested' => [ + new NotNull(groups: ['Default', 'Foobar']), + ], + 'groups' => ['Default', 'Strict'], + ]); + } + public function testImplicitGroupNamesAreForwarded() { $constraint = new ConcreteComposite([ new NotNull(groups: ['Default']), new NotBlank(groups: ['Strict']), + ], [ + new Length(exactly: 10, groups: ['Default']), ]); $constraint->addImplicitGroupName('ImplicitGroup'); @@ -128,14 +164,17 @@ public function testImplicitGroupNamesAreForwarded() $this->assertEquals(['Default', 'Strict', 'ImplicitGroup'], $constraint->groups); $this->assertEquals(['Default', 'ImplicitGroup'], $constraint->constraints[0]->groups); $this->assertEquals(['Strict'], $constraint->constraints[1]->groups); + $this->assertEquals(['Default', 'ImplicitGroup'], $constraint->otherNested[0]->groups); } public function testSingleConstraintsAccepted() { $nestedConstraint = new NotNull(); - $constraint = new ConcreteComposite($nestedConstraint); + $otherNestedConstraint = new Length(exactly: 10); + $constraint = new ConcreteComposite($nestedConstraint, $otherNestedConstraint); $this->assertEquals([$nestedConstraint], $constraint->constraints); + $this->assertEquals([$otherNestedConstraint], $constraint->otherNested); } public function testFailIfNoConstraint() @@ -147,6 +186,15 @@ public function testFailIfNoConstraint() ]); } + public function testFailIfNoConstraintInAnotherNested() + { + $this->expectException(ConstraintDefinitionException::class); + new ConcreteComposite([new NotNull()], [ + new NotNull(groups: ['Default']), + 'NotBlank', + ]); + } + public function testFailIfNoConstraintObject() { $this->expectException(ConstraintDefinitionException::class); @@ -156,6 +204,15 @@ public function testFailIfNoConstraintObject() ]); } + public function testFailIfNoConstraintObjectInAnotherNested() + { + $this->expectException(ConstraintDefinitionException::class); + new ConcreteComposite([new NotNull()], [ + new NotNull(groups: ['Default']), + new \ArrayObject(), + ]); + } + public function testValidCantBeNested() { $this->expectException(ConstraintDefinitionException::class); @@ -163,4 +220,10 @@ public function testValidCantBeNested() new Valid(), ]); } + + public function testValidCantBeNestedInAnotherNested() + { + $this->expectException(ConstraintDefinitionException::class); + new ConcreteComposite([new NotNull()], [new Valid()]); + } } diff --git a/Tests/Constraints/Fixtures/WhenTestWithAttributes.php b/Tests/Constraints/Fixtures/WhenTestWithAttributes.php index a683eb3c6..31258dc0d 100644 --- a/Tests/Constraints/Fixtures/WhenTestWithAttributes.php +++ b/Tests/Constraints/Fixtures/WhenTestWithAttributes.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\When; @@ -36,6 +37,9 @@ class WhenTestWithAttributes #[When(expression: 'true', constraints: new NotNull(), groups: ['foo'])] private $qux; + #[When(expression: 'true', constraints: new NotNull(), otherwise: new Length(exactly: 10), groups: ['foo'])] + private $quux; + #[When(expression: 'true', constraints: [ new NotNull(), new NotBlank(), diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index fa71de02e..6516a1014 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\When; @@ -59,6 +60,7 @@ public function testAttributes() groups: ['Default', 'WhenTestWithAttributes'], ), ], $classConstraint->constraints); + self::assertEmpty($classConstraint->otherwise); [$fooConstraint] = $metadata->properties['foo']->getConstraints(); @@ -68,6 +70,7 @@ public function testAttributes() new NotNull(groups: ['Default', 'WhenTestWithAttributes']), new NotBlank(groups: ['Default', 'WhenTestWithAttributes']), ], $fooConstraint->constraints); + self::assertEmpty($fooConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithAttributes'], $fooConstraint->groups); [$barConstraint] = $metadata->properties['bar']->getConstraints(); @@ -78,6 +81,7 @@ public function testAttributes() new NotNull(groups: ['foo']), new NotBlank(groups: ['foo']), ], $barConstraint->constraints); + self::assertEmpty($barConstraint->otherwise); self::assertSame(['foo'], $barConstraint->groups); [$quxConstraint] = $metadata->properties['qux']->getConstraints(); @@ -85,6 +89,7 @@ public function testAttributes() self::assertInstanceOf(When::class, $quxConstraint); self::assertSame('true', $quxConstraint->expression); self::assertEquals([new NotNull(groups: ['foo'])], $quxConstraint->constraints); + self::assertEmpty($quxConstraint->otherwise); self::assertSame(['foo'], $quxConstraint->groups); [$bazConstraint] = $metadata->getters['baz']->getConstraints(); @@ -95,6 +100,15 @@ public function testAttributes() new NotNull(groups: ['Default', 'WhenTestWithAttributes']), new NotBlank(groups: ['Default', 'WhenTestWithAttributes']), ], $bazConstraint->constraints); + self::assertEmpty($bazConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithAttributes'], $bazConstraint->groups); + + [$quuxConstraint] = $metadata->properties['quux']->getConstraints(); + + self::assertInstanceOf(When::class, $quuxConstraint); + self::assertSame('true', $quuxConstraint->expression); + self::assertEquals([new NotNull(groups: ['foo'])], $quuxConstraint->constraints); + self::assertEquals([new Length(exactly: 10, groups: ['foo'])], $quuxConstraint->otherwise); + self::assertSame(['foo'], $quuxConstraint->groups); } } diff --git a/Tests/Constraints/WhenValidatorTest.php b/Tests/Constraints/WhenValidatorTest.php index 019ec828f..501398d2a 100644 --- a/Tests/Constraints/WhenValidatorTest.php +++ b/Tests/Constraints/WhenValidatorTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Constraints\Blank; use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NegativeOrZero; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; @@ -50,6 +51,20 @@ public function testConstraintIsExecuted() )); } + public function testOtherwiseIsExecutedWhenFalse() + { + $constraint = new NotNull(); + $otherwise = new Length(exactly: 10); + + $this->expectValidateValue(0, 'Foo', [$otherwise]); + + $this->validator->validate('Foo', new When( + expression: 'false', + constraints: $constraint, + otherwise: $otherwise, + )); + } + public function testConstraintsAreExecutedWithNull() { $constraints = [ @@ -159,6 +174,21 @@ public function testConstraintsNotExecuted() $this->assertNoViolation(); } + public function testOtherwiseIsExecutedWhenTrue() + { + $constraints = [new NotNull()]; + + $this->expectValidateValue(0, '', $constraints); + + $this->validator->validate('', new When( + expression: 'true', + constraints: $constraints, + otherwise: new Length(exactly: 10), + )); + + $this->assertNoViolation(); + } + public function testConstraintsNotExecutedWithObject() { $number = new \stdClass(); From 0304b1f12e711cc3009eb35f7f1f60ff4fb9f144 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 27 Jun 2023 16:01:55 +0200 Subject: [PATCH 121/149] [FrameworkBundle][Validator] Add `framework.validation.disable_translation` config --- Context/ExecutionContext.php | 6 ++++-- Context/ExecutionContextFactory.php | 2 +- Tests/ValidatorBuilderTest.php | 5 +++++ ValidatorBuilder.php | 12 +++++++++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Context/ExecutionContext.php b/Context/ExecutionContext.php index 0378c3e6f..1fd373309 100644 --- a/Context/ExecutionContext.php +++ b/Context/ExecutionContext.php @@ -107,7 +107,7 @@ public function __construct( private ValidatorInterface $validator, private mixed $root, private TranslatorInterface $translator, - private ?string $translationDomain = null, + private string|false|null $translationDomain = null, ) { $this->violations = new ConstraintViolationList(); $this->cachedObjectsRefs = new \SplObjectStorage(); @@ -134,7 +134,9 @@ public function setConstraint(Constraint $constraint): void public function addViolation(string|\Stringable $message, array $parameters = []): void { $this->violations->add(new ConstraintViolation( - $this->translator->trans($message, $parameters, $this->translationDomain), + false === $this->translationDomain ? + strtr($message, $parameters) : + $this->translator->trans($message, $parameters, $this->translationDomain), $message, $parameters, $this->root, diff --git a/Context/ExecutionContextFactory.php b/Context/ExecutionContextFactory.php index a6a2844ad..39c2add35 100644 --- a/Context/ExecutionContextFactory.php +++ b/Context/ExecutionContextFactory.php @@ -25,7 +25,7 @@ class ExecutionContextFactory implements ExecutionContextFactoryInterface { public function __construct( private TranslatorInterface $translator, - private ?string $translationDomain = null, + private string|false|null $translationDomain = null, ) { } diff --git a/Tests/ValidatorBuilderTest.php b/Tests/ValidatorBuilderTest.php index c57a507e2..ec464b748 100644 --- a/Tests/ValidatorBuilderTest.php +++ b/Tests/ValidatorBuilderTest.php @@ -99,6 +99,11 @@ public function testSetTranslationDomain() $this->assertSame($this->builder, $this->builder->setTranslationDomain('TRANS_DOMAIN')); } + public function testDisableTranslation() + { + $this->assertSame($this->builder, $this->builder->disableTranslation()); + } + public function testGetValidator() { $this->assertInstanceOf(RecursiveValidator::class, $this->builder->getValidator()); diff --git a/ValidatorBuilder.php b/ValidatorBuilder.php index 83e543b8f..917f1c57a 100644 --- a/ValidatorBuilder.php +++ b/ValidatorBuilder.php @@ -50,7 +50,7 @@ class ValidatorBuilder private ?ContainerInterface $groupProviderLocator = null; private ?CacheItemPoolInterface $mappingCache = null; private ?TranslatorInterface $translator = null; - private ?string $translationDomain = null; + private string|false|null $translationDomain = null; /** * Adds an object initializer to the validator. @@ -292,6 +292,16 @@ public function setTranslationDomain(?string $translationDomain): static return $this; } + /** + * @return $this + */ + public function disableTranslation(): static + { + $this->translationDomain = false; + + return $this; + } + /** * @return $this */ From c8b6f7fc91cd60bc5f9f022abd806e9fd2ee45c3 Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Fri, 20 Dec 2024 15:56:14 +0100 Subject: [PATCH 122/149] [Validator] Allow Unique constraint validation on all elements --- CHANGELOG.md | 49 +--------------- Constraints/Unique.php | 3 + Constraints/UniqueValidator.php | 22 ++++--- Tests/Constraints/UniqueValidatorTest.php | 71 +++++++++++++++++++++++ 4 files changed, 88 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 960195c95..e421f748e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,59 +5,12 @@ CHANGELOG --- * Deprecate defining custom constraints not supporting named arguments - - Before: - - ```php - use Symfony\Component\Validator\Constraint; - - class CustomConstraint extends Constraint - { - public function __construct(array $options) - { - // ... - } - } - ``` - - After: - - ```php - use Symfony\Component\Validator\Attribute\HasNamedArguments; - use Symfony\Component\Validator\Constraint; - - class CustomConstraint extends Constraint - { - #[HasNamedArguments] - public function __construct($option1, $option2, $groups, $payload) - { - // ... - } - } - ``` * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead - - Before: - - ```php - new NotNull([ - 'groups' => ['foo', 'bar'], - 'message' => 'a custom constraint violation message', - ]) - ``` - - After: - - ```php - new NotNull( - groups: ['foo', 'bar'], - message: 'a custom constraint violation message', - ) - ``` * Add support for ratio checks for SVG files to the `Image` constraint * Add the `Slug` constraint * Add support for the `otherwise` option in the `When` constraint * Add support for multiple fields containing nested constraints in `Composite` constraints + * Add the `stopOnFirstError` option to the `Unique` constraint to validate all elements 7.2 --- diff --git a/Constraints/Unique.php b/Constraints/Unique.php index 857672daf..1e6503785 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -27,6 +27,7 @@ class Unique extends Constraint public array|string $fields = []; public ?string $errorPath = null; + public bool $stopOnFirstError = true; protected const ERROR_NAMES = [ self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE', @@ -50,6 +51,7 @@ public function __construct( mixed $payload = null, array|string|null $fields = null, ?string $errorPath = null, + ?bool $stopOnFirstError = null, ) { if (\is_array($options)) { trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); @@ -61,6 +63,7 @@ public function __construct( $this->normalizer = $normalizer ?? $this->normalizer; $this->fields = $fields ?? $this->fields; $this->errorPath = $errorPath ?? $this->errorPath; + $this->stopOnFirstError = $stopOnFirstError ?? $this->stopOnFirstError; if (null !== $this->normalizer && !\is_callable($this->normalizer)) { throw new InvalidArgumentException(\sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer))); diff --git a/Constraints/UniqueValidator.php b/Constraints/UniqueValidator.php index 8977fd221..bd78cac72 100644 --- a/Constraints/UniqueValidator.php +++ b/Constraints/UniqueValidator.php @@ -46,20 +46,24 @@ public function validate(mixed $value, Constraint $constraint): void continue; } - if (\in_array($element, $collectionElements, true)) { - $violationBuilder = $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($element)) - ->setCode(Unique::IS_NOT_UNIQUE); + if (!\in_array($element, $collectionElements, true)) { + $collectionElements[] = $element; + continue; + } - if (null !== $constraint->errorPath) { - $violationBuilder->atPath("[$index].{$constraint->errorPath}"); - } + $violationBuilder = $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($element)) + ->setCode(Unique::IS_NOT_UNIQUE); + + if (!$constraint->stopOnFirstError || null !== $constraint->errorPath) { + $violationBuilder->atPath("[$index]".(null !== $constraint->errorPath ? ".{$constraint->errorPath}" : '')); + } - $violationBuilder->addViolation(); + $violationBuilder->addViolation(); + if ($constraint->stopOnFirstError) { return; } - $collectionElements[] = $element; } } diff --git a/Tests/Constraints/UniqueValidatorTest.php b/Tests/Constraints/UniqueValidatorTest.php index ac43ed2b8..12efb7698 100644 --- a/Tests/Constraints/UniqueValidatorTest.php +++ b/Tests/Constraints/UniqueValidatorTest.php @@ -387,6 +387,77 @@ public function testErrorPathWithNonList() ->assertRaised(); } + public function testWithoutStopOnFirstError() + { + $this->validator->validate( + ['a1', 'a2', 'a1', 'a1', 'a2'], + new Unique(stopOnFirstError: false), + ); + + $this + ->buildViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', '"a1"') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[2]') + + ->buildNextViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', '"a1"') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[3]') + + ->buildNextViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', '"a2"') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[4]') + + ->assertRaised(); + } + + public function testWithoutStopOnFirstErrorWithErrorPath() + { + $array = [ + new DummyClassOne(), + new DummyClassOne(), + new DummyClassOne(), + new DummyClassOne(), + new DummyClassOne(), + ]; + + $array[0]->code = 'a1'; + $array[1]->code = 'a2'; + $array[2]->code = 'a1'; + $array[3]->code = 'a1'; + $array[4]->code = 'a2'; + + $this->validator->validate( + $array, + new Unique( + normalizer: [self::class, 'normalizeDummyClassOne'], + fields: 'code', + errorPath: 'code', + stopOnFirstError: false, + ) + ); + + $this + ->buildViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', 'array') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[2].code') + + ->buildNextViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', 'array') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[3].code') + + ->buildNextViolation('This collection should contain only unique elements.') + ->setParameter('{{ value }}', 'array') + ->setCode(Unique::IS_NOT_UNIQUE) + ->atPath('property.path[4].code') + + ->assertRaised(); + } + public static function normalizeDummyClassOne(DummyClassOne $obj): array { return [ From 4a3587a519d372f2396da8e0bf713d38f93ac56d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 18 Feb 2025 13:35:11 +0100 Subject: [PATCH 123/149] [Validator] Add support for closures in `When` --- CHANGELOG.md | 1 + Constraints/When.php | 6 +-- Constraints/WhenValidator.php | 6 ++- .../Fixtures/WhenTestWithClosure.php | 31 ++++++++++++++ Tests/Constraints/WhenTest.php | 35 ++++++++++++++++ Tests/Constraints/WhenValidatorTest.php | 42 +++++++++++++++++++ 6 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 Tests/Constraints/Fixtures/WhenTestWithClosure.php diff --git a/CHANGELOG.md b/CHANGELOG.md index e421f748e..0d2c87f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * Add support for the `otherwise` option in the `When` constraint * Add support for multiple fields containing nested constraints in `Composite` constraints * Add the `stopOnFirstError` option to the `Unique` constraint to validate all elements + * Add support for closures in the `When` constraint 7.2 --- diff --git a/Constraints/When.php b/Constraints/When.php index 5fe83ab53..f32b81a37 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -25,13 +25,13 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class When extends Composite { - public string|Expression $expression; + public string|Expression|\Closure $expression; public array|Constraint $constraints = []; public array $values = []; public array|Constraint $otherwise = []; /** - * @param string|Expression|array $expression The condition to evaluate, written with the ExpressionLanguage syntax + * @param string|Expression|array|\Closure(object): bool $expression The condition to evaluate, either as a closure or using the ExpressionLanguage syntax * @param Constraint[]|Constraint|null $constraints One or multiple constraints that are applied if the expression returns true * @param array|null $values The values of the custom variables used in the expression (defaults to []) * @param string[]|null $groups @@ -39,7 +39,7 @@ class When extends Composite * @param Constraint[]|Constraint $otherwise One or multiple constraints that are applied if the expression returns false */ #[HasNamedArguments] - public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null, array|Constraint $otherwise = []) + public function __construct(string|Expression|array|\Closure $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null, array|Constraint $otherwise = []) { if (!class_exists(ExpressionLanguage::class)) { throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__)); diff --git a/Constraints/WhenValidator.php b/Constraints/WhenValidator.php index 272bd86e9..1ef14469f 100644 --- a/Constraints/WhenValidator.php +++ b/Constraints/WhenValidator.php @@ -35,7 +35,11 @@ public function validate(mixed $value, Constraint $constraint): void $variables['this'] = $context->getObject(); $variables['context'] = $context; - $result = $this->getExpressionLanguage()->evaluate($constraint->expression, $variables); + if ($constraint->expression instanceof \Closure) { + $result = ($constraint->expression)($context->getObject()); + } else { + $result = $this->getExpressionLanguage()->evaluate($constraint->expression, $variables); + } if ($result) { $context->getValidator()->inContext($context) diff --git a/Tests/Constraints/Fixtures/WhenTestWithClosure.php b/Tests/Constraints/Fixtures/WhenTestWithClosure.php new file mode 100644 index 000000000..56777ac7a --- /dev/null +++ b/Tests/Constraints/Fixtures/WhenTestWithClosure.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; + +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\When; + +#[When(expression: static function () { + return true; + }, constraints: new NotNull() +)] +class WhenTestWithClosure +{ + #[When(expression: static function () { + return true; + }, constraints: [ + new NotNull(), + new NotBlank(), + ])] + private $foo; +} diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 6516a1014..630bed281 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -22,6 +22,7 @@ use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; use Symfony\Component\Validator\Tests\Constraints\Fixtures\WhenTestWithAttributes; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\WhenTestWithClosure; final class WhenTest extends TestCase { @@ -111,4 +112,38 @@ public function testAttributes() self::assertEquals([new Length(exactly: 10, groups: ['foo'])], $quuxConstraint->otherwise); self::assertSame(['foo'], $quuxConstraint->groups); } + + /** + * @requires PHP 8.5 + */ + public function testAttributesWithClosure() + { + $loader = new AttributeLoader(); + $metadata = new ClassMetadata(WhenTestWithClosure::class); + + self::assertTrue($loader->loadClassMetadata($metadata)); + + [$classConstraint] = $metadata->getConstraints(); + + self::assertInstanceOf(When::class, $classConstraint); + self::assertInstanceOf(\Closure::class, $classConstraint->expression); + self::assertEquals([ + new Callback( + callback: 'callback', + groups: ['Default', 'WhenTestWithClosure'], + ), + ], $classConstraint->constraints); + self::assertEmpty($classConstraint->otherwise); + + [$fooConstraint] = $metadata->properties['foo']->getConstraints(); + + self::assertInstanceOf(When::class, $fooConstraint); + self::assertInstanceOf(\Closure::class, $fooConstraint->expression); + self::assertEquals([ + new NotNull(groups: ['Default', 'WhenTestWithClosure']), + new NotBlank(groups: ['Default', 'WhenTestWithClosure']), + ], $fooConstraint->constraints); + self::assertEmpty($fooConstraint->otherwise); + self::assertSame(['Default', 'WhenTestWithClosure'], $fooConstraint->groups); + } } diff --git a/Tests/Constraints/WhenValidatorTest.php b/Tests/Constraints/WhenValidatorTest.php index 501398d2a..35d8b8ce9 100644 --- a/Tests/Constraints/WhenValidatorTest.php +++ b/Tests/Constraints/WhenValidatorTest.php @@ -40,6 +40,34 @@ public function testConstraintsAreExecuted() )); } + public function testConstraintsAreExecutedWhenClosureIsTrue() + { + $constraints = [ + new NotNull(), + new NotBlank(), + ]; + + $this->expectValidateValue(0, 'Foo', $constraints); + + $this->validator->validate('Foo', new When( + expression: static fn () => true, + constraints: $constraints, + )); + } + + public function testClosureTakesSubject() + { + $subject = new \stdClass(); + $this->setObject($subject); + + $this->validator->validate($subject, new When( + expression: static function ($closureSubject) use ($subject) { + self::assertSame($subject, $closureSubject); + }, + constraints: new NotNull(), + )); + } + public function testConstraintIsExecuted() { $constraint = new NotNull(); @@ -65,6 +93,20 @@ public function testOtherwiseIsExecutedWhenFalse() )); } + public function testOtherwiseIsExecutedWhenClosureReturnsFalse() + { + $constraint = new NotNull(); + $otherwise = new Length(exactly: 10); + + $this->expectValidateValue(0, 'Foo', [$otherwise]); + + $this->validator->validate('Foo', new When( + expression: static fn () => false, + constraints: $constraint, + otherwise: $otherwise, + )); + } + public function testConstraintsAreExecutedWithNull() { $constraints = [ From 15b904cb4ec4be0b83084f0f8c33898875d95c88 Mon Sep 17 00:00:00 2001 From: iraouf Date: Mon, 7 Oct 2024 23:41:27 +0200 Subject: [PATCH 124/149] [Validator] Add `filenameCharset` and `filenameCountUnit` options to `File` constraint --- CHANGELOG.md | 1 + Constraints/File.php | 31 ++++++++- Constraints/FileValidator.php | 32 ++++++++- Constraints/Image.php | 8 ++- Tests/Constraints/FileTest.php | 31 ++++++++- Tests/Constraints/FileValidatorTestCase.php | 75 ++++++++++++++++++--- 6 files changed, 163 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e421f748e..1884db589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.3 --- + * Add the `filenameCharset` and `filenameCountUnit` options to the `File` constraint * Deprecate defining custom constraints not supporting named arguments * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead * Add support for ratio checks for SVG files to the `Image` constraint diff --git a/Constraints/File.php b/Constraints/File.php index 8117339ba..7d93a2084 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\InvalidArgumentException; /** * Validates that a value is a valid "file". @@ -38,6 +39,17 @@ class File extends Constraint public const INVALID_MIME_TYPE_ERROR = '744f00bc-4389-4c74-92de-9a43cde55534'; public const INVALID_EXTENSION_ERROR = 'c8c7315c-6186-4719-8b71-5659e16bdcb7'; public const FILENAME_TOO_LONG = 'e5706483-91a8-49d8-9a59-5e81a3c634a8'; + public const FILENAME_INVALID_CHARACTERS = '04ee58e1-42b4-45c7-8423-8a4a145fedd9'; + + public const FILENAME_COUNT_BYTES = 'bytes'; + public const FILENAME_COUNT_CODEPOINTS = 'codepoints'; + public const FILENAME_COUNT_GRAPHEMES = 'graphemes'; + + private const FILENAME_VALID_COUNT_UNITS = [ + self::FILENAME_COUNT_BYTES, + self::FILENAME_COUNT_CODEPOINTS, + self::FILENAME_COUNT_GRAPHEMES, + ]; protected const ERROR_NAMES = [ self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR', @@ -47,12 +59,17 @@ class File extends Constraint self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR', self::INVALID_EXTENSION_ERROR => 'INVALID_EXTENSION_ERROR', self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG', + self::FILENAME_INVALID_CHARACTERS => 'FILENAME_INVALID_CHARACTERS', ]; public ?bool $binaryFormat = null; public array|string $mimeTypes = []; public ?int $filenameMaxLength = null; public array|string $extensions = []; + public ?string $filenameCharset = null; + /** @var self::FILENAME_COUNT_* */ + public string $filenameCountUnit = self::FILENAME_COUNT_BYTES; + public string $notFoundMessage = 'The file could not be found.'; public string $notReadableMessage = 'The file is not readable.'; public string $maxSizeMessage = 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.'; @@ -60,6 +77,7 @@ class File extends Constraint public string $extensionsMessage = 'The extension of the file is invalid ({{ extension }}). Allowed extensions are {{ extensions }}.'; public string $disallowEmptyMessage = 'An empty file is not allowed.'; public string $filenameTooLongMessage = 'The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.'; + public string $filenameCharsetMessage = 'This filename does not match the expected charset.'; public string $uploadIniSizeErrorMessage = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'; public string $uploadFormSizeErrorMessage = 'The file is too large.'; @@ -87,6 +105,8 @@ class File extends Constraint * @param string|null $uploadErrorMessage Message if an unknown error occurred on upload * @param string[]|null $groups * @param array|string|null $extensions A list of valid extensions to check. Related media types are also enforced ({@see https://symfony.com/doc/current/reference/constraints/File.html#extensions}) + * @param string|null $filenameCharset The charset to be used when computing filename length (defaults to null) + * @param self::FILENAME_COUNT_*|null $filenameCountUnit The character count unit used for checking the filename length (defaults to {@see File::FILENAME_COUNT_BYTES}) * * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types */ @@ -114,9 +134,11 @@ public function __construct( ?string $uploadErrorMessage = null, ?array $groups = null, mixed $payload = null, - array|string|null $extensions = null, ?string $extensionsMessage = null, + ?string $filenameCharset = null, + ?string $filenameCountUnit = null, + ?string $filenameCharsetMessage = null, ) { if (\is_array($options)) { trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); @@ -128,6 +150,8 @@ public function __construct( $this->binaryFormat = $binaryFormat ?? $this->binaryFormat; $this->mimeTypes = $mimeTypes ?? $this->mimeTypes; $this->filenameMaxLength = $filenameMaxLength ?? $this->filenameMaxLength; + $this->filenameCharset = $filenameCharset ?? $this->filenameCharset; + $this->filenameCountUnit = $filenameCountUnit ?? $this->filenameCountUnit; $this->extensions = $extensions ?? $this->extensions; $this->notFoundMessage = $notFoundMessage ?? $this->notFoundMessage; $this->notReadableMessage = $notReadableMessage ?? $this->notReadableMessage; @@ -136,6 +160,7 @@ public function __construct( $this->extensionsMessage = $extensionsMessage ?? $this->extensionsMessage; $this->disallowEmptyMessage = $disallowEmptyMessage ?? $this->disallowEmptyMessage; $this->filenameTooLongMessage = $filenameTooLongMessage ?? $this->filenameTooLongMessage; + $this->filenameCharsetMessage = $filenameCharsetMessage ?? $this->filenameCharsetMessage; $this->uploadIniSizeErrorMessage = $uploadIniSizeErrorMessage ?? $this->uploadIniSizeErrorMessage; $this->uploadFormSizeErrorMessage = $uploadFormSizeErrorMessage ?? $this->uploadFormSizeErrorMessage; $this->uploadPartialErrorMessage = $uploadPartialErrorMessage ?? $this->uploadPartialErrorMessage; @@ -148,6 +173,10 @@ public function __construct( if (null !== $this->maxSize) { $this->normalizeBinaryFormat($this->maxSize); } + + if (!\in_array($this->filenameCountUnit, self::FILENAME_VALID_COUNT_UNITS, true)) { + throw new InvalidArgumentException(\sprintf('The "filenameCountUnit" option must be one of the "%s::FILENAME_COUNT_*" constants ("%s" given).', __CLASS__, $this->filenameCountUnit)); + } } public function __set(string $option, mixed $value): void diff --git a/Constraints/FileValidator.php b/Constraints/FileValidator.php index 03c12b91e..2b8e33494 100644 --- a/Constraints/FileValidator.php +++ b/Constraints/FileValidator.php @@ -137,10 +137,36 @@ public function validate(mixed $value, Constraint $constraint): void return; } - $sizeInBytes = filesize($path); $basename = $value instanceof UploadedFile ? $value->getClientOriginalName() : basename($path); + $filenameCharset = $constraint->filenameCharset ?? (File::FILENAME_COUNT_BYTES !== $constraint->filenameCountUnit ? 'UTF-8' : null); + + if ($invalidFilenameCharset = null !== $filenameCharset) { + try { + $invalidFilenameCharset = !@mb_check_encoding($basename, $constraint->filenameCharset); + } catch (\ValueError $e) { + if (!str_starts_with($e->getMessage(), 'mb_check_encoding(): Argument #2 ($encoding) must be a valid encoding')) { + throw $e; + } + } + } + + $filenameLength = $invalidFilenameCharset ? 0 : match ($constraint->filenameCountUnit) { + File::FILENAME_COUNT_BYTES => \strlen($basename), + File::FILENAME_COUNT_CODEPOINTS => mb_strlen($basename, $filenameCharset), + File::FILENAME_COUNT_GRAPHEMES => grapheme_strlen($basename), + }; + + if ($invalidFilenameCharset || false === ($filenameLength ?? false)) { + $this->context->buildViolation($constraint->filenameCharsetMessage) + ->setParameter('{{ name }}', $this->formatValue($basename)) + ->setParameter('{{ charset }}', $filenameCharset) + ->setCode(File::FILENAME_INVALID_CHARACTERS) + ->addViolation(); + + return; + } - if ($constraint->filenameMaxLength && $constraint->filenameMaxLength < $filenameLength = \strlen($basename)) { + if ($constraint->filenameMaxLength && $constraint->filenameMaxLength < $filenameLength) { $this->context->buildViolation($constraint->filenameTooLongMessage) ->setParameter('{{ filename_max_length }}', $this->formatValue($constraint->filenameMaxLength)) ->setCode(File::FILENAME_TOO_LONG) @@ -150,7 +176,7 @@ public function validate(mixed $value, Constraint $constraint): void return; } - if (0 === $sizeInBytes) { + if (!$sizeInBytes = filesize($path)) { $this->context->buildViolation($constraint->disallowEmptyMessage) ->setParameter('{{ file }}', $this->formatValue($path)) ->setParameter('{{ name }}', $this->formatValue($basename)) diff --git a/Constraints/Image.php b/Constraints/Image.php index 0f045d9f9..59cd6ba88 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -165,6 +165,9 @@ public function __construct( ?string $corruptedMessage = null, ?array $groups = null, mixed $payload = null, + ?string $filenameCharset = null, + ?string $filenameCountUnit = null, + ?string $filenameCharsetMessage = null, ) { parent::__construct( $options, @@ -187,7 +190,10 @@ public function __construct( $uploadExtensionErrorMessage, $uploadErrorMessage, $groups, - $payload + $payload, + $filenameCharset, + $filenameCountUnit, + $filenameCharsetMessage, ); $this->minWidth = $minWidth ?? $this->minWidth; diff --git a/Tests/Constraints/FileTest.php b/Tests/Constraints/FileTest.php index e4e30a581..3e03f7881 100644 --- a/Tests/Constraints/FileTest.php +++ b/Tests/Constraints/FileTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; @@ -79,6 +80,31 @@ public function testMaxSizeCannotBeSetToInvalidValueAfterInitialization($maxSize $this->assertSame(1000, $file->maxSize); } + public function testFilenameMaxLength() + { + $file = new File(filenameMaxLength: 30); + $this->assertSame(30, $file->filenameMaxLength); + } + + public function testDefaultFilenameCountUnitIsUsed() + { + $file = new File(); + self::assertSame(File::FILENAME_COUNT_BYTES, $file->filenameCountUnit); + } + + public function testFilenameCharsetDefaultsToNull() + { + $file = new File(); + self::assertNull($file->filenameCharset); + } + + public function testInvalidFilenameCountUnitThrowsException() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage(\sprintf('The "filenameCountUnit" option must be one of the "%s::FILENAME_COUNT_*" constants ("%s" given).', File::class, 'nonExistentCountUnit')); + $file = new File(filenameCountUnit: 'nonExistentCountUnit'); + } + /** * @dataProvider provideInValidSizes */ @@ -162,6 +188,9 @@ public function testAttributes() self::assertSame(100000, $cConstraint->maxSize); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); + self::assertSame(30, $cConstraint->filenameMaxLength); + self::assertSame('ISO-8859-15', $cConstraint->filenameCharset); + self::assertSame(File::FILENAME_COUNT_CODEPOINTS, $cConstraint->filenameCountUnit); } } @@ -173,6 +202,6 @@ class FileDummy #[File(maxSize: 100, notFoundMessage: 'myMessage')] private $b; - #[File(maxSize: '100K', groups: ['my_group'], payload: 'some attached data')] + #[File(maxSize: '100K', filenameMaxLength: 30, filenameCharset: 'ISO-8859-15', filenameCountUnit: File::FILENAME_COUNT_CODEPOINTS, groups: ['my_group'], payload: 'some attached data')] private $c; } diff --git a/Tests/Constraints/FileValidatorTestCase.php b/Tests/Constraints/FileValidatorTestCase.php index 81e833b27..b1ebf530e 100644 --- a/Tests/Constraints/FileValidatorTestCase.php +++ b/Tests/Constraints/FileValidatorTestCase.php @@ -675,11 +675,11 @@ public function testUploadedFileExtensions() /** * @dataProvider provideFilenameMaxLengthIsTooLong */ - public function testFilenameMaxLengthIsTooLong(File $constraintFile, string $messageViolation) + public function testFilenameMaxLengthIsTooLong(File $constraintFile, string $filename, string $messageViolation) { file_put_contents($this->path, '1'); - $file = new UploadedFile($this->path, 'myFileWithATooLongOriginalFileName', null, null, true); + $file = new UploadedFile($this->path, $filename, null, null, true); $this->validator->validate($file, $constraintFile); $this->buildViolation($messageViolation) @@ -693,26 +693,83 @@ public function testFilenameMaxLengthIsTooLong(File $constraintFile, string $mes public static function provideFilenameMaxLengthIsTooLong(): \Generator { - yield 'Simple case with only the parameter "filenameMaxLength" ' => [ + yield 'Codepoints and UTF-8 : default' => [ new File(filenameMaxLength: 30), + 'myFileWithATooLongOriginalFileName', 'The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.', ]; - yield 'Case with the parameter "filenameMaxLength" and a custom error message' => [ - new File(filenameMaxLength: 20, filenameTooLongMessage: 'Your filename is too long. Please use at maximum {{ filename_max_length }} characters'), - 'Your filename is too long. Please use at maximum {{ filename_max_length }} characters', + yield 'Codepoints and UTF-8: custom error message' => [ + new File(filenameMaxLength: 20, filenameTooLongMessage: 'myMessage'), + 'myFileWithATooLongOriginalFileName', + 'myMessage', + ]; + + yield 'Graphemes' => [ + new File(filenameMaxLength: 1, filenameCountUnit: File::FILENAME_COUNT_GRAPHEMES, filenameTooLongMessage: 'myMessage'), + "A\u{0300}A\u{0300}", + 'myMessage', + ]; + + yield 'Bytes' => [ + new File(filenameMaxLength: 5, filenameCountUnit: File::FILENAME_COUNT_BYTES, filenameTooLongMessage: 'myMessage'), + "A\u{0300}A\u{0300}", + 'myMessage', ]; } - public function testFilenameMaxLength() + /** + * @dataProvider provideFilenameCountUnit + */ + public function testValidCountUnitFilenameMaxLength(int $maxLength, string $countUnit) { file_put_contents($this->path, '1'); - $file = new UploadedFile($this->path, 'tinyOriginalFileName', null, null, true); - $this->validator->validate($file, new File(filenameMaxLength: 20)); + $file = new UploadedFile($this->path, "A\u{0300}", null, null, true); + $this->validator->validate($file, new File(filenameMaxLength: $maxLength, filenameCountUnit: $countUnit)); $this->assertNoViolation(); } + /** + * @dataProvider provideFilenameCharset + */ + public function testFilenameCharset(string $filename, string $charset, bool $isValid) + { + file_put_contents($this->path, '1'); + + $file = new UploadedFile($this->path, $filename, null, null, true); + $this->validator->validate($file, new File(filenameCharset: $charset, filenameCharsetMessage: 'myMessage')); + + if ($isValid) { + $this->assertNoViolation(); + } else { + $this->buildViolation('myMessage') + ->setParameter('{{ name }}', '"'.$filename.'"') + ->setParameter('{{ charset }}', $charset) + ->setCode(File::FILENAME_INVALID_CHARACTERS) + ->assertRaised(); + } + } + + public static function provideFilenameCountUnit(): array + { + return [ + 'graphemes' => [1, File::FILENAME_COUNT_GRAPHEMES], + 'codepoints' => [2, File::FILENAME_COUNT_CODEPOINTS], + 'bytes' => [3, File::FILENAME_COUNT_BYTES], + ]; + } + + public static function provideFilenameCharset(): array + { + return [ + ['é', 'utf8', true], + ["\xE9", 'CP1252', true], + ["\xE9", 'XXX', false], + ["\xE9", 'utf8', false], + ]; + } + abstract protected function getFile($filename); } From 9bf984601dffebb3e808f6ce4291e9b167a7ee61 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Mar 2025 15:41:56 +0100 Subject: [PATCH 125/149] fix test setup and skip test until bug is fixed in PHP --- Tests/Constraints/Fixtures/WhenTestWithClosure.php | 3 ++- Tests/Constraints/WhenTest.php | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/Constraints/Fixtures/WhenTestWithClosure.php b/Tests/Constraints/Fixtures/WhenTestWithClosure.php index 56777ac7a..de0f07daa 100644 --- a/Tests/Constraints/Fixtures/WhenTestWithClosure.php +++ b/Tests/Constraints/Fixtures/WhenTestWithClosure.php @@ -11,13 +11,14 @@ namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; +use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\When; #[When(expression: static function () { return true; - }, constraints: new NotNull() + }, constraints: new Callback('isValid') )] class WhenTestWithClosure { diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index a7dea114d..4435dd88e 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -118,6 +118,8 @@ public function testAttributes() */ public function testAttributesWithClosure() { + $this->markTestSkipped('Requires https://github.com/php/php-src/issues/17851 to be fixed'); + $loader = new AttributeLoader(); $metadata = new ClassMetadata(WhenTestWithClosure::class); @@ -129,7 +131,7 @@ public function testAttributesWithClosure() self::assertInstanceOf(\Closure::class, $classConstraint->expression); self::assertEquals([ new Callback( - callback: 'callback', + callback: 'isValid', groups: ['Default', 'WhenTestWithClosure'], ), ], $classConstraint->constraints); From bc1f8c8be24ccc4c7a0d2a7840a1ccfe963409af Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Mar 2025 16:03:52 +0100 Subject: [PATCH 126/149] replace assertEmpty() with stricter assertions --- Test/CompoundConstraintTestCase.php | 3 ++- Tests/Constraints/WhenTest.php | 14 +++++++------- Tests/Mapping/Loader/PropertyInfoLoaderTest.php | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Test/CompoundConstraintTestCase.php b/Test/CompoundConstraintTestCase.php index db7021729..1a0544c82 100644 --- a/Test/CompoundConstraintTestCase.php +++ b/Test/CompoundConstraintTestCase.php @@ -102,7 +102,8 @@ protected function getConstraints(array $options): array next($expectedViolations); } - $this->assertEmpty( + $this->assertSame( + [], $failedToAssertViolations, \sprintf('Expected violation(s) for constraint(s) %s to be raised by compound.', implode(', ', array_map(fn ($violation) => ($violation->getConstraint())::class, $failedToAssertViolations)) diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 4435dd88e..a1b4bd501 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -61,7 +61,7 @@ public function testAttributes() groups: ['Default', 'WhenTestWithAttributes'], ), ], $classConstraint->constraints); - self::assertEmpty($classConstraint->otherwise); + self::assertSame([], $classConstraint->otherwise); [$fooConstraint] = $metadata->properties['foo']->getConstraints(); @@ -71,7 +71,7 @@ public function testAttributes() new NotNull(groups: ['Default', 'WhenTestWithAttributes']), new NotBlank(groups: ['Default', 'WhenTestWithAttributes']), ], $fooConstraint->constraints); - self::assertEmpty($fooConstraint->otherwise); + self::assertSame([], $fooConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithAttributes'], $fooConstraint->groups); [$barConstraint] = $metadata->properties['bar']->getConstraints(); @@ -82,7 +82,7 @@ public function testAttributes() new NotNull(groups: ['foo']), new NotBlank(groups: ['foo']), ], $barConstraint->constraints); - self::assertEmpty($barConstraint->otherwise); + self::assertSame([], $barConstraint->otherwise); self::assertSame(['foo'], $barConstraint->groups); [$quxConstraint] = $metadata->properties['qux']->getConstraints(); @@ -90,7 +90,7 @@ public function testAttributes() self::assertInstanceOf(When::class, $quxConstraint); self::assertSame('true', $quxConstraint->expression); self::assertEquals([new NotNull(groups: ['foo'])], $quxConstraint->constraints); - self::assertEmpty($quxConstraint->otherwise); + self::assertSame([], $quxConstraint->otherwise); self::assertSame(['foo'], $quxConstraint->groups); [$bazConstraint] = $metadata->getters['baz']->getConstraints(); @@ -101,7 +101,7 @@ public function testAttributes() new NotNull(groups: ['Default', 'WhenTestWithAttributes']), new NotBlank(groups: ['Default', 'WhenTestWithAttributes']), ], $bazConstraint->constraints); - self::assertEmpty($bazConstraint->otherwise); + self::assertSame([], $bazConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithAttributes'], $bazConstraint->groups); [$quuxConstraint] = $metadata->properties['quux']->getConstraints(); @@ -135,7 +135,7 @@ public function testAttributesWithClosure() groups: ['Default', 'WhenTestWithClosure'], ), ], $classConstraint->constraints); - self::assertEmpty($classConstraint->otherwise); + self::assertSame([], $classConstraint->otherwise); [$fooConstraint] = $metadata->properties['foo']->getConstraints(); @@ -145,7 +145,7 @@ public function testAttributesWithClosure() new NotNull(groups: ['Default', 'WhenTestWithClosure']), new NotBlank(groups: ['Default', 'WhenTestWithClosure']), ], $fooConstraint->constraints); - self::assertEmpty($fooConstraint->otherwise); + self::assertSame([], $fooConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithClosure'], $fooConstraint->groups); } } diff --git a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php index 6c49f0d25..ae5253a3f 100644 --- a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php +++ b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php @@ -214,7 +214,7 @@ public function getTypes(string $class, string $property, array $context = []): $this->assertInstanceOf(Iban::class, $alreadyPartiallyMappedCollectionConstraints[0]->constraints[1]); $readOnlyMetadata = $classMetadata->getPropertyMetadata('readOnly'); - $this->assertEmpty($readOnlyMetadata); + $this->assertSame([], $readOnlyMetadata); /** @var PropertyMetadata[] $noAutoMappingMetadata */ $noAutoMappingMetadata = $classMetadata->getPropertyMetadata('noAutoMapping'); @@ -298,7 +298,7 @@ public function getTypes(string $class, string $property, array $context = []): /** @var ClassMetadata $classMetadata */ $classMetadata = $validator->getMetadataFor(new PropertyInfoLoaderNoAutoMappingEntity()); - $this->assertEmpty($classMetadata->getPropertyMetadata('string')); + $this->assertSame([], $classMetadata->getPropertyMetadata('string')); $this->assertCount(2, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->constraints); $this->assertSame(AutoMappingStrategy::ENABLED, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->getAutoMappingStrategy()); } From 9950d2e3af00a7261691033d9229405993cbf9dc Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 6 Mar 2025 15:18:16 +0100 Subject: [PATCH 127/149] [Validator] Fix docblock of `All` constraint constructor --- Constraints/All.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Constraints/All.php b/Constraints/All.php index 1da939dd5..128454584 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -25,8 +25,8 @@ class All extends Composite public array|Constraint $constraints = []; /** - * @param array|array|null $constraints - * @param string[]|null $groups + * @param array|array|Constraint|null $constraints + * @param string[]|null $groups */ public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { From f8d1c698576d7771e62506464ee8dc377c405393 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 8 Mar 2025 00:16:33 +0100 Subject: [PATCH 128/149] chore: PHP CS Fixer fixes --- Constraints/Sequentially.php | 2 +- Constraints/ZeroComparisonConstraintTrait.php | 4 ++-- Tests/Constraints/CardSchemeValidatorTest.php | 2 +- Tests/Constraints/ChoiceValidatorTest.php | 3 +++ Tests/Constraints/CountValidatorTestCase.php | 6 ++++++ Tests/Constraints/ImageValidatorTest.php | 2 +- Tests/Constraints/IsbnValidatorTest.php | 2 ++ Tests/Constraints/LengthValidatorTest.php | 2 +- Tests/Constraints/RangeValidatorTest.php | 10 ++++++++++ Tests/Constraints/RegexValidatorTest.php | 2 ++ Tests/Mapping/Loader/FilesLoaderTest.php | 6 ++++-- 11 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index bdedb2a5e..1096a994d 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -30,7 +30,7 @@ class Sequentially extends Composite */ public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { - if (is_array($constraints) && !array_is_list($constraints)) { + if (\is_array($constraints) && !array_is_list($constraints)) { trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } diff --git a/Constraints/ZeroComparisonConstraintTrait.php b/Constraints/ZeroComparisonConstraintTrait.php index d8479c20f..d0841adea 100644 --- a/Constraints/ZeroComparisonConstraintTrait.php +++ b/Constraints/ZeroComparisonConstraintTrait.php @@ -29,11 +29,11 @@ public function __construct(?array $options = null, ?string $message = null, ?ar trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } - if (is_array($options) && isset($options['propertyPath'])) { + if (\is_array($options) && isset($options['propertyPath'])) { throw new ConstraintDefinitionException(\sprintf('The "propertyPath" option of the "%s" constraint cannot be set.', static::class)); } - if (is_array($options) && isset($options['value'])) { + if (\is_array($options) && isset($options['value'])) { throw new ConstraintDefinitionException(\sprintf('The "value" option of the "%s" constraint cannot be set.', static::class)); } diff --git a/Tests/Constraints/CardSchemeValidatorTest.php b/Tests/Constraints/CardSchemeValidatorTest.php index 87b1daebc..d70457833 100644 --- a/Tests/Constraints/CardSchemeValidatorTest.php +++ b/Tests/Constraints/CardSchemeValidatorTest.php @@ -31,7 +31,7 @@ public function testNullIsValid() public function testEmptyStringIsValid() { - $this->validator->validate('', new CardScheme(schemes:[])); + $this->validator->validate('', new CardScheme(schemes: [])); $this->assertNoViolation(); } diff --git a/Tests/Constraints/ChoiceValidatorTest.php b/Tests/Constraints/ChoiceValidatorTest.php index a219e44d8..39affe442 100644 --- a/Tests/Constraints/ChoiceValidatorTest.php +++ b/Tests/Constraints/ChoiceValidatorTest.php @@ -92,6 +92,7 @@ public static function provideConstraintsWithChoicesArray(): iterable /** * @group legacy + * * @dataProvider provideLegacyConstraintsWithChoicesArrayDoctrineStyle */ public function testValidChoiceArrayDoctrineStyle(Choice $constraint) @@ -126,6 +127,7 @@ public static function provideConstraintsWithCallbackFunction(): iterable /** * @group legacy + * * @dataProvider provideLegacyConstraintsWithCallbackFunctionDoctrineStyle */ public function testValidChoiceCallbackFunctionDoctrineStyle(Choice $constraint) @@ -262,6 +264,7 @@ public function testInvalidChoiceMultiple() ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } + /** * @group legacy */ diff --git a/Tests/Constraints/CountValidatorTestCase.php b/Tests/Constraints/CountValidatorTestCase.php index f60199027..da319cd8b 100644 --- a/Tests/Constraints/CountValidatorTestCase.php +++ b/Tests/Constraints/CountValidatorTestCase.php @@ -71,6 +71,7 @@ public static function getFiveOrMoreElements() /** * @group legacy + * * @dataProvider getThreeOrLessElements */ public function testValidValuesMax($value) @@ -94,6 +95,7 @@ public function testValidValuesMaxNamed($value) /** * @group legacy + * * @dataProvider getFiveOrMoreElements */ public function testValidValuesMin($value) @@ -117,6 +119,7 @@ public function testValidValuesMinNamed($value) /** * @group legacy + * * @dataProvider getFourElements */ public function testValidValuesExact($value) @@ -140,6 +143,7 @@ public function testValidValuesExactNamed($value) /** * @group legacy + * * @dataProvider getFiveOrMoreElements */ public function testTooManyValues($value) @@ -180,6 +184,7 @@ public function testTooManyValuesNamed($value) /** * @group legacy + * * @dataProvider getThreeOrLessElements */ public function testTooFewValues($value) @@ -220,6 +225,7 @@ public function testTooFewValuesNamed($value) /** * @group legacy + * * @dataProvider getFiveOrMoreElements */ public function testTooManyValuesExact($value) diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index 8811c5774..a79bd9dd4 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -545,7 +545,7 @@ public function testInvalidMimeTypeWithNarrowedSetDoctrineStyle() ])); $this->buildViolation('The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.') - ->setParameter('{{ file }}', sprintf('"%s"', $this->image)) + ->setParameter('{{ file }}', \sprintf('"%s"', $this->image)) ->setParameter('{{ type }}', '"image/gif"') ->setParameter('{{ types }}', '"image/jpeg", "image/png"') ->setParameter('{{ name }}', '"test.gif"') diff --git a/Tests/Constraints/IsbnValidatorTest.php b/Tests/Constraints/IsbnValidatorTest.php index 9d2336f7f..f9393158d 100644 --- a/Tests/Constraints/IsbnValidatorTest.php +++ b/Tests/Constraints/IsbnValidatorTest.php @@ -159,6 +159,7 @@ public function testValidIsbn10($isbn) /** * @group legacy + * * @dataProvider getInvalidIsbn10 */ public function testInvalidIsbn10($isbn, $code) @@ -203,6 +204,7 @@ public function testValidIsbn13($isbn) /** * @group legacy + * * @dataProvider getInvalidIsbn13 */ public function testInvalidIsbn13($isbn, $code) diff --git a/Tests/Constraints/LengthValidatorTest.php b/Tests/Constraints/LengthValidatorTest.php index 0c228f25d..10f61f50b 100644 --- a/Tests/Constraints/LengthValidatorTest.php +++ b/Tests/Constraints/LengthValidatorTest.php @@ -271,7 +271,7 @@ public function testInvalidValuesExactLessThanFour(int|string $value, int $value $constraint = new Length( min: 4, max: 4, - exactMessage: 'myMessage', + exactMessage: 'myMessage', ); $this->validator->validate($value, $constraint); diff --git a/Tests/Constraints/RangeValidatorTest.php b/Tests/Constraints/RangeValidatorTest.php index f8765af18..423c8d460 100644 --- a/Tests/Constraints/RangeValidatorTest.php +++ b/Tests/Constraints/RangeValidatorTest.php @@ -71,6 +71,7 @@ public static function getMoreThanTwenty(): array /** * @group legacy + * * @dataProvider getTenToTwenty */ public function testValidValuesMin($value) @@ -94,6 +95,7 @@ public function testValidValuesMinNamed($value) /** * @group legacy + * * @dataProvider getTenToTwenty */ public function testValidValuesMax($value) @@ -117,6 +119,7 @@ public function testValidValuesMaxNamed($value) /** * @group legacy + * * @dataProvider getTenToTwenty */ public function testValidValuesMinMax($value) @@ -140,6 +143,7 @@ public function testValidValuesMinMaxNamed($value) /** * @group legacy + * * @dataProvider getLessThanTen */ public function testInvalidValuesMin($value, $formattedValue) @@ -176,6 +180,7 @@ public function testInvalidValuesMinNamed($value, $formattedValue) /** * @group legacy + * * @dataProvider getMoreThanTwenty */ public function testInvalidValuesMax($value, $formattedValue) @@ -212,6 +217,7 @@ public function testInvalidValuesMaxNamed($value, $formattedValue) /** * @group legacy + * * @dataProvider getMoreThanTwenty */ public function testInvalidValuesCombinedMax($value, $formattedValue) @@ -251,6 +257,7 @@ public function testInvalidValuesCombinedMaxNamed($value, $formattedValue) /** * @group legacy + * * @dataProvider getLessThanTen */ public function testInvalidValuesCombinedMin($value, $formattedValue) @@ -629,6 +636,7 @@ public function testNoViolationOnNullObjectWithPropertyPaths() /** * @group legacy + * * @dataProvider getTenToTwenty */ public function testValidValuesMinPropertyPath($value) @@ -741,6 +749,7 @@ public function testInvalidValuesMaxPropertyPath($value, $formattedValue) /** * @group legacy + * * @dataProvider getMoreThanTwenty */ public function testInvalidValuesCombinedMaxPropertyPath($value, $formattedValue) @@ -792,6 +801,7 @@ public function testInvalidValuesCombinedMaxPropertyPathNamed($value, $formatted /** * @group legacy + * * @dataProvider getLessThanTen */ public function testInvalidValuesCombinedMinPropertyPath($value, $formattedValue) diff --git a/Tests/Constraints/RegexValidatorTest.php b/Tests/Constraints/RegexValidatorTest.php index 576df3748..bafc752c3 100644 --- a/Tests/Constraints/RegexValidatorTest.php +++ b/Tests/Constraints/RegexValidatorTest.php @@ -56,6 +56,7 @@ public function testValidValues($value) /** * @group legacy + * * @dataProvider getValidValuesWithWhitespaces */ public function testValidValuesWithWhitespaces($value) @@ -107,6 +108,7 @@ public static function getValidValuesWithWhitespaces() /** * @group legacy + * * @dataProvider getInvalidValues */ public function testInvalidValues($value) diff --git a/Tests/Mapping/Loader/FilesLoaderTest.php b/Tests/Mapping/Loader/FilesLoaderTest.php index 765b84fd6..ffb0dd23b 100644 --- a/Tests/Mapping/Loader/FilesLoaderTest.php +++ b/Tests/Mapping/Loader/FilesLoaderTest.php @@ -36,11 +36,13 @@ public function testCallsActualFileLoaderForMetadata() public function getFilesLoader(LoaderInterface $loader) { - return new class([ + $files = [ __DIR__.'/constraint-mapping.xml', __DIR__.'/constraint-mapping.yaml', __DIR__.'/constraint-mapping.test', __DIR__.'/constraint-mapping.txt', - ], $loader) extends FilesLoader {}; + ]; + + return new class($files, $loader) extends FilesLoader {}; } } From 90190df2419927059b9c286a7f2fa6db62148885 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 27 Mar 2025 11:21:50 +0100 Subject: [PATCH 129/149] [Validator] Don't skip `WhenTest` case after fix merge in `php-src` --- Tests/Constraints/WhenTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index a1b4bd501..8f04376b3 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -118,8 +118,6 @@ public function testAttributes() */ public function testAttributesWithClosure() { - $this->markTestSkipped('Requires https://github.com/php/php-src/issues/17851 to be fixed'); - $loader = new AttributeLoader(); $metadata = new ClassMetadata(WhenTestWithClosure::class); From b823796406903ae9fe6a27536afd12db45753475 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Wed, 9 Apr 2025 10:17:22 +0200 Subject: [PATCH 130/149] [Validator] fix php doc --- Constraints/Type.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Constraints/Type.php b/Constraints/Type.php index 0482ff253..eb410bb8a 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -31,9 +31,9 @@ class Type extends Constraint public string|array|null $type = null; /** - * @param string|string[]|array|null $type The type(s) to enforce on the value - * @param string[]|null $groups - * @param array $options + * @param string|list|array|null $type The type(s) to enforce on the value + * @param string[]|null $groups + * @param array $options */ public function __construct(string|array|null $type, ?string $message = null, ?array $groups = null, mixed $payload = null, array $options = []) { From d0f8a8f7f754f809e758ae55463862ef8d3a0c4e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 18 Apr 2025 14:51:48 +0200 Subject: [PATCH 131/149] Don't enable tracing unless the profiler is enabled --- Validator/TraceableValidator.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Validator/TraceableValidator.php b/Validator/TraceableValidator.php index 5442c53da..6f9ab5bbc 100644 --- a/Validator/TraceableValidator.php +++ b/Validator/TraceableValidator.php @@ -29,6 +29,7 @@ class TraceableValidator implements ValidatorInterface, ResetInterface public function __construct( private ValidatorInterface $validator, + protected readonly ?\Closure $disabled = null, ) { } @@ -56,6 +57,10 @@ public function validate(mixed $value, Constraint|array|null $constraints = null { $violations = $this->validator->validate($value, $constraints, $groups); + if ($this->disabled?->__invoke()) { + return $violations; + } + $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 7); $file = $trace[0]['file']; From 1922a53e7e3442d0e5a6e14f39bc48f6262a0cd0 Mon Sep 17 00:00:00 2001 From: Oleg Zhulnev Date: Fri, 25 Apr 2025 13:57:18 +0300 Subject: [PATCH 132/149] [Validator] [WordCount] Treat 0 as one character word and do not exclude it --- Constraints/WordCountValidator.php | 2 +- Tests/Constraints/WordCountValidatorTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Constraints/WordCountValidator.php b/Constraints/WordCountValidator.php index ee090de26..0fe6e885a 100644 --- a/Constraints/WordCountValidator.php +++ b/Constraints/WordCountValidator.php @@ -44,7 +44,7 @@ public function validate(mixed $value, Constraint $constraint): void $words = iterator_to_array($iterator->getPartsIterator()); // erase "blank words" and don't count them as words - $wordsCount = \count(array_filter(array_map(trim(...), $words))); + $wordsCount = \count(array_filter(array_map(trim(...), $words), fn ($word) => '' !== $word)); if (null !== $constraint->min && $wordsCount < $constraint->min) { $this->context->buildViolation($constraint->minMessage) diff --git a/Tests/Constraints/WordCountValidatorTest.php b/Tests/Constraints/WordCountValidatorTest.php index 3e3b760c4..ce1256f92 100644 --- a/Tests/Constraints/WordCountValidatorTest.php +++ b/Tests/Constraints/WordCountValidatorTest.php @@ -83,6 +83,7 @@ public static function provideValidValues() yield [new StringableValue('my ûtf 8'), 3]; yield [null, 1]; // null should always pass and eventually be handled by NotNullValidator yield ['', 1]; // empty string should always pass and eventually be handled by NotBlankValidator + yield ['My String 0', 3]; } public static function provideInvalidTypes() From 733c4c82a66437f3100d262555a5fe9f76bd2ac6 Mon Sep 17 00:00:00 2001 From: Chris Shennan Date: Thu, 1 May 2025 17:27:33 +0100 Subject: [PATCH 133/149] docs: Update @param for $match to reflect the correct default value. --- Constraints/Regex.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 4a2a90610..d10cb5fa8 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -38,7 +38,7 @@ class Regex extends Constraint /** * @param string|array|null $pattern The regular expression to match * @param string|null $htmlPattern The pattern to use in the HTML5 pattern attribute - * @param bool|null $match Whether to validate the value matches the configured pattern or not (defaults to false) + * @param bool|null $match Whether to validate the value matches the configured pattern or not (defaults to true) * @param string[]|null $groups * @param array $options */ From 9ded0f389dfb8f4473b580d9b874052bc2385b3d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 15 May 2025 09:25:30 +0200 Subject: [PATCH 134/149] let the SlugValidator accept AsciiSlugger results --- Constraints/Slug.php | 2 +- Tests/Constraints/SlugValidatorTest.php | 19 ++++++++++++++++--- composer.json | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Constraints/Slug.php b/Constraints/Slug.php index 52a5d94c2..55d847ccb 100644 --- a/Constraints/Slug.php +++ b/Constraints/Slug.php @@ -25,7 +25,7 @@ class Slug extends Constraint public const NOT_SLUG_ERROR = '14e6df1e-c8ab-4395-b6ce-04b132a3765e'; public string $message = 'This value is not a valid slug.'; - public string $regex = '/^[a-z0-9]+(?:-[a-z0-9]+)*$/'; + public string $regex = '/^[a-z0-9]+(?:-[a-z0-9]+)*$/i'; #[HasNamedArguments] public function __construct( diff --git a/Tests/Constraints/SlugValidatorTest.php b/Tests/Constraints/SlugValidatorTest.php index e8d210b83..f88bff3e1 100644 --- a/Tests/Constraints/SlugValidatorTest.php +++ b/Tests/Constraints/SlugValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use Symfony\Component\String\Slugger\AsciiSlugger; use Symfony\Component\Validator\Constraints\Slug; use Symfony\Component\Validator\Constraints\SlugValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -47,6 +48,7 @@ public function testExpectsStringCompatibleType() * @testWith ["test-slug"] * ["slug-123-test"] * ["slug"] + * ["TestSlug"] */ public function testValidSlugs($slug) { @@ -56,8 +58,7 @@ public function testValidSlugs($slug) } /** - * @testWith ["NotASlug"] - * ["Not a slug"] + * @testWith ["Not a slug"] * ["not-á-slug"] * ["not-@-slug"] */ @@ -91,7 +92,7 @@ public function testCustomRegexInvalidSlugs($slug) /** * @testWith ["slug"] - * @testWith ["test1234"] + * ["test1234"] */ public function testCustomRegexValidSlugs($slug) { @@ -101,4 +102,16 @@ public function testCustomRegexValidSlugs($slug) $this->assertNoViolation(); } + + /** + * @testWith ["PHP"] + * ["Symfony is cool"] + * ["Lorem ipsum dolor sit amet"] + */ + public function testAcceptAsciiSluggerResults(string $text) + { + $this->validator->validate((new AsciiSlugger())->slug($text), new Slug()); + + $this->assertNoViolation(); + } } diff --git a/composer.json b/composer.json index 5177d37d2..0eaac5f6b 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "symfony/mime": "^6.4|^7.0", "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4.3|^7.0.3", "symfony/type-info": "^7.1", "egulias/email-validator": "^2.1.10|^3|^4" From 4e192e532505fa6226f09a580fd0c5d1eaa63b62 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Mon, 19 May 2025 10:27:13 +0200 Subject: [PATCH 135/149] Revert Slug constraint This commit reverts #58542. --- CHANGELOG.md | 1 - Constraints/Slug.php | 42 --------- Constraints/SlugValidator.php | 47 ---------- Tests/Constraints/SlugTest.php | 47 ---------- Tests/Constraints/SlugValidatorTest.php | 117 ------------------------ 5 files changed, 254 deletions(-) delete mode 100644 Constraints/Slug.php delete mode 100644 Constraints/SlugValidator.php delete mode 100644 Tests/Constraints/SlugTest.php delete mode 100644 Tests/Constraints/SlugValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ae1ae20da..a7363d7f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ CHANGELOG * Deprecate defining custom constraints not supporting named arguments * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead * Add support for ratio checks for SVG files to the `Image` constraint - * Add the `Slug` constraint * Add support for the `otherwise` option in the `When` constraint * Add support for multiple fields containing nested constraints in `Composite` constraints * Add the `stopOnFirstError` option to the `Unique` constraint to validate all elements diff --git a/Constraints/Slug.php b/Constraints/Slug.php deleted file mode 100644 index 55d847ccb..000000000 --- a/Constraints/Slug.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Constraints; - -use Symfony\Component\Validator\Attribute\HasNamedArguments; -use Symfony\Component\Validator\Constraint; - -/** - * Validates that a value is a valid slug. - * - * @author Raffaele Carelle - */ -#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class Slug extends Constraint -{ - public const NOT_SLUG_ERROR = '14e6df1e-c8ab-4395-b6ce-04b132a3765e'; - - public string $message = 'This value is not a valid slug.'; - public string $regex = '/^[a-z0-9]+(?:-[a-z0-9]+)*$/i'; - - #[HasNamedArguments] - public function __construct( - ?string $regex = null, - ?string $message = null, - ?array $groups = null, - mixed $payload = null, - ) { - parent::__construct([], $groups, $payload); - - $this->message = $message ?? $this->message; - $this->regex = $regex ?? $this->regex; - } -} diff --git a/Constraints/SlugValidator.php b/Constraints/SlugValidator.php deleted file mode 100644 index b914cad31..000000000 --- a/Constraints/SlugValidator.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Constraints; - -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Symfony\Component\Validator\Exception\UnexpectedValueException; - -/** - * @author Raffaele Carelle - */ -class SlugValidator extends ConstraintValidator -{ - public function validate(mixed $value, Constraint $constraint): void - { - if (!$constraint instanceof Slug) { - throw new UnexpectedTypeException($constraint, Slug::class); - } - - if (null === $value || '' === $value) { - return; - } - - if (!\is_scalar($value) && !$value instanceof \Stringable) { - throw new UnexpectedValueException($value, 'string'); - } - - $value = (string) $value; - - if (0 === preg_match($constraint->regex, $value)) { - $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Slug::NOT_SLUG_ERROR) - ->addViolation(); - } - } -} diff --git a/Tests/Constraints/SlugTest.php b/Tests/Constraints/SlugTest.php deleted file mode 100644 index a2c5b07d3..000000000 --- a/Tests/Constraints/SlugTest.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Constraints; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Validator\Constraints\Slug; -use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; - -class SlugTest extends TestCase -{ - public function testAttributes() - { - $metadata = new ClassMetadata(SlugDummy::class); - $loader = new AttributeLoader(); - self::assertTrue($loader->loadClassMetadata($metadata)); - - [$bConstraint] = $metadata->properties['b']->getConstraints(); - self::assertSame('myMessage', $bConstraint->message); - self::assertSame(['Default', 'SlugDummy'], $bConstraint->groups); - - [$cConstraint] = $metadata->properties['c']->getConstraints(); - self::assertSame(['my_group'], $cConstraint->groups); - self::assertSame('some attached data', $cConstraint->payload); - } -} - -class SlugDummy -{ - #[Slug] - private $a; - - #[Slug(message: 'myMessage')] - private $b; - - #[Slug(groups: ['my_group'], payload: 'some attached data')] - private $c; -} diff --git a/Tests/Constraints/SlugValidatorTest.php b/Tests/Constraints/SlugValidatorTest.php deleted file mode 100644 index f88bff3e1..000000000 --- a/Tests/Constraints/SlugValidatorTest.php +++ /dev/null @@ -1,117 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Constraints; - -use Symfony\Component\String\Slugger\AsciiSlugger; -use Symfony\Component\Validator\Constraints\Slug; -use Symfony\Component\Validator\Constraints\SlugValidator; -use Symfony\Component\Validator\Exception\UnexpectedValueException; -use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; - -class SlugValidatorTest extends ConstraintValidatorTestCase -{ - protected function createValidator(): SlugValidator - { - return new SlugValidator(); - } - - public function testNullIsValid() - { - $this->validator->validate(null, new Slug()); - - $this->assertNoViolation(); - } - - public function testEmptyStringIsValid() - { - $this->validator->validate('', new Slug()); - - $this->assertNoViolation(); - } - - public function testExpectsStringCompatibleType() - { - $this->expectException(UnexpectedValueException::class); - $this->validator->validate(new \stdClass(), new Slug()); - } - - /** - * @testWith ["test-slug"] - * ["slug-123-test"] - * ["slug"] - * ["TestSlug"] - */ - public function testValidSlugs($slug) - { - $this->validator->validate($slug, new Slug()); - - $this->assertNoViolation(); - } - - /** - * @testWith ["Not a slug"] - * ["not-á-slug"] - * ["not-@-slug"] - */ - public function testInvalidSlugs($slug) - { - $constraint = new Slug(message: 'myMessage'); - - $this->validator->validate($slug, $constraint); - - $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"'.$slug.'"') - ->setCode(Slug::NOT_SLUG_ERROR) - ->assertRaised(); - } - - /** - * @testWith ["test-slug", true] - * ["slug-123-test", true] - */ - public function testCustomRegexInvalidSlugs($slug) - { - $constraint = new Slug(regex: '/^[a-z0-9]+$/i'); - - $this->validator->validate($slug, $constraint); - - $this->buildViolation($constraint->message) - ->setParameter('{{ value }}', '"'.$slug.'"') - ->setCode(Slug::NOT_SLUG_ERROR) - ->assertRaised(); - } - - /** - * @testWith ["slug"] - * ["test1234"] - */ - public function testCustomRegexValidSlugs($slug) - { - $constraint = new Slug(regex: '/^[a-z0-9]+$/i'); - - $this->validator->validate($slug, $constraint); - - $this->assertNoViolation(); - } - - /** - * @testWith ["PHP"] - * ["Symfony is cool"] - * ["Lorem ipsum dolor sit amet"] - */ - public function testAcceptAsciiSluggerResults(string $text) - { - $this->validator->validate((new AsciiSlugger())->slug($text), new Slug()); - - $this->assertNoViolation(); - } -} From 41cc2fe55553a88b99bdd2d855f1660efc817a98 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 21 May 2025 09:40:19 +0200 Subject: [PATCH 136/149] fix merge --- Constraints/Image.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Constraints/Image.php b/Constraints/Image.php index 0dd09bf1f..5812b9a49 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -58,7 +58,7 @@ class Image extends File self::CORRUPTED_IMAGE_ERROR => 'CORRUPTED_IMAGE_ERROR', ]; - public array|string $mimeTypes = 'image/*'; + public array|string $mimeTypes = []; public ?int $minWidth = null; public ?int $maxWidth = null; public ?int $maxHeight = null; @@ -220,7 +220,7 @@ public function __construct( $this->allowPortraitMessage = $allowPortraitMessage ?? $this->allowPortraitMessage; $this->corruptedMessage = $corruptedMessage ?? $this->corruptedMessage; - if (null === $this->mimeTypes && [] === $this->extensions) { + if ([] === $this->mimeTypes && [] === $this->extensions) { $this->mimeTypes = 'image/*'; } From 715fd0b66872656290f26672b9cb77b0c5892b8c Mon Sep 17 00:00:00 2001 From: tcoch Date: Wed, 14 May 2025 16:12:06 +0200 Subject: [PATCH 137/149] [Validator] Add tests for `MacAddress` --- Tests/Constraints/MacAddressValidatorTest.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Tests/Constraints/MacAddressValidatorTest.php b/Tests/Constraints/MacAddressValidatorTest.php index d755df486..5abb7487b 100644 --- a/Tests/Constraints/MacAddressValidatorTest.php +++ b/Tests/Constraints/MacAddressValidatorTest.php @@ -63,6 +63,19 @@ public function testValidMac($mac) $this->assertNoViolation(); } + /** + * @dataProvider getNotValidMacs + */ + public function testNotValidMac($mac) + { + $this->validator->validate($mac, new MacAddress()); + + $this->buildViolation('This value is not a valid MAC address.') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + public static function getValidMacs(): array { return [ @@ -76,6 +89,17 @@ public static function getValidMacs(): array ]; } + public static function getNotValidMacs(): array + { + return [ + ['00:00:00:00:00'], + ['00:00:00:00:00:0G'], + ['GG:GG:GG:GG:GG:GG'], + ['GG-GG-GG-GG-GG-GG'], + ['GGGG.GGGG.GGGG'], + ]; + } + public static function getValidLocalUnicastMacs(): array { return [ From a9e21997ff6ea9fc13a93572633488582301a36b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 21:58:06 +0200 Subject: [PATCH 138/149] re-add accidentally removed changelog examples --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7363d7f5..e8146d2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,55 @@ CHANGELOG * Add the `filenameCharset` and `filenameCountUnit` options to the `File` constraint * Deprecate defining custom constraints not supporting named arguments + + Before: + + ```php + use Symfony\Component\Validator\Constraint; + + class CustomConstraint extends Constraint + { + public function __construct(array $options) + { + // ... + } + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Attribute\HasNamedArguments; + use Symfony\Component\Validator\Constraint; + + class CustomConstraint extends Constraint + { + #[HasNamedArguments] + public function __construct($option1, $option2, $groups, $payload) + { + // ... + } + } + ``` * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead + + Before: + + ```php + new NotNull([ + 'groups' => ['foo', 'bar'], + 'message' => 'a custom constraint violation message', + ]) + ``` + + After: + + ```php + new NotNull( + groups: ['foo', 'bar'], + message: 'a custom constraint violation message', + ) + ``` * Add support for ratio checks for SVG files to the `Image` constraint * Add support for the `otherwise` option in the `When` constraint * Add support for multiple fields containing nested constraints in `Composite` constraints From 230dd2f02ead4d83d18dbc43bd6316babb3ad85b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 15 Jun 2025 12:48:34 +0200 Subject: [PATCH 139/149] fix merge --- Tests/Constraints/CascadeTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/Constraints/CascadeTest.php b/Tests/Constraints/CascadeTest.php index 2ef4c9c83..fc4d7ce0f 100644 --- a/Tests/Constraints/CascadeTest.php +++ b/Tests/Constraints/CascadeTest.php @@ -35,6 +35,9 @@ public function testExcludeProperties() self::assertSame(['foo' => 0, 'bar' => 1], $constraint->exclude); } + /** + * @group legacy + */ public function testExcludePropertiesDoctrineStyle() { $constraint = new Cascade(['exclude' => ['foo', 'bar']]); From a19337e1324736024cfd74a743586e98791a779d Mon Sep 17 00:00:00 2001 From: JK Groupe Date: Thu, 19 Jun 2025 16:15:46 +0200 Subject: [PATCH 140/149] [Validator] Add missing HasNamedArguments to some constraints --- Constraints/AtLeastOneOf.php | 2 ++ Constraints/Composite.php | 2 ++ Constraints/Compound.php | 2 ++ Constraints/GroupSequence.php | 3 +++ Constraints/Image.php | 3 +++ Constraints/Sequentially.php | 2 ++ 6 files changed, 14 insertions(+) diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index b20ea0df0..20d55f458 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -39,6 +40,7 @@ class AtLeastOneOf extends Composite * @param string|null $messageCollection Failure message for All and Collection inner constraints * @param bool|null $includeInternalMessages Whether to include inner constraint messages (defaults to true) */ + #[HasNamedArguments] public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null, ?string $message = null, ?string $messageCollection = null, ?bool $includeInternalMessages = null) { if (\is_array($constraints) && !array_is_list($constraints)) { diff --git a/Constraints/Composite.php b/Constraints/Composite.php index deac22cc5..ce6283b84 100644 --- a/Constraints/Composite.php +++ b/Constraints/Composite.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -49,6 +50,7 @@ abstract class Composite extends Constraint * cached. When constraints are loaded from the cache, no more group * checks need to be done. */ + #[HasNamedArguments] public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Constraints/Compound.php b/Constraints/Compound.php index ac2b5ac98..261871533 100644 --- a/Constraints/Compound.php +++ b/Constraints/Compound.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -24,6 +25,7 @@ abstract class Compound extends Composite /** @var Constraint[] */ public array $constraints = []; + #[HasNamedArguments] public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null) { if (isset($options[$this->getCompositeOption()])) { diff --git a/Constraints/GroupSequence.php b/Constraints/GroupSequence.php index 3c2cc48ba..e3e4f47f9 100644 --- a/Constraints/GroupSequence.php +++ b/Constraints/GroupSequence.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; + /** * A sequence of validation groups. * @@ -75,6 +77,7 @@ class GroupSequence * * @param array $groups The groups in the sequence */ + #[HasNamedArguments] public function __construct(array $groups) { $this->groups = $groups['value'] ?? $groups; diff --git a/Constraints/Image.php b/Constraints/Image.php index 5a4b3e129..d9b7c8822 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; + /** * Validates that a file (or a path to a file) is a valid image. * @@ -118,6 +120,7 @@ class Image extends File * * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types */ + #[HasNamedArguments] public function __construct( ?array $options = null, int|string|null $maxSize = null, diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index 1096a994d..6389ebb89 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; /** @@ -28,6 +29,7 @@ class Sequentially extends Composite * @param Constraint[]|array|null $constraints An array of validation constraints * @param string[]|null $groups */ + #[HasNamedArguments] public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) { if (\is_array($constraints) && !array_is_list($constraints)) { From e2f2497c869fc57446f735fbf00cff4de32ae8c3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 26 Jun 2025 15:22:23 +0200 Subject: [PATCH 141/149] fix merge --- Tests/Constraints/LocaleValidatorTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tests/Constraints/LocaleValidatorTest.php b/Tests/Constraints/LocaleValidatorTest.php index 9ca252cf8..5a060e4da 100644 --- a/Tests/Constraints/LocaleValidatorTest.php +++ b/Tests/Constraints/LocaleValidatorTest.php @@ -91,9 +91,7 @@ public static function getInvalidLocales() public function testTooLongLocale() { - $constraint = new Locale([ - 'message' => 'myMessage', - ]); + $constraint = new Locale(message: 'myMessage'); $locale = str_repeat('a', (\defined('INTL_MAX_LOCALE_LEN') ? \INTL_MAX_LOCALE_LEN : 85) + 1); $this->validator->validate($locale, $constraint); From 5d34deb2e02f0e71297ca83634694628d177f683 Mon Sep 17 00:00:00 2001 From: Michael Telgmann Date: Wed, 2 Jul 2025 10:33:05 +0200 Subject: [PATCH 142/149] chore: Increase minimum version of type-info component --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5177d37d2..1ef7d5964 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation": "^6.4.3|^7.0.3", - "symfony/type-info": "^7.1", + "symfony/type-info": "^7.1.8", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { From 8cc864a45f39eece0da77afd607c016f4e003be7 Mon Sep 17 00:00:00 2001 From: thePanz Date: Mon, 7 Jul 2025 13:53:10 +0200 Subject: [PATCH 143/149] Allow mixed root on CompoundConstraintTestCase validator --- Test/CompoundConstraintTestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/CompoundConstraintTestCase.php b/Test/CompoundConstraintTestCase.php index db7021729..462d966be 100644 --- a/Test/CompoundConstraintTestCase.php +++ b/Test/CompoundConstraintTestCase.php @@ -35,7 +35,7 @@ abstract class CompoundConstraintTestCase extends TestCase protected ValidatorInterface $validator; protected ?ConstraintViolationListInterface $violationList = null; protected ExecutionContextInterface $context; - protected string $root; + protected mixed $root; private mixed $validatedValue; From bb88dce92bb63015b72913adfd3e2134960b3c96 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 15 Jul 2025 23:14:40 +0200 Subject: [PATCH 144/149] fix merge --- Tests/Constraints/ExpressionTest.php | 3 +++ Tests/Constraints/RegexTest.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Tests/Constraints/ExpressionTest.php b/Tests/Constraints/ExpressionTest.php index 97fcab838..07abb071d 100644 --- a/Tests/Constraints/ExpressionTest.php +++ b/Tests/Constraints/ExpressionTest.php @@ -51,6 +51,9 @@ public function testMissingPattern() new Expression(null); } + /** + * @group legacy + */ public function testMissingPatternDoctrineStyle() { $this->expectException(MissingOptionsException::class); diff --git a/Tests/Constraints/RegexTest.php b/Tests/Constraints/RegexTest.php index 3d13dcdc9..853e0d785 100644 --- a/Tests/Constraints/RegexTest.php +++ b/Tests/Constraints/RegexTest.php @@ -148,6 +148,9 @@ public function testMissingPattern() new Regex(null); } + /** + * @group legacy + */ public function testMissingPatternDoctrineStyle() { $this->expectException(MissingOptionsException::class); From 2f893f1f970a0ae5670cb82bb3bcf8196b487b55 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 15 Jul 2025 23:26:24 +0200 Subject: [PATCH 145/149] error if the fields option is missing for the Collection constraint --- Constraints/Collection.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Constraints/Collection.php b/Constraints/Collection.php index eca5a4eee..3de2c14ab 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -45,13 +45,20 @@ class Collection extends Composite #[HasNamedArguments] public function __construct(mixed $fields = null, ?array $groups = null, mixed $payload = null, ?bool $allowExtraFields = null, ?bool $allowMissingFields = null, ?string $extraFieldsMessage = null, ?string $missingFieldsMessage = null) { + $options = $fields; + if (self::isFieldsOption($fields)) { + $options = []; + + if (null !== $fields) { + $options['fields'] = $fields; + } $fields = ['fields' => $fields]; } else { trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } - parent::__construct($fields, $groups, $payload); + parent::__construct($options, $groups, $payload); $this->allowExtraFields = $allowExtraFields ?? $this->allowExtraFields; $this->allowMissingFields = $allowMissingFields ?? $this->allowMissingFields; @@ -88,6 +95,10 @@ protected function getCompositeOption(): string private static function isFieldsOption($options): bool { + if (null === $options) { + return true; + } + if (!\is_array($options)) { return false; } From 8d6884cc5fd27e287aa90b59ba66d438f75ec3fa Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 16 Jul 2025 10:27:38 +0200 Subject: [PATCH 146/149] fix CS --- Constraints/Collection.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Constraints/Collection.php b/Constraints/Collection.php index 3de2c14ab..b59caa89d 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -53,7 +53,6 @@ public function __construct(mixed $fields = null, ?array $groups = null, mixed $ if (null !== $fields) { $options['fields'] = $fields; } - $fields = ['fields' => $fields]; } else { trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); } From 7dd22de924d7f7cb0ba6975afa9414b94140c1b2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 3 Sep 2025 16:08:24 +0200 Subject: [PATCH 147/149] fall back to legacy options handling if configured named arguments do not match --- Mapping/Loader/AbstractLoader.php | 10 +++++++++- Tests/Mapping/Loader/XmlFileLoaderTest.php | 20 +++++++++++++++++++ .../constraint-mapping-exactly-value.xml | 17 ++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Tests/Mapping/Loader/constraint-mapping-exactly-value.xml diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index 67b7b2010..ebff64fd5 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -101,7 +101,15 @@ protected function newConstraint(string $name, mixed $options = null): Constrain return new $className($options); } - return new $className(...$options); + try { + return new $className(...$options); + } catch (\Error $e) { + if (str_starts_with($e->getMessage(), 'Unknown named parameter ')) { + return new $className($options); + } + + throw $e; + } } if ($options) { diff --git a/Tests/Mapping/Loader/XmlFileLoaderTest.php b/Tests/Mapping/Loader/XmlFileLoaderTest.php index 08a4bb862..03757720c 100644 --- a/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Validator\Constraints\Choice; use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\IsTrue; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Constraints\Regex; @@ -188,4 +189,23 @@ public function testLoadConstraintWithoutNamedArgumentsSupport() $loader->loadClassMetadata($metadata); } + + /** + * @group legacy + */ + public function testLengthConstraintValueOptionTriggersDeprecation() + { + $loader = new XmlFileLoader(__DIR__.'/constraint-mapping-exactly-value.xml'); + $metadata = new ClassMetadata(Entity_81::class); + + $this->expectUserDeprecationMessage(\sprintf('Since symfony/validator 7.3: Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', Length::class)); + + $loader->loadClassMetadata($metadata); + $constraints = $metadata->getPropertyMetadata('title')[0]->constraints; + + self::assertCount(1, $constraints); + self::assertInstanceOf(Length::class, $constraints[0]); + self::assertSame(6, $constraints[0]->min); + self::assertSame(6, $constraints[0]->max); + } } diff --git a/Tests/Mapping/Loader/constraint-mapping-exactly-value.xml b/Tests/Mapping/Loader/constraint-mapping-exactly-value.xml new file mode 100644 index 000000000..40e04f3a3 --- /dev/null +++ b/Tests/Mapping/Loader/constraint-mapping-exactly-value.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + From 986931185f0a8e1a28131bf80f02cfc507c623fd Mon Sep 17 00:00:00 2001 From: Alexander Kim Date: Fri, 19 Sep 2025 12:13:33 -0400 Subject: [PATCH 148/149] [Validator] Expression constraint docblock incorrectly states default value for negate --- Constraints/Expression.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constraints/Expression.php b/Constraints/Expression.php index 750c6f8fc..f40577d7b 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -44,7 +44,7 @@ class Expression extends Constraint * @param array|null $values The values of the custom variables used in the expression (defaults to an empty array) * @param string[]|null $groups * @param array|null $options - * @param bool|null $negate Whether to fail if the expression evaluates to true (defaults to false) + * @param bool|null $negate When set to true, if the expression returns true, the validation will pass (defaults to true) */ #[HasNamedArguments] public function __construct( From 5a2904dc351c7bd1fb402dfc4faccccd6f55ce01 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 29 Oct 2025 01:43:49 +0100 Subject: [PATCH 149/149] [Validator] Fix call to undefined getParser() in YamlValidator --- Constraints/YamlValidator.php | 8 ++-- Tests/Constraints/YamlValidatorTest.php | 51 +++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Constraints/YamlValidator.php b/Constraints/YamlValidator.php index 2675ed9aa..165e3fad8 100644 --- a/Constraints/YamlValidator.php +++ b/Constraints/YamlValidator.php @@ -39,17 +39,19 @@ public function validate(mixed $value, Constraint $constraint): void $value = (string) $value; + $parser = new Parser(); + /** @see \Symfony\Component\Yaml\Command\LintCommand::validate() */ - $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { + $prevErrorHandler = set_error_handler(static function ($level, $message, $file, $line) use (&$prevErrorHandler, $parser) { if (\E_USER_DEPRECATED === $level) { - throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1); + throw new ParseException($message, $parser->getRealCurrentLineNb() + 1); } return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; }); try { - (new Parser())->parse($value, $constraint->flags); + $parser->parse($value, $constraint->flags); } catch (ParseException $e) { $this->context->buildViolation($constraint->message) ->setParameter('{{ error }}', $e->getMessage()) diff --git a/Tests/Constraints/YamlValidatorTest.php b/Tests/Constraints/YamlValidatorTest.php index a31892667..5a90ccf03 100644 --- a/Tests/Constraints/YamlValidatorTest.php +++ b/Tests/Constraints/YamlValidatorTest.php @@ -71,6 +71,27 @@ public function testInvalidFlags() ->assertRaised(); } + /** + * @dataProvider getDeprecationOnLinesData + */ + public function testDeprecationTriggersParseException(int $yamlLine, string $yamlValue) + { + $lines = explode("\n", $yamlValue); + $errorLine = end($lines); + $expectedError = 'This is a simulated deprecation at line '.$yamlLine.' (near "'.$errorLine.'")'; + + $constraint = new Yaml( + message: 'myMessageTest', + flags: YamlParser::PARSE_OBJECT, + ); + $this->validator->validate($yamlValue, $constraint); + $this->buildViolation('myMessageTest') + ->setParameter('{{ error }}', $expectedError) + ->setParameter('{{ line }}', $yamlLine) + ->setCode(Yaml::INVALID_YAML_ERROR) + ->assertRaised(); + } + public static function getValidValues() { return [ @@ -94,4 +115,34 @@ public static function getInvalidValues(): array ["key:\nvalue", 'Unable to parse at line 2 (near "value").', 2], ]; } + + /** + * @return array + */ + public static function getDeprecationOnLinesData(): array + { + $serialized = serialize(new DeprecatedObjectFixture()); + + return [ + 'deprecation at line 1' => [1, "object: !php/object '".$serialized."'"], + 'deprecation at line 2' => [2, "valid: yaml\nobject: !php/object '".$serialized."'"], + 'deprecation at line 5' => [5, "line1: value\nline2: value\nline3: value\nline4: value\nobject: !php/object '".$serialized."'"], + ]; + } +} + +/** + * Fixture class for triggering deprecation during unserialize. + */ +class DeprecatedObjectFixture +{ + public function __serialize(): array + { + return []; + } + + public function __unserialize(array $data): void + { + @trigger_error('This is a simulated deprecation', \E_USER_DEPRECATED); + } }