diff --git a/Attribute/ExtendsValidationFor.php b/Attribute/ExtendsValidationFor.php new file mode 100644 index 000000000..1d7b50cbb --- /dev/null +++ b/Attribute/ExtendsValidationFor.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Attribute; + +/** + * Declares that constraints listed on the current class should be added to the given class. + * + * Classes that use this attribute should contain only properties and methods that + * exist on the target class (not necessarily all of them). + * + * @author Nicolas Grekas + */ +#[\Attribute(\Attribute::TARGET_CLASS)] +final class ExtendsValidationFor +{ + /** + * @param class-string $class + */ + public function __construct( + public string $class, + ) { + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index e8146d2a5..b0d349a68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,194 @@ CHANGELOG ========= +7.4 +--- + + * Deprecate handling associative arrays in `GroupSequence` + + Before: + + ```php + $groupSequence = GroupSequence(['value' => ['group 1', 'group 2']]); + ``` + + After: + + ```php + $groupSequence = GroupSequence(['group 1', 'group 2']); + ``` + * Deprecate configuring constraint options implicitly with the XML format + + Before: + + ```xml + + + Symfony\Component\Validator\Tests\Fixtures\CallbackClass + callback + + + ``` + + After: + + ```xml + + + + + + ``` + * Deprecate configuring constraint options implicitly with the YAML format + + Before: + + ```yaml + Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity: + constraints: + - Callback: validateMeStatic + - Callback: [Symfony\Component\Validator\Tests\Fixtures\CallbackClass, callback] + ``` + + After: + + ```yaml + Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity: + constraints: + - Callback: + callback: validateMeStatic + - Callback: + callback: [Symfony\Component\Validator\Tests\Fixtures\CallbackClass, callback] + ``` + * Add `#[ExtendsValidationFor]` to declare new constraints for a class + * Add `ValidatorBuilder::addAttributeMappings()` and `AttributeMetadataPass` to declare compile-time constraint metadata using attributes + * Add the `Video` constraint for validating video files + * Deprecate implementing `__sleep/wakeup()` on `GenericMetadata` implementations; use `__(un)serialize()` instead + * Deprecate passing a list of choices to the first argument of the `Choice` constraint. Use the `choices` option instead + * Add the `min` and `max` parameter to the `Length` constraint violation + * Deprecate `getRequiredOptions()` and `getDefaultOption()` methods of the `All`, `AtLeastOneOf`, `CardScheme`, `Collection`, + `CssColor`, `Expression`, `Regex`, `Sequentially`, `Type`, and `When` constraints + * Deprecate evaluating options in the base `Constraint` class. Initialize properties in the constructor of the concrete constraint + class instead. + + Before: + + ```php + class CustomConstraint extends Constraint + { + public $option1; + public $option2; + + public function __construct(?array $options = null) + { + parent::__construct($options); + } + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Attribute\HasNamedArguments; + + class CustomConstraint extends Constraint + { + #[HasNamedArguments] + public function __construct( + public $option1 = null, + public $option2 = null, + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct(null, $groups, $payload); + } + } + ``` + * Deprecate the `getRequiredOptions()` method of the base `Constraint` class. Use mandatory constructor arguments instead. + + Before: + + ```php + class CustomConstraint extends Constraint + { + public $option1; + public $option2; + + public function __construct(?array $options = null) + { + parent::__construct($options); + } + + public function getRequiredOptions() + { + return ['option1']; + } + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Attribute\HasNamedArguments; + + class CustomConstraint extends Constraint + { + #[HasNamedArguments] + public function __construct( + public $option1, + public $option2 = null, + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct(null, $groups, $payload); + } + } + ``` + * Deprecate the `normalizeOptions()` and `getDefaultOption()` methods of the base `Constraint` class without replacements. + Overriding them in child constraint will not have any effects starting with Symfony 8.0. + * Deprecate passing an array of options to the `Composite` constraint class. Initialize the properties referenced with `getNestedConstraints()` + in child classes before calling the constructor of `Composite`. + + Before: + + ```php + class CustomCompositeConstraint extends Composite + { + public array $constraints = []; + + public function __construct(?array $options = null) + { + parent::__construct($options); + } + + protected function getCompositeOption(): string + { + return 'constraints'; + } + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Attribute\HasNamedArguments; + + class CustomCompositeConstraint extends Composite + { + #[HasNamedArguments] + public function __construct( + public array $constraints, + ?array $groups = null, + mixed $payload = null) + { + parent::__construct(null, $groups, $payload); + } + } + ``` + 7.3 --- diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index 70b508d09..e6a076c97 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -50,10 +50,10 @@ protected function configure(): void ->addArgument('class', InputArgument::REQUIRED, 'A fully qualified class name or a path') ->addOption('show-all', null, InputOption::VALUE_NONE, 'Show all classes even if they have no validation constraints') ->setHelp(<<<'EOF' -The %command.name% 'App\Entity\Dummy' command dumps the validators for the dummy class. + The %command.name% 'App\Entity\Dummy' command dumps the validators for the dummy class. -The %command.name% src/ command dumps the validators for the `src` directory. -EOF + The %command.name% src/ command dumps the validators for the `src` directory. + EOF ) ; } diff --git a/Constraint.php b/Constraint.php index 5fd8ce84c..47dd9b42d 100644 --- a/Constraint.php +++ b/Constraint.php @@ -110,6 +110,17 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed { unset($this->groups); // enable lazy initialization + if (null === $options && (\func_num_args() > 0 || self::class === (new \ReflectionMethod($this, 'getRequiredOptions'))->getDeclaringClass()->getName())) { + if (null !== $groups) { + $this->groups = $groups; + } + $this->payload = $payload; + + return; + } + + trigger_deprecation('symfony/validator', '7.4', 'Support for evaluating options in the base Constraint class is deprecated. Initialize properties in the constructor of %s instead.', static::class); + $options = $this->normalizeOptions($options); if (null !== $groups) { $options['groups'] = $groups; @@ -122,14 +133,16 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed } /** + * @deprecated since Symfony 7.4 + * * @return array */ protected function normalizeOptions(mixed $options): array { $normalizedOptions = []; - $defaultOption = $this->getDefaultOption(); + $defaultOption = $this->getDefaultOption(false); $invalidOptions = []; - $missingOptions = array_flip($this->getRequiredOptions()); + $missingOptions = array_flip($this->getRequiredOptions(false)); $knownOptions = get_class_vars(static::class); if (\is_array($options) && isset($options['value']) && !property_exists($this, 'value')) { @@ -241,10 +254,15 @@ public function addImplicitGroupName(string $group): void * * Override this method to define a default option. * + * @deprecated since Symfony 7.4 * @see __construct() */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return null; } @@ -255,10 +273,15 @@ public function getDefaultOption(): ?string * * @return string[] * + * @deprecated since Symfony 7.4 * @see __construct() */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return []; } @@ -287,14 +310,23 @@ public function getTargets(): string|array /** * Optimizes the serialized value to minimize storage space. - * - * @internal */ - public function __sleep(): array + public function __serialize(): array { // Initialize "groups" option if it is not set $this->groups; - return array_keys(get_object_vars($this)); + $data = []; + $class = $this::class; + foreach ((array) $this as $k => $v) { + $data[match (true) { + '' === $k || "\0" !== $k[0] => $k, + str_starts_with($k, "\0*\0") => substr($k, 3), + str_starts_with($k, "\0{$class}\0") => substr($k, 2 + \strlen($class)), + default => $k, + }] = $v; + } + + return $data; } } diff --git a/Constraints/AbstractComparison.php b/Constraints/AbstractComparison.php index 3830da789..786d86803 100644 --- a/Constraints/AbstractComparison.php +++ b/Constraints/AbstractComparison.php @@ -36,19 +36,19 @@ public function __construct(mixed $value = null, ?string $propertyPath = null, ? 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 ?? []); + $value = null; } elseif (null !== $value) { 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); - } else { - $options = []; - } - $options['value'] = $value; + $options['value'] = $value; + } } parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; + $this->value = $value ?? $this->value; $this->propertyPath = $propertyPath ?? $this->propertyPath; if (null === $this->value && null === $this->propertyPath) { @@ -64,8 +64,15 @@ public function __construct(mixed $value = null, ?string $propertyPath = null, ? } } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'value'; } } diff --git a/Constraints/All.php b/Constraints/All.php index 92ded329b..533599ad0 100644 --- a/Constraints/All.php +++ b/Constraints/All.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * When applied to an array (or Traversable object), this constraint allows you to apply @@ -26,26 +27,48 @@ class All extends Composite public array|Constraint $constraints = []; /** - * @param array|array|Constraint|null $constraints - * @param string[]|null $groups + * @param array|Constraint|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.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + if (null === $constraints || [] === $constraints) { + throw new MissingOptionsException(\sprintf('The options "constraints" must be set for constraint "%s".', self::class), ['constraints']); } - parent::__construct($constraints ?? [], $groups, $payload); + if (!$constraints instanceof Constraint && !\is_array($constraints) || \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); + + parent::__construct($constraints, $groups, $payload); + } else { + $this->constraints = $constraints; + + parent::__construct(null, $groups, $payload); + } } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'constraints'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['constraints']; } diff --git a/Constraints/AtLeastOneOf.php b/Constraints/AtLeastOneOf.php index 20d55f458..bc99b3385 100644 --- a/Constraints/AtLeastOneOf.php +++ b/Constraints/AtLeastOneOf.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Checks that at least one of the given constraint is satisfied. @@ -34,33 +35,54 @@ class AtLeastOneOf extends Composite 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) + * @param 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) */ #[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)) { + if (null === $constraints || [] === $constraints) { + throw new MissingOptionsException(\sprintf('The options "constraints" must be set for constraint "%s".', self::class), ['constraints']); + } + + if (!$constraints instanceof Constraint && !\is_array($constraints) || \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); + $options = $constraints; + } else { + $this->constraints = $constraints; } - parent::__construct($constraints ?? [], $groups, $payload); + parent::__construct($options ?? null, $groups, $payload); $this->message = $message ?? $this->message; $this->messageCollection = $messageCollection ?? $this->messageCollection; $this->includeInternalMessages = $includeInternalMessages ?? $this->includeInternalMessages; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'constraints'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['constraints']; } diff --git a/Constraints/Bic.php b/Constraints/Bic.php index 5390d5556..ef1ae1660 100644 --- a/Constraints/Bic.php +++ b/Constraints/Bic.php @@ -65,7 +65,6 @@ class Bic extends Constraint 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 diff --git a/Constraints/BicValidator.php b/Constraints/BicValidator.php index 55b6bbbc0..0c3e42b98 100644 --- a/Constraints/BicValidator.php +++ b/Constraints/BicValidator.php @@ -78,7 +78,7 @@ public function validate(mixed $value, Constraint $constraint): void $canonicalize = str_replace(' ', '', $value); // the bic must be either 8 or 11 characters long - if (!\in_array(\strlen($canonicalize), [8, 11])) { + if (!\in_array(\strlen($canonicalize), [8, 11], true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Bic::INVALID_LENGTH_ERROR) diff --git a/Constraints/Blank.php b/Constraints/Blank.php index 72fbae57a..b03586933 100644 --- a/Constraints/Blank.php +++ b/Constraints/Blank.php @@ -31,8 +31,7 @@ class Blank extends Constraint public string $message = 'This value should be blank.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) @@ -41,7 +40,7 @@ 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); } - parent::__construct($options ?? [], $groups, $payload); + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; } diff --git a/Constraints/Callback.php b/Constraints/Callback.php index 44b51ac2a..326125a45 100644 --- a/Constraints/Callback.php +++ b/Constraints/Callback.php @@ -28,8 +28,8 @@ class Callback extends Constraint public $callback; /** - * @param string|string[]|callable|array|null $callback The callback definition - * @param string[]|null $groups + * @param string|string[]|callable|null $callback The callback definition + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(array|string|callable|null $callback = null, ?array $groups = null, mixed $payload = null, ?array $options = null) @@ -44,22 +44,28 @@ public function __construct(array|string|callable|null $callback = null, ?array if (!\is_array($callback) || (!isset($callback['callback']) && !isset($callback['groups']) && !isset($callback['payload']))) { 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); - } else { - $options = []; } - - $options['callback'] = $callback; } 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); $options = array_merge($callback, $options ?? []); + $callback = null; } parent::__construct($options, $groups, $payload); + + $this->callback = $callback ?? $this->callback; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'callback'; } diff --git a/Constraints/CardScheme.php b/Constraints/CardScheme.php index 81de342f5..706969796 100644 --- a/Constraints/CardScheme.php +++ b/Constraints/CardScheme.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Validates a credit card number for a given credit card company. @@ -48,41 +49,54 @@ class CardScheme extends Constraint public array|string|null $schemes = null; /** - * @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|null $options + * @param non-empty-string|non-empty-string[]|null $schemes Name(s) of the number scheme(s) used to validate the credit card number + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(array|string|null $schemes, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { + if (null === $schemes && !isset($options['schemes'])) { + throw new MissingOptionsException(\sprintf('The options "schemes" must be set for constraint "%s".', self::class), ['schemes']); + } + if (\is_array($schemes) && \is_string(key($schemes))) { 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 ?? []); + $schemes = null; } else { 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); - } else { - $options = []; - } - - if (null !== $schemes) { - $options['value'] = $schemes; } } parent::__construct($options, $groups, $payload); + $this->schemes = $schemes ?? $this->schemes; $this->message = $message ?? $this->message; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'schemes'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['schemes']; } } diff --git a/Constraints/Cascade.php b/Constraints/Cascade.php index 7d90cfcf7..86419d7e6 100644 --- a/Constraints/Cascade.php +++ b/Constraints/Cascade.php @@ -26,8 +26,7 @@ class Cascade extends Constraint public array $exclude = []; /** - * @param non-empty-string[]|non-empty-string|array|null $exclude Properties excluded from validation - * @param array|null $options + * @param non-empty-string[]|non-empty-string|null $exclude Properties excluded from validation */ #[HasNamedArguments] public function __construct(array|string|null $exclude = null, ?array $options = null) @@ -37,19 +36,23 @@ public function __construct(array|string|null $exclude = null, ?array $options = $options = array_merge($exclude, $options ?? []); $options['exclude'] = array_flip((array) ($options['exclude'] ?? [])); + $exclude = $options['exclude'] ?? null; } else { 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->exclude = array_flip((array) $exclude); + $exclude = array_flip((array) $exclude); + $this->exclude = $exclude; } 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, $options['payload'] ?? null); + + $this->exclude = $exclude ?? $this->exclude; } public function getTargets(): string|array diff --git a/Constraints/Choice.php b/Constraints/Choice.php index 1435a762b..cf353907d 100644 --- a/Constraints/Choice.php +++ b/Constraints/Choice.php @@ -45,8 +45,15 @@ class Choice extends Constraint public string $maxMessage = 'You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.'; public bool $match = true; + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'choices'; } @@ -62,7 +69,7 @@ public function getDefaultOption(): ?string */ #[HasNamedArguments] public function __construct( - string|array $options = [], + string|array|null $options = null, ?array $choices = null, callable|string|null $callback = null, ?bool $multiple = null, @@ -78,18 +85,16 @@ public function __construct( ?bool $match = null, ) { if (\is_array($options) && $options && array_is_list($options)) { + trigger_deprecation('symfony/validator', '7.4', 'Support for passing the choices as the first argument to %s is deprecated.', static::class); $choices ??= $options; - $options = []; + $options = null; } elseif (\is_array($options) && [] !== $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); } - if (null !== $choices) { - $options['value'] = $choices; - } - parent::__construct($options, $groups, $payload); + $this->choices = $choices ?? $this->choices; $this->callback = $callback ?? $this->callback; $this->multiple = $multiple ?? $this->multiple; $this->strict = $strict ?? $this->strict; diff --git a/Constraints/Collection.php b/Constraints/Collection.php index b59caa89d..3954a6c38 100644 --- a/Constraints/Collection.php +++ b/Constraints/Collection.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Validates a collection with constraints defined for specific keys. @@ -37,27 +38,27 @@ class Collection extends Composite public string $missingFieldsMessage = 'This field is missing.'; /** - * @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) + * @param 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) */ #[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 (null === $fields) { + throw new MissingOptionsException(\sprintf('The options "fields" must be set for constraint "%s".', self::class), ['fields']); + } if (self::isFieldsOption($fields)) { - $options = []; - - if (null !== $fields) { - $options['fields'] = $fields; - } + $this->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); + + $options = $fields; } - parent::__construct($options, $groups, $payload); + parent::__construct($options ?? null, $groups, $payload); $this->allowExtraFields = $allowExtraFields ?? $this->allowExtraFields; $this->allowMissingFields = $allowMissingFields ?? $this->allowMissingFields; @@ -82,8 +83,15 @@ protected function initializeNestedConstraints(): void } } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['fields']; } @@ -94,10 +102,6 @@ protected function getCompositeOption(): string private static function isFieldsOption($options): bool { - if (null === $options) { - return true; - } - if (!\is_array($options)) { return false; } diff --git a/Constraints/Composite.php b/Constraints/Composite.php index 1710d9a49..fdfaacc2e 100644 --- a/Constraints/Composite.php +++ b/Constraints/Composite.php @@ -53,6 +53,10 @@ abstract class Composite extends Constraint #[HasNamedArguments] public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null) { + if (null !== $options) { + trigger_deprecation('symfony/validator', '7.4', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); + } + parent::__construct($options, $groups, $payload); $this->initializeNestedConstraints(); diff --git a/Constraints/Count.php b/Constraints/Count.php index 108872904..c947c9225 100644 --- a/Constraints/Count.php +++ b/Constraints/Count.php @@ -44,12 +44,12 @@ class Count extends Constraint public ?int $divisibleBy = null; /** - * @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 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|null $options + * @param int<0, max>|null $exactly The exact expected number of elements + * @param int<0, max>|null $min Minimum 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|null $options */ #[HasNamedArguments] public function __construct( @@ -72,8 +72,6 @@ public function __construct( $exactly = $options['value'] ?? null; } elseif (\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); - } else { - $options = []; } $min ??= $options['min'] ?? null; diff --git a/Constraints/Country.php b/Constraints/Country.php index 135f996dd..89d4717b4 100644 --- a/Constraints/Country.php +++ b/Constraints/Country.php @@ -36,9 +36,8 @@ class Country extends Constraint 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 + * @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 */ diff --git a/Constraints/CssColor.php b/Constraints/CssColor.php index 793a4a576..f73d2941b 100644 --- a/Constraints/CssColor.php +++ b/Constraints/CssColor.php @@ -63,9 +63,8 @@ class CssColor extends Constraint public array|string $formats; /** - * @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 + * @param non-empty-string[]|non-empty-string $formats The types of CSS colors allowed ({@see https://symfony.com/doc/current/reference/constraints/CssColor.html#formats}) + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(array|string $formats = [], ?string $message = null, ?array $groups = null, $payload = null, ?array $options = null) @@ -73,39 +72,53 @@ public function __construct(array|string $formats = [], ?string $message = null, $validationModesAsString = implode(', ', self::$validationModes); if (!$formats) { - $options['value'] = self::$validationModes; + $formats = self::$validationModes; } elseif (\is_array($formats) && \is_string(key($formats))) { 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 ?? []); + $formats = null; } 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)); } - - $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)); } - $options['value'] = [$formats]; + $formats = [$formats]; } else { throw new InvalidArgumentException('The "formats" parameter type is not valid. It should be a string or an array.'); } parent::__construct($options, $groups, $payload); + $this->formats = $formats ?? $this->formats; $this->message = $message ?? $this->message; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'formats'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['formats']; } } diff --git a/Constraints/Currency.php b/Constraints/Currency.php index c8f6417b3..678538a8a 100644 --- a/Constraints/Currency.php +++ b/Constraints/Currency.php @@ -36,8 +36,7 @@ class Currency extends Constraint public string $message = 'This value is not a valid currency.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] 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 adb48474f..f2ae75691 100644 --- a/Constraints/Date.php +++ b/Constraints/Date.php @@ -35,8 +35,7 @@ class Date extends Constraint public string $message = 'This value is not a valid date.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] 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 6b287be75..63c8bda90 100644 --- a/Constraints/DateTime.php +++ b/Constraints/DateTime.php @@ -38,9 +38,8 @@ class DateTime extends Constraint public string $message = 'This value is not a valid datetime.'; /** - * @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|null $options + * @param non-empty-string|null $format The datetime format to match (defaults to 'Y-m-d H:i:s') + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(string|array|null $format = null, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) @@ -49,23 +48,30 @@ public function __construct(string|array|null $format = null, ?string $message = 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 ?? []); + $format = null; } elseif (null !== $format) { 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); - } else { - $options = []; - } - $options['value'] = $format; + $options['value'] = $format; + } } parent::__construct($options, $groups, $payload); + $this->format = $format ?? $this->format; $this->message = $message ?? $this->message; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'format'; } } diff --git a/Constraints/DisableAutoMapping.php b/Constraints/DisableAutoMapping.php index 7cbea8b38..926d8be25 100644 --- a/Constraints/DisableAutoMapping.php +++ b/Constraints/DisableAutoMapping.php @@ -26,9 +26,6 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)] class DisableAutoMapping extends Constraint { - /** - * @param array|null $options - */ #[HasNamedArguments] public function __construct(?array $options = null, mixed $payload = null) { diff --git a/Constraints/Email.php b/Constraints/Email.php index 4a66986b2..193384019 100644 --- a/Constraints/Email.php +++ b/Constraints/Email.php @@ -47,8 +47,7 @@ class Email extends Constraint 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 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] diff --git a/Constraints/EnableAutoMapping.php b/Constraints/EnableAutoMapping.php index 873430677..58e77805a 100644 --- a/Constraints/EnableAutoMapping.php +++ b/Constraints/EnableAutoMapping.php @@ -26,9 +26,6 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)] class EnableAutoMapping extends Constraint { - /** - * @param array|null $options - */ #[HasNamedArguments] public function __construct(?array $options = null, mixed $payload = null) { diff --git a/Constraints/Existence.php b/Constraints/Existence.php index 72bc1da61..a867f09e5 100644 --- a/Constraints/Existence.php +++ b/Constraints/Existence.php @@ -20,8 +20,26 @@ abstract class Existence extends Composite { public array|Constraint $constraints = []; + public function __construct(mixed $constraints = null, ?array $groups = null, mixed $payload = null) + { + if (!$constraints instanceof Constraint && !\is_array($constraints) || \is_array($constraints) && !array_is_list($constraints)) { + parent::__construct($constraints, $groups, $payload); + } else { + $this->constraints = $constraints; + + parent::__construct(null, $groups, $payload); + } + } + + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'constraints'; } diff --git a/Constraints/Expression.php b/Constraints/Expression.php index f40577d7b..1c990cf6e 100644 --- a/Constraints/Expression.php +++ b/Constraints/Expression.php @@ -16,6 +16,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Validates a value using an expression from the Expression Language component. @@ -43,8 +44,7 @@ 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|null $options - * @param bool|null $negate When set to true, if the expression returns true, the validation will pass (defaults to true) + * @param bool|null $negate Whether to fail if the expression evaluates to true (defaults to false) */ #[HasNamedArguments] public function __construct( @@ -60,36 +60,50 @@ public function __construct( 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 (null === $expression && !isset($options['expression'])) { + throw new MissingOptionsException(\sprintf('The options "expression" must be set for constraint "%s".', self::class), ['expression']); + } + if (\is_array($expression)) { 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 ?? []); + $expression = null; } else { 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); - } else { - $options = []; - } - - if (null !== $expression) { - $options['value'] = $expression; } } parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; + $this->expression = $expression ?? $this->expression; $this->values = $values ?? $this->values; $this->negate = $negate ?? $this->negate; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'expression'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['expression']; } diff --git a/Constraints/ExpressionSyntax.php b/Constraints/ExpressionSyntax.php index 5a0a09de1..55ed3c542 100644 --- a/Constraints/ExpressionSyntax.php +++ b/Constraints/ExpressionSyntax.php @@ -33,10 +33,9 @@ class ExpressionSyntax extends Constraint public ?array $allowedVariables = null; /** - * @param array|null $options - * @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 + * @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 */ #[HasNamedArguments] 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 7d93a2084..169e94154 100644 --- a/Constraints/File.php +++ b/Constraints/File.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -21,7 +22,7 @@ * * 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). + * - A valid {@see \Symfony\Component\HttpFoundation\File\File} object (including objects of {@see UploadedFile} class). * * @property int $maxSize * @@ -91,7 +92,6 @@ class File extends Constraint protected int|string|null $maxSize = null; /** - * @param array|null $options * @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. @@ -106,7 +106,7 @@ class File extends Constraint * @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}) + * @param self::FILENAME_COUNT_*|null $filenameCountUnit The character count unit used for checking the filename length (defaults to {@see self::FILENAME_COUNT_BYTES}) * * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types */ diff --git a/Constraints/GroupSequence.php b/Constraints/GroupSequence.php index e3e4f47f9..440c29003 100644 --- a/Constraints/GroupSequence.php +++ b/Constraints/GroupSequence.php @@ -80,6 +80,10 @@ class GroupSequence #[HasNamedArguments] public function __construct(array $groups) { + if (!array_is_list($groups)) { + trigger_deprecation('symfony/validator', '7.4', 'Support for passing an array of options to "%s()" is deprecated.', __METHOD__); + } + $this->groups = $groups['value'] ?? $groups; } } diff --git a/Constraints/Hostname.php b/Constraints/Hostname.php index ca9bc3a32..f388c950c 100644 --- a/Constraints/Hostname.php +++ b/Constraints/Hostname.php @@ -32,9 +32,8 @@ class Hostname extends Constraint 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 + * @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( diff --git a/Constraints/Iban.php b/Constraints/Iban.php index 459fb5fb0..4898155c1 100644 --- a/Constraints/Iban.php +++ b/Constraints/Iban.php @@ -43,8 +43,7 @@ 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 + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) diff --git a/Constraints/Image.php b/Constraints/Image.php index d9b7c8822..b47dc8ba6 100644 --- a/Constraints/Image.php +++ b/Constraints/Image.php @@ -91,7 +91,6 @@ class Image extends File public string $corruptedMessage = 'The image file is corrupted.'; /** - * @param array|null $options * @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 non-empty-string[]|null $mimeTypes Acceptable media types diff --git a/Constraints/Ip.php b/Constraints/Ip.php index 4db552a76..91f247156 100644 --- a/Constraints/Ip.php +++ b/Constraints/Ip.php @@ -108,7 +108,6 @@ class Ip extends Constraint 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 */ diff --git a/Constraints/IsFalse.php b/Constraints/IsFalse.php index bcdadeaf9..722f2a247 100644 --- a/Constraints/IsFalse.php +++ b/Constraints/IsFalse.php @@ -31,8 +31,7 @@ class IsFalse extends Constraint public string $message = 'This value should be false.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) @@ -41,7 +40,7 @@ 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); } - parent::__construct($options ?? [], $groups, $payload); + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; } diff --git a/Constraints/IsNull.php b/Constraints/IsNull.php index fa04703ea..7447aed9f 100644 --- a/Constraints/IsNull.php +++ b/Constraints/IsNull.php @@ -31,8 +31,7 @@ class IsNull extends Constraint public string $message = 'This value should be null.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) @@ -41,7 +40,7 @@ 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); } - parent::__construct($options ?? [], $groups, $payload); + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; } diff --git a/Constraints/IsTrue.php b/Constraints/IsTrue.php index 3c0345e77..58d25b594 100644 --- a/Constraints/IsTrue.php +++ b/Constraints/IsTrue.php @@ -31,8 +31,7 @@ class IsTrue extends Constraint public string $message = 'This value should be true.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) @@ -41,7 +40,7 @@ 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); } - parent::__construct($options ?? [], $groups, $payload); + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; } diff --git a/Constraints/Isbn.php b/Constraints/Isbn.php index 45ca4e4b8..5251150f3 100644 --- a/Constraints/Isbn.php +++ b/Constraints/Isbn.php @@ -50,10 +50,9 @@ class Isbn extends Constraint 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|null $options + * @param self::ISBN_*|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 */ #[HasNamedArguments] public function __construct( @@ -70,14 +69,9 @@ public function __construct( 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.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); - } else { - $options = []; - } - - $options['value'] = $type; + $type = $options['type'] ?? null; + } elseif (\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); @@ -86,10 +80,18 @@ public function __construct( $this->isbn10Message = $isbn10Message ?? $this->isbn10Message; $this->isbn13Message = $isbn13Message ?? $this->isbn13Message; $this->bothIsbnMessage = $bothIsbnMessage ?? $this->bothIsbnMessage; + $this->type = $type ?? $this->type; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'type'; } } diff --git a/Constraints/Isin.php b/Constraints/Isin.php index 7bd9abe2d..821ec62d5 100644 --- a/Constraints/Isin.php +++ b/Constraints/Isin.php @@ -40,8 +40,7 @@ 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 + * @param string[]|null $groups */ #[HasNamedArguments] 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 048c18f5e..1c4ba88d0 100644 --- a/Constraints/Issn.php +++ b/Constraints/Issn.php @@ -46,10 +46,9 @@ class Issn extends Constraint 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 + * @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 */ #[HasNamedArguments] public function __construct( diff --git a/Constraints/Json.php b/Constraints/Json.php index 18078a2fe..8798c94aa 100644 --- a/Constraints/Json.php +++ b/Constraints/Json.php @@ -31,8 +31,7 @@ class Json extends Constraint public string $message = 'This value should be valid JSON.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] 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 61ac4644b..dfa91b4f7 100644 --- a/Constraints/Language.php +++ b/Constraints/Language.php @@ -36,9 +36,8 @@ class Language extends Constraint 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 + * @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( diff --git a/Constraints/Length.php b/Constraints/Length.php index ce1460c6e..3c7f93ae6 100644 --- a/Constraints/Length.php +++ b/Constraints/Length.php @@ -59,14 +59,13 @@ class Length extends Constraint public string $countUnit = self::COUNT_CODEPOINTS; /** - * @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|null $options + * @param positive-int|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 */ #[HasNamedArguments] public function __construct( @@ -91,8 +90,6 @@ public function __construct( $exactly = $options['value'] ?? null; } elseif (\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); - } else { - $options = []; } $min ??= $options['min'] ?? null; diff --git a/Constraints/LengthValidator.php b/Constraints/LengthValidator.php index 985660bc2..b30f6722d 100644 --- a/Constraints/LengthValidator.php +++ b/Constraints/LengthValidator.php @@ -71,9 +71,15 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->max && $length > $constraint->max) { $exactlyOptionEnabled = $constraint->min == $constraint->max; - $this->context->buildViolation($exactlyOptionEnabled ? $constraint->exactMessage : $constraint->maxMessage) + $builder = $this->context->buildViolation($exactlyOptionEnabled ? $constraint->exactMessage : $constraint->maxMessage); + if (null !== $constraint->min) { + $builder->setParameter('{{ min }}', $constraint->min); + } + + $builder ->setParameter('{{ value }}', $this->formatValue($stringValue)) ->setParameter('{{ limit }}', $constraint->max) + ->setParameter('{{ max }}', $constraint->max) // To be consistent with the min error message ->setParameter('{{ value_length }}', $length) ->setInvalidValue($value) ->setPlural($constraint->max) @@ -86,9 +92,15 @@ public function validate(mixed $value, Constraint $constraint): void if (null !== $constraint->min && $length < $constraint->min) { $exactlyOptionEnabled = $constraint->min == $constraint->max; - $this->context->buildViolation($exactlyOptionEnabled ? $constraint->exactMessage : $constraint->minMessage) + $builder = $this->context->buildViolation($exactlyOptionEnabled ? $constraint->exactMessage : $constraint->minMessage); + if (null !== $constraint->max) { + $builder->setParameter('{{ max }}', $constraint->max); + } + + $builder ->setParameter('{{ value }}', $this->formatValue($stringValue)) ->setParameter('{{ limit }}', $constraint->min) + ->setParameter('{{ min }}', $constraint->min) // To be consistent with the max error message ->setParameter('{{ value_length }}', $length) ->setInvalidValue($value) ->setPlural($constraint->min) diff --git a/Constraints/Locale.php b/Constraints/Locale.php index 0ffe4b0e8..d309fecee 100644 --- a/Constraints/Locale.php +++ b/Constraints/Locale.php @@ -36,9 +36,8 @@ class Locale extends Constraint 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 + * @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( diff --git a/Constraints/Luhn.php b/Constraints/Luhn.php index 9421fc3c7..f2e93a867 100644 --- a/Constraints/Luhn.php +++ b/Constraints/Luhn.php @@ -37,8 +37,7 @@ class Luhn extends Constraint public string $message = 'Invalid card number.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct( diff --git a/Constraints/NoSuspiciousCharacters.php b/Constraints/NoSuspiciousCharacters.php index f0d28dba2..2a5ba530c 100644 --- a/Constraints/NoSuspiciousCharacters.php +++ b/Constraints/NoSuspiciousCharacters.php @@ -83,7 +83,6 @@ class NoSuspiciousCharacters extends Constraint public ?array $locales = null; /** - * @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) diff --git a/Constraints/NotBlank.php b/Constraints/NotBlank.php index 725e7eede..f26f6aff8 100644 --- a/Constraints/NotBlank.php +++ b/Constraints/NotBlank.php @@ -36,9 +36,8 @@ class NotBlank extends Constraint public $normalizer; /** - * @param array|null $options - * @param bool|null $allowNull Whether to allow null values (defaults to false) - * @param string[]|null $groups + * @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) @@ -47,7 +46,7 @@ public function __construct(?array $options = null, ?string $message = null, ?bo 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); + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; $this->allowNull = $allowNull ?? $this->allowNull; diff --git a/Constraints/NotCompromisedPassword.php b/Constraints/NotCompromisedPassword.php index ef1e03da9..8a6219580 100644 --- a/Constraints/NotCompromisedPassword.php +++ b/Constraints/NotCompromisedPassword.php @@ -33,10 +33,9 @@ class NotCompromisedPassword extends Constraint public bool $skipOnError = false; /** - * @param array|null $options - * @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 + * @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 */ #[HasNamedArguments] public function __construct( diff --git a/Constraints/NotNull.php b/Constraints/NotNull.php index 28596925e..b00c72bed 100644 --- a/Constraints/NotNull.php +++ b/Constraints/NotNull.php @@ -31,8 +31,7 @@ class NotNull extends Constraint public string $message = 'This value should not be null.'; /** - * @param array|null $options - * @param string[]|null $groups + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(?array $options = null, ?string $message = null, ?array $groups = null, mixed $payload = null) @@ -41,7 +40,7 @@ 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); } - parent::__construct($options ?? [], $groups, $payload); + parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; } diff --git a/Constraints/PasswordStrength.php b/Constraints/PasswordStrength.php index 3867cfbda..7ad2b13fd 100644 --- a/Constraints/PasswordStrength.php +++ b/Constraints/PasswordStrength.php @@ -40,18 +40,19 @@ 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 + * @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 (\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; + $options['minScore'] ??= self::STRENGTH_MEDIUM; + } else { + $minScore ??= self::STRENGTH_MEDIUM; + } parent::__construct($options, $groups, $payload); diff --git a/Constraints/Range.php b/Constraints/Range.php index aac582430..231058aec 100644 --- a/Constraints/Range.php +++ b/Constraints/Range.php @@ -49,7 +49,6 @@ 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|non-empty-string|null $min The minimum value, either numeric or a datetime string representation diff --git a/Constraints/Regex.php b/Constraints/Regex.php index 5c8501fa0..8ab6d0619 100644 --- a/Constraints/Regex.php +++ b/Constraints/Regex.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\InvalidArgumentException; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Validates that a value matches a regular expression. @@ -37,11 +38,10 @@ class Regex extends Constraint 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 true) - * @param string[]|null $groups - * @param array|null $options + * @param string|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 true) + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct( @@ -54,22 +54,22 @@ public function __construct( mixed $payload = null, ?array $options = null, ) { + if (null === $pattern && !isset($options['pattern'])) { + throw new MissingOptionsException(\sprintf('The options "pattern" must be set for constraint "%s".', self::class), ['pattern']); + } + if (\is_array($pattern)) { 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.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class); - } else { - $options = []; - } - - $options['value'] = $pattern; + $pattern = $options['pattern'] ?? null; + } elseif (\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); + $this->pattern = $pattern ?? $this->pattern; $this->message = $message ?? $this->message; $this->htmlPattern = $htmlPattern ?? $this->htmlPattern; $this->match = $match ?? $this->match; @@ -80,13 +80,27 @@ public function __construct( } } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'pattern'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['pattern']; } diff --git a/Constraints/Sequentially.php b/Constraints/Sequentially.php index 6389ebb89..ff53be7af 100644 --- a/Constraints/Sequentially.php +++ b/Constraints/Sequentially.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Use this constraint to sequentially validate nested constraints. @@ -26,26 +27,47 @@ class Sequentially extends Composite public array|Constraint $constraints = []; /** - * @param Constraint[]|array|null $constraints An array of validation constraints - * @param string[]|null $groups + * @param Constraint[]|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)) { + if (null === $constraints || [] === $constraints) { + throw new MissingOptionsException(\sprintf('The options "constraints" must be set for constraint "%s".', self::class), ['constraints']); + } + + if (!$constraints instanceof Constraint && !\is_array($constraints) || \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); + $options = $constraints; + } else { + $this->constraints = $constraints; } - parent::__construct($constraints ?? [], $groups, $payload); + parent::__construct($options ?? null, $groups, $payload); } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'constraints'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['constraints']; } diff --git a/Constraints/Time.php b/Constraints/Time.php index a99702cb2..e166ec0ff 100644 --- a/Constraints/Time.php +++ b/Constraints/Time.php @@ -34,9 +34,8 @@ class Time extends Constraint 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) + * @param string[]|null $groups + * @param bool|null $withSeconds Whether to allow seconds in the given value (defaults to true) */ #[HasNamedArguments] public function __construct( diff --git a/Constraints/Timezone.php b/Constraints/Timezone.php index 93b0692ef..0a05085a1 100644 --- a/Constraints/Timezone.php +++ b/Constraints/Timezone.php @@ -42,11 +42,10 @@ class Timezone extends Constraint ]; /** - * @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|null $options + * @param int|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 * * @see \DateTimeZone */ @@ -64,18 +63,18 @@ public function __construct( 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 ?? []); + $zone = null; } elseif (null !== $zone) { 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); - } else { - $options = []; - } - $options['value'] = $zone; + $options['value'] = $zone; + } } parent::__construct($options, $groups, $payload); + $this->zone = $zone ?? $this->zone; $this->message = $message ?? $this->message; $this->countryCode = $countryCode ?? $this->countryCode; $this->intlCompatible = $intlCompatible ?? $this->intlCompatible; @@ -92,8 +91,15 @@ public function __construct( } } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'zone'; } } diff --git a/Constraints/Traverse.php b/Constraints/Traverse.php index d8546e323..6571b31f1 100644 --- a/Constraints/Traverse.php +++ b/Constraints/Traverse.php @@ -26,7 +26,7 @@ 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). + * @param bool|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). */ #[HasNamedArguments] public function __construct(bool|array|null $traverse = null, mixed $payload = null) @@ -37,13 +37,24 @@ public function __construct(bool|array|null $traverse = null, mixed $payload = n if (\is_array($traverse)) { 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 = $traverse; + $traverse = $options['traverse'] ?? null; } - parent::__construct($traverse, null, $payload); + parent::__construct($options ?? null, $payload); + + $this->traverse = $traverse ?? $this->traverse; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'traverse'; } diff --git a/Constraints/Type.php b/Constraints/Type.php index f3fe56dbb..a2050e11d 100644 --- a/Constraints/Type.php +++ b/Constraints/Type.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Validates that a value is of a specific data type. @@ -32,25 +33,25 @@ class Type extends Constraint public string|array|null $type = null; /** - * @param string|list|array|null $type The type(s) to enforce on the value - * @param string[]|null $groups - * @param array|null $options + * @param string|list|null $type The type(s) to enforce on the value + * @param string[]|null $groups */ #[HasNamedArguments] public function __construct(string|array|null $type, ?string $message = null, ?array $groups = null, mixed $payload = null, ?array $options = null) { + if (null === $type && !isset($options['type'])) { + throw new MissingOptionsException(\sprintf('The options "type" must be set for constraint "%s".', self::class), ['type']); + } + if (\is_array($type) && \is_string(key($type))) { 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 ?? []); + $type = $options['type'] ?? null; } elseif (null !== $type) { 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); - } else { - $options = []; } - - $options['value'] = $type; } elseif (\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); } @@ -58,15 +59,30 @@ public function __construct(string|array|null $type, ?string $message = null, ?a parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; + $this->type = $type ?? $this->type; } + /** + * @deprecated since Symfony 7.4 + */ public function getDefaultOption(): ?string { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return 'type'; } + /** + * @deprecated since Symfony 7.4 + */ public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['type']; } } diff --git a/Constraints/Ulid.php b/Constraints/Ulid.php index 91d395fd2..c9f9dbaf6 100644 --- a/Constraints/Ulid.php +++ b/Constraints/Ulid.php @@ -47,9 +47,8 @@ class Ulid extends Constraint public string $format = self::FORMAT_BASE_32; /** - * @param array|null $options - * @param string[]|null $groups - * @param self::FORMAT_*|null $format + * @param string[]|null $groups + * @param self::FORMAT_*|null $format */ #[HasNamedArguments] public function __construct( diff --git a/Constraints/Unique.php b/Constraints/Unique.php index 1e6503785..3794b192c 100644 --- a/Constraints/Unique.php +++ b/Constraints/Unique.php @@ -38,9 +38,8 @@ class Unique extends Constraint public $normalizer; /** - * @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) + * @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( diff --git a/Constraints/Url.php b/Constraints/Url.php index b3e7256a0..08d391e38 100644 --- a/Constraints/Url.php +++ b/Constraints/Url.php @@ -40,17 +40,16 @@ class Url extends Constraint 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 - * @param bool|null $requireTld Whether to require the URL to include a top-level domain (defaults to false) + * @param string[]|string|null $protocols The protocols considered to be valid for the URL (e.g. http, https, ftp, etc.) (defaults to ['http', 'https']; use '*' to allow any protocol) + * @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) */ #[HasNamedArguments] public function __construct( ?array $options = null, ?string $message = null, - ?array $protocols = null, + array|string|null $protocols = null, ?bool $relativeProtocol = null, ?callable $normalizer = null, ?array $groups = null, @@ -68,6 +67,10 @@ public function __construct( 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".'); } + if (\is_string($protocols)) { + $protocols = (array) $protocols; + } + $this->message = $message ?? $this->message; $this->protocols = $protocols ?? $this->protocols; $this->relativeProtocol = $relativeProtocol ?? $this->relativeProtocol; diff --git a/Constraints/UrlValidator.php b/Constraints/UrlValidator.php index dc4c2486a..ad17f5d25 100644 --- a/Constraints/UrlValidator.php +++ b/Constraints/UrlValidator.php @@ -75,8 +75,15 @@ public function validate(mixed $value, Constraint $constraint): void $value = ($constraint->normalizer)($value); } + if (['*'] === $constraint->protocols) { + // Use RFC 3986 compliant scheme pattern: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + $protocols = '[a-zA-Z][a-zA-Z0-9+.-]*'; + } else { + $protocols = implode('|', $constraint->protocols); + } + $pattern = $constraint->relativeProtocol ? str_replace('(%s):', '(?:(%s):)?', static::PATTERN) : static::PATTERN; - $pattern = \sprintf($pattern, implode('|', $constraint->protocols)); + $pattern = sprintf($pattern, $protocols); if (!preg_match($pattern, $value)) { $this->context->buildViolation($constraint->message) diff --git a/Constraints/Uuid.php b/Constraints/Uuid.php index 9c6526457..3602c2fb8 100644 --- a/Constraints/Uuid.php +++ b/Constraints/Uuid.php @@ -96,7 +96,6 @@ class Uuid extends Constraint public $normalizer; /** - * @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 diff --git a/Constraints/Valid.php b/Constraints/Valid.php index 48deae8ac..6106627c6 100644 --- a/Constraints/Valid.php +++ b/Constraints/Valid.php @@ -25,9 +25,8 @@ 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) + * @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) @@ -36,7 +35,7 @@ public function __construct(?array $options = null, ?array $groups = null, $payl 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); + parent::__construct($options, $groups, $payload); $this->traverse = $traverse ?? $this->traverse; } diff --git a/Constraints/Video.php b/Constraints/Video.php new file mode 100644 index 000000000..796dfb94e --- /dev/null +++ b/Constraints/Video.php @@ -0,0 +1,254 @@ + + * + * 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\Process\ExecutableFinder; +use Symfony\Component\Process\Process; +use Symfony\Component\Validator\Attribute\HasNamedArguments; +use Symfony\Component\Validator\Exception\LogicException; + +/** + * @author Kev + * @author Nicolas Grekas + */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class Video extends File +{ + public const SIZE_NOT_DETECTED_ERROR = '5dab98df-43c8-481b-94f9-46a3c958285c'; + public const TOO_WIDE_ERROR = '9e18d6a4-aeda-4644-be8e-9e29dbfd6c4a'; + public const TOO_NARROW_ERROR = 'b267f54b-d994-46d4-9ca6-338fc4f7962f'; + public const TOO_HIGH_ERROR = '44f4c411-0199-48c2-b597-df1f5944ccde'; + public const TOO_LOW_ERROR = '0b6bc3ce-df90-40f9-90aa-5bbb840cb481'; + public const TOO_FEW_PIXEL_ERROR = '510ddf98-2eda-436e-be7e-b6f107bc0e22'; + public const TOO_MANY_PIXEL_ERROR = 'ff0a8ee8-951d-4c97-afe2-03c0d61a2a02'; + public const RATIO_TOO_BIG_ERROR = '5e6b9c21-d4d8-444d-9f4c-e3ff1e25a9a6'; + public const RATIO_TOO_SMALL_ERROR = '26985857-7447-49dc-b271-1477a76cc63c'; + public const SQUARE_NOT_ALLOWED_ERROR = '18500335-b868-4056-b2a2-aa2aeeb0cbdf'; + public const LANDSCAPE_NOT_ALLOWED_ERROR = 'cbf38fbc-04c0-457a-8c29-a6f3080e415a'; + public const PORTRAIT_NOT_ALLOWED_ERROR = '6c3e34a8-94d5-4434-9f20-fb9c0f3ab531'; + public const CORRUPTED_VIDEO_ERROR = '591b9c4d-d357-425f-8672-6b187816550e'; + public const MULTIPLE_VIDEO_STREAMS_ERROR = '2d1b2b2e-3f37-4fdd-9a2a-8b6b77b2a6a3'; + public const UNSUPPORTED_VIDEO_CODEC_ERROR = 'a9f2f6f7-2b5a-4f3c-b746-d3e2e9d1b2a1'; + public const UNSUPPORTED_VIDEO_CONTAINER_ERROR = 'b7c9d2a4-5e1f-4aa0-8f9d-1c3e2b4a6d7e'; + + // Include the mapping from the base class + + protected const ERROR_NAMES = [ + self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR', + self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR', + self::EMPTY_ERROR => 'EMPTY_ERROR', + self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', + self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR', + self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG', + self::SIZE_NOT_DETECTED_ERROR => 'SIZE_NOT_DETECTED_ERROR', + self::TOO_WIDE_ERROR => 'TOO_WIDE_ERROR', + self::TOO_NARROW_ERROR => 'TOO_NARROW_ERROR', + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', + self::TOO_FEW_PIXEL_ERROR => 'TOO_FEW_PIXEL_ERROR', + self::TOO_MANY_PIXEL_ERROR => 'TOO_MANY_PIXEL_ERROR', + self::RATIO_TOO_BIG_ERROR => 'RATIO_TOO_BIG_ERROR', + self::RATIO_TOO_SMALL_ERROR => 'RATIO_TOO_SMALL_ERROR', + self::SQUARE_NOT_ALLOWED_ERROR => 'SQUARE_NOT_ALLOWED_ERROR', + self::LANDSCAPE_NOT_ALLOWED_ERROR => 'LANDSCAPE_NOT_ALLOWED_ERROR', + self::PORTRAIT_NOT_ALLOWED_ERROR => 'PORTRAIT_NOT_ALLOWED_ERROR', + self::CORRUPTED_VIDEO_ERROR => 'CORRUPTED_VIDEO_ERROR', + self::MULTIPLE_VIDEO_STREAMS_ERROR => 'MULTIPLE_VIDEO_STREAMS_ERROR', + self::UNSUPPORTED_VIDEO_CODEC_ERROR => 'UNSUPPORTED_VIDEO_CODEC_ERROR', + self::UNSUPPORTED_VIDEO_CONTAINER_ERROR => 'UNSUPPORTED_VIDEO_CONTAINER_ERROR', + ]; + + public array|string $mimeTypes = 'video/*'; + 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 array $allowedCodecs = ['h264', 'hevc', 'h265', 'vp9', 'av1', 'mpeg4', 'mpeg2video']; + public array $allowedContainers = ['mp4', 'mov', 'mkv', 'webm', 'avi']; + + // The constant for a wrong MIME type is taken from the parent class. + public string $mimeTypesMessage = 'This file is not a valid video.'; + public string $sizeNotDetectedMessage = 'The size of the video could not be detected.'; + public string $maxWidthMessage = 'The video width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.'; + public string $minWidthMessage = 'The video width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.'; + public string $maxHeightMessage = 'The video height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.'; + public string $minHeightMessage = 'The video height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.'; + public string $minPixelsMessage = 'The video has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels.'; + public string $maxPixelsMessage = 'The video has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels.'; + public string $maxRatioMessage = 'The video ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.'; + public string $minRatioMessage = 'The video ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.'; + public string $allowSquareMessage = 'The video is square ({{ width }}x{{ height }}px). Square videos are not allowed.'; + public string $allowLandscapeMessage = 'The video is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented videos are not allowed.'; + public string $allowPortraitMessage = 'The video is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented videos are not allowed.'; + public string $corruptedMessage = 'The video file is corrupted.'; + public string $multipleVideoStreamsMessage = 'The video contains multiple streams. Only one stream is allowed.'; + public string $unsupportedCodecMessage = 'Unsupported video codec "{{ codec }}".'; + public string $unsupportedContainerMessage = 'Unsupported video container "{{ container }}".'; + + /** + * @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 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 + * @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<0, int>|null $minWidth Minimum video width + * @param positive-int|null $maxWidth Maximum video width + * @param positive-int|null $maxHeight Maximum video height + * @param int<0, int>|null $minHeight Minimum video weight + * @param positive-int|float|null $maxRatio Maximum video ratio + * @param int<0, max>|float|null $minRatio Minimum video ratio + * @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 video (defaults to true) + * @param bool|null $allowLandscape Whether to allow a landscape video (defaults to true) + * @param bool|null $allowPortrait Whether to allow a portrait video (defaults to true) + * @param string|null $sizeNotDetectedMessage Message if the system can not determine video size and there is a size constraint to validate + * @param string[]|null $allowedCodecs Allowed codec names + * @param string[]|null $allowedContainers Allowed container names + * + * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types + */ + #[HasNamedArguments] + public function __construct( + int|string|null $maxSize = null, + ?bool $binaryFormat = null, + array|string|null $mimeTypes = null, + ?int $filenameMaxLength = null, + ?int $minWidth = null, + ?int $maxWidth = null, + ?int $maxHeight = null, + ?int $minHeight = null, + int|float|null $maxRatio = null, + int|float|null $minRatio = null, + int|float|null $minPixels = null, + int|float|null $maxPixels = null, + ?bool $allowSquare = null, + ?bool $allowLandscape = null, + ?bool $allowPortrait = null, + ?array $allowedCodecs = null, + ?array $allowedContainers = null, + ?string $notFoundMessage = null, + ?string $notReadableMessage = null, + ?string $maxSizeMessage = null, + ?string $mimeTypesMessage = null, + ?string $disallowEmptyMessage = null, + ?string $filenameTooLongMessage = null, + ?string $uploadIniSizeErrorMessage = null, + ?string $uploadFormSizeErrorMessage = null, + ?string $uploadPartialErrorMessage = null, + ?string $uploadNoFileErrorMessage = null, + ?string $uploadNoTmpDirErrorMessage = null, + ?string $uploadCantWriteErrorMessage = null, + ?string $uploadExtensionErrorMessage = null, + ?string $uploadErrorMessage = null, + ?string $sizeNotDetectedMessage = null, + ?string $maxWidthMessage = null, + ?string $minWidthMessage = null, + ?string $maxHeightMessage = null, + ?string $minHeightMessage = null, + ?string $minPixelsMessage = null, + ?string $maxPixelsMessage = null, + ?string $maxRatioMessage = null, + ?string $minRatioMessage = null, + ?string $allowSquareMessage = null, + ?string $allowLandscapeMessage = null, + ?string $allowPortraitMessage = null, + ?string $corruptedMessage = null, + ?string $multipleVideoStreamsMessage = null, + ?string $unsupportedCodecMessage = null, + ?string $unsupportedContainerMessage = null, + ?array $groups = null, + mixed $payload = null, + ) { + static $hasFfprobe; + if (!$hasFfprobe) { + if (!class_exists(Process::class)) { + throw new LogicException('The Process component is required to use the Video constraint. Try running "composer require symfony/process".'); + } + if (!$hasFfprobe ??= (new ExecutableFinder())->find('ffprobe')) { + throw new LogicException('The ffprobe binary is required to use the Video constraint.'); + } + } + + parent::__construct( + null, + $maxSize, + $binaryFormat, + $mimeTypes, + $filenameMaxLength, + $notFoundMessage, + $notReadableMessage, + $maxSizeMessage, + $mimeTypesMessage, + $disallowEmptyMessage, + $filenameTooLongMessage, + $uploadIniSizeErrorMessage, + $uploadFormSizeErrorMessage, + $uploadPartialErrorMessage, + $uploadNoFileErrorMessage, + $uploadNoTmpDirErrorMessage, + $uploadCantWriteErrorMessage, + $uploadExtensionErrorMessage, + $uploadErrorMessage, + $groups, + $payload + ); + + $this->minWidth = $minWidth ?? $this->minWidth; + $this->maxWidth = $maxWidth ?? $this->maxWidth; + $this->maxHeight = $maxHeight ?? $this->maxHeight; + $this->minHeight = $minHeight ?? $this->minHeight; + $this->maxRatio = $maxRatio ?? $this->maxRatio; + $this->minRatio = $minRatio ?? $this->minRatio; + $this->minPixels = $minPixels ?? $this->minPixels; + $this->maxPixels = $maxPixels ?? $this->maxPixels; + $this->allowSquare = $allowSquare ?? $this->allowSquare; + $this->allowLandscape = $allowLandscape ?? $this->allowLandscape; + $this->allowPortrait = $allowPortrait ?? $this->allowPortrait; + $this->allowedCodecs = $allowedCodecs ?? $this->allowedCodecs; + $this->allowedContainers = $allowedContainers ?? $this->allowedContainers; + $this->sizeNotDetectedMessage = $sizeNotDetectedMessage ?? $this->sizeNotDetectedMessage; + $this->maxWidthMessage = $maxWidthMessage ?? $this->maxWidthMessage; + $this->minWidthMessage = $minWidthMessage ?? $this->minWidthMessage; + $this->maxHeightMessage = $maxHeightMessage ?? $this->maxHeightMessage; + $this->minHeightMessage = $minHeightMessage ?? $this->minHeightMessage; + $this->minPixelsMessage = $minPixelsMessage ?? $this->minPixelsMessage; + $this->maxPixelsMessage = $maxPixelsMessage ?? $this->maxPixelsMessage; + $this->maxRatioMessage = $maxRatioMessage ?? $this->maxRatioMessage; + $this->minRatioMessage = $minRatioMessage ?? $this->minRatioMessage; + $this->allowSquareMessage = $allowSquareMessage ?? $this->allowSquareMessage; + $this->allowLandscapeMessage = $allowLandscapeMessage ?? $this->allowLandscapeMessage; + $this->allowPortraitMessage = $allowPortraitMessage ?? $this->allowPortraitMessage; + $this->corruptedMessage = $corruptedMessage ?? $this->corruptedMessage; + $this->multipleVideoStreamsMessage = $multipleVideoStreamsMessage ?? $this->multipleVideoStreamsMessage; + $this->unsupportedCodecMessage = $unsupportedCodecMessage ?? $this->unsupportedCodecMessage; + $this->unsupportedContainerMessage = $unsupportedContainerMessage ?? $this->unsupportedContainerMessage; + + if (!\in_array('video/*', (array) $this->mimeTypes, true) && null === $mimeTypesMessage) { + $this->mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.'; + } + } +} diff --git a/Constraints/VideoValidator.php b/Constraints/VideoValidator.php new file mode 100644 index 000000000..afbe30b5f --- /dev/null +++ b/Constraints/VideoValidator.php @@ -0,0 +1,265 @@ + + * + * 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\Process\Process; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Kev + * @author Nicolas Grekas + */ +class VideoValidator extends FileValidator +{ + public function validate(mixed $value, Constraint $constraint): void + { + if (!$constraint instanceof Video) { + throw new UnexpectedTypeException($constraint, Video::class); + } + + $violations = \count($this->context->getViolations()); + + parent::validate($value, $constraint); + + $failed = \count($this->context->getViolations()) !== $violations; + + if ($failed || null === $value || '' === $value) { + return; + } + + if (null === $constraint->minWidth && null === $constraint->maxWidth + && null === $constraint->minHeight && null === $constraint->maxHeight + && null === $constraint->minPixels && null === $constraint->maxPixels + && null === $constraint->minRatio && null === $constraint->maxRatio + && $constraint->allowSquare && $constraint->allowLandscape && $constraint->allowPortrait + ) { + return; + } + + $process = new Process([ + 'ffprobe', + '-v', 'error', + '-select_streams', 'v', + '-show_entries', 'stream=index,codec_type,codec_name,width,height', + '-show_entries', 'format=format_name', + '-of', 'json', + (string) $value, + ]); + $process->run(); + + if (!$process->isSuccessful()) { + $this->context->buildViolation($constraint->corruptedMessage) + ->setCode(Video::CORRUPTED_VIDEO_ERROR) + ->addViolation(); + + return; + } + + $meta = json_decode($process->getOutput(), true) ?: []; + $streams = $meta['streams'] ?? []; + $formats = explode(',', strtolower($meta['format']['format_name'] ?? 'unknown')); + + if (!($streams[0]['width'] ?? false) || !($streams[0]['height'] ?? false)) { + $this->context->buildViolation($constraint->sizeNotDetectedMessage) + ->setCode(Video::SIZE_NOT_DETECTED_ERROR) + ->addViolation(); + + return; + } + + $width = $streams[0]['width']; + $height = $streams[0]['height']; + + if (1 !== \count($streams)) { + $this->context->buildViolation($constraint->multipleVideoStreamsMessage) + ->setCode(Video::MULTIPLE_VIDEO_STREAMS_ERROR) + ->addViolation(); + + return; + } + + if ($constraint->allowedCodecs) { + foreach ($streams as $stream) { + $codec = strtolower($stream['codec_name'] ?? 'unknown'); + if (!\in_array($codec, $constraint->allowedCodecs, true)) { + $this->context->buildViolation($constraint->unsupportedCodecMessage) + ->setParameter('{{ codec }}', $codec) + ->setCode(Video::UNSUPPORTED_VIDEO_CODEC_ERROR) + ->addViolation(); + + return; + } + } + } + + if ($constraint->allowedContainers && !array_intersect($formats, $constraint->allowedContainers)) { + $this->context->buildViolation($constraint->unsupportedContainerMessage) + ->setParameter('{{ container }}', $formats[0]) + ->setCode(Video::UNSUPPORTED_VIDEO_CONTAINER_ERROR) + ->addViolation(); + + return; + } + + if ($constraint->minWidth) { + if ($constraint->minWidth < 0) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum width.', $constraint->minWidth)); + } + + if ($width < $constraint->minWidth) { + $this->context->buildViolation($constraint->minWidthMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ min_width }}', $constraint->minWidth) + ->setCode(Video::TOO_NARROW_ERROR) + ->addViolation(); + + return; + } + } + + if ($constraint->maxWidth) { + if ($constraint->maxWidth < 0) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum width.', $constraint->maxWidth)); + } + + if ($width > $constraint->maxWidth) { + $this->context->buildViolation($constraint->maxWidthMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ max_width }}', $constraint->maxWidth) + ->setCode(Video::TOO_WIDE_ERROR) + ->addViolation(); + + return; + } + } + + if ($constraint->minHeight) { + if ($constraint->minHeight < 0) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum height.', $constraint->minHeight)); + } + + if ($height < $constraint->minHeight) { + $this->context->buildViolation($constraint->minHeightMessage) + ->setParameter('{{ height }}', $height) + ->setParameter('{{ min_height }}', $constraint->minHeight) + ->setCode(Video::TOO_LOW_ERROR) + ->addViolation(); + + return; + } + } + + if ($constraint->maxHeight) { + if ($constraint->maxHeight < 0) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum height.', $constraint->maxHeight)); + } + + if ($height > $constraint->maxHeight) { + $this->context->buildViolation($constraint->maxHeightMessage) + ->setParameter('{{ height }}', $height) + ->setParameter('{{ max_height }}', $constraint->maxHeight) + ->setCode(Video::TOO_HIGH_ERROR) + ->addViolation(); + } + } + + $pixels = $width * $height; + + if (null !== $constraint->minPixels) { + if ($constraint->minPixels < 0) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum amount of pixels.', $constraint->minPixels)); + } + + if ($pixels < $constraint->minPixels) { + $this->context->buildViolation($constraint->minPixelsMessage) + ->setParameter('{{ pixels }}', $pixels) + ->setParameter('{{ min_pixels }}', $constraint->minPixels) + ->setParameter('{{ height }}', $height) + ->setParameter('{{ width }}', $width) + ->setCode(Video::TOO_FEW_PIXEL_ERROR) + ->addViolation(); + } + } + + if (null !== $constraint->maxPixels) { + if ($constraint->maxPixels < 0) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum amount of pixels.', $constraint->maxPixels)); + } + + if ($pixels > $constraint->maxPixels) { + $this->context->buildViolation($constraint->maxPixelsMessage) + ->setParameter('{{ pixels }}', $pixels) + ->setParameter('{{ max_pixels }}', $constraint->maxPixels) + ->setParameter('{{ height }}', $height) + ->setParameter('{{ width }}', $width) + ->setCode(Video::TOO_MANY_PIXEL_ERROR) + ->addViolation(); + } + } + + $ratio = round($height > 0 ? $width / $height : 0, 2); + + if (null !== $constraint->minRatio) { + if (!is_numeric((string) $constraint->minRatio)) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid minimum ratio.', $constraint->minRatio)); + } + + if ($ratio < round($constraint->minRatio, 2)) { + $this->context->buildViolation($constraint->minRatioMessage) + ->setParameter('{{ ratio }}', $ratio) + ->setParameter('{{ min_ratio }}', round($constraint->minRatio, 2)) + ->setCode(Video::RATIO_TOO_SMALL_ERROR) + ->addViolation(); + } + } + + if (null !== $constraint->maxRatio) { + if (!is_numeric((string) $constraint->maxRatio)) { + throw new ConstraintDefinitionException(\sprintf('"%s" is not a valid maximum ratio.', $constraint->maxRatio)); + } + + if ($ratio > round($constraint->maxRatio, 2)) { + $this->context->buildViolation($constraint->maxRatioMessage) + ->setParameter('{{ ratio }}', $ratio) + ->setParameter('{{ max_ratio }}', round($constraint->maxRatio, 2)) + ->setCode(Video::RATIO_TOO_BIG_ERROR) + ->addViolation(); + } + } + + if (!$constraint->allowSquare && $width == $height) { + $this->context->buildViolation($constraint->allowSquareMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ height }}', $height) + ->setCode(Video::SQUARE_NOT_ALLOWED_ERROR) + ->addViolation(); + } + + if (!$constraint->allowLandscape && $width > $height) { + $this->context->buildViolation($constraint->allowLandscapeMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ height }}', $height) + ->setCode(Video::LANDSCAPE_NOT_ALLOWED_ERROR) + ->addViolation(); + } + + if (!$constraint->allowPortrait && $width < $height) { + $this->context->buildViolation($constraint->allowPortraitMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ height }}', $height) + ->setCode(Video::PORTRAIT_NOT_ALLOWED_ERROR) + ->addViolation(); + } + } +} diff --git a/Constraints/When.php b/Constraints/When.php index 74601c1d1..b7482f938 100644 --- a/Constraints/When.php +++ b/Constraints/When.php @@ -16,6 +16,7 @@ use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\LogicException; +use Symfony\Component\Validator\Exception\MissingOptionsException; /** * Conditionally apply validation constraints based on an expression using the ExpressionLanguage syntax. @@ -31,12 +32,11 @@ class When extends Composite public array|Constraint $otherwise = []; /** - * @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 - * @param array|null $options - * @param Constraint[]|Constraint $otherwise One or multiple constraints that are applied if the expression returns false + * @param string|Expression|\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 + * @param Constraint[]|Constraint $otherwise One or multiple constraints that are applied if the expression returns false */ #[HasNamedArguments] 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 = []) @@ -52,15 +52,21 @@ public function __construct(string|Expression|array|\Closure $expression, array| } else { 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['expression'] = $expression; + if (null !== $constraints) { + $options['constraints'] = $constraints; + } + $options['otherwise'] = $otherwise; } else { - $options = []; - } + if (null === $constraints) { + throw new MissingOptionsException(\sprintf('The options "constraints" must be set for constraint "%s".', self::class), ['constraints']); + } - $options['expression'] = $expression; - if (null !== $constraints) { - $options['constraints'] = $constraints; + $this->expression = $expression; + $this->constraints = $constraints; + $this->otherwise = $otherwise; } - $options['otherwise'] = $otherwise; } if (!\is_array($options['constraints'] ?? [])) { @@ -71,21 +77,17 @@ public function __construct(string|Expression|array|\Closure $expression, array| $options['otherwise'] = [$options['otherwise']]; } - if (null !== $groups) { - $options['groups'] = $groups; - } - - if (null !== $payload) { - $options['payload'] = $payload; - } - - parent::__construct($options); + parent::__construct($options, $groups, $payload); $this->values = $values ?? $this->values; } public function getRequiredOptions(): array { + if (0 === \func_num_args() || func_get_arg(0)) { + trigger_deprecation('symfony/validator', '7.4', 'The %s() method is deprecated.', __METHOD__); + } + return ['expression', 'constraints']; } diff --git a/DependencyInjection/AttributeMetadataPass.php b/DependencyInjection/AttributeMetadataPass.php new file mode 100644 index 000000000..a337504a5 --- /dev/null +++ b/DependencyInjection/AttributeMetadataPass.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\Validator\Exception\MappingException; + +/** + * @author Nicolas Grekas + */ +final class AttributeMetadataPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition('validator.builder')) { + return; + } + + $resolve = $container->getParameterBag()->resolveValue(...); + $mappedClasses = []; + foreach ($container->getDefinitions() as $id => $definition) { + if (!$definition->hasTag('validator.attribute_metadata')) { + continue; + } + if (!$definition->hasTag('container.excluded')) { + throw new InvalidArgumentException(\sprintf('The resource "%s" tagged "validator.attribute_metadata" is missing the "container.excluded" tag.', $id)); + } + $class = $resolve($definition->getClass()); + foreach ($definition->getTag('validator.attribute_metadata') as $attributes) { + if ($class !== $for = $attributes['for'] ?? $class) { + $this->checkSourceMapsToTarget($container, $class, $for); + } + + $mappedClasses[$for][$class] = true; + } + } + + if (!$mappedClasses) { + return; + } + + ksort($mappedClasses); + + $container->getDefinition('validator.builder') + ->addMethodCall('addAttributeMappings', [array_map('array_keys', $mappedClasses)]); + } + + private function checkSourceMapsToTarget(ContainerBuilder $container, string $source, string $target): void + { + $source = $container->getReflectionClass($source); + $target = $container->getReflectionClass($target); + + foreach ($source->getProperties() as $p) { + if ($p->class === $source->name && !($target->hasProperty($p->name) && $target->getProperty($p->name)->class === $target->name)) { + throw new MappingException(\sprintf('The property "%s" on "%s" is not present on "%s".', $p->name, $source->name, $target->name)); + } + } + + foreach ($source->getMethods() as $m) { + if ($m->class === $source->name && !($target->hasMethod($m->name) && $target->getMethod($m->name)->class === $target->name)) { + throw new MappingException(\sprintf('The method "%s" on "%s" is not present on "%s".', $m->name, $source->name, $target->name)); + } + } + } +} diff --git a/Mapping/ClassMetadata.php b/Mapping/ClassMetadata.php index d812255c4..b9ac2ec41 100644 --- a/Mapping/ClassMetadata.php +++ b/Mapping/ClassMetadata.php @@ -31,67 +31,36 @@ */ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface { - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getClassName()} instead. - */ - public string $name; - - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getDefaultGroup()} instead. - */ - public string $defaultGroup; + private string $name; + private string $defaultGroup; /** * @var MemberMetadata[][] - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getPropertyMetadata()} instead. */ - public array $members = []; + private array $members = []; /** * @var PropertyMetadata[] - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getPropertyMetadata()} instead. */ - public array $properties = []; + private array $properties = []; /** * @var GetterMetadata[] - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getPropertyMetadata()} instead. */ - public array $getters = []; + private array $getters = []; - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getGroupSequence()} instead. - */ - public ?GroupSequence $groupSequence = null; + private ?GroupSequence $groupSequence = null; + private bool $groupSequenceProvider = false; + private ?string $groupProvider = null; /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link isGroupSequenceProvider()} instead. - */ - public bool $groupSequenceProvider = false; - - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getGroupProvider()} instead. + * The strategy for cascading objects. + * + * By default, objects are not cascaded. + * + * @var CascadingStrategy::* */ - public ?string $groupProvider = null; + private int $cascadingStrategy = CascadingStrategy::NONE; /** * The strategy for traversing traversable objects. @@ -99,12 +68,8 @@ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface * By default, only instances of {@link \Traversable} are traversed. * * @var TraversalStrategy::* - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getTraversalStrategy()} instead. */ - public int $traversalStrategy = TraversalStrategy::IMPLICIT; + private int $traversalStrategy = TraversalStrategy::IMPLICIT; private \ReflectionClass $reflClass; @@ -119,14 +84,52 @@ public function __construct(string $class) } } - public function __sleep(): array + public function __serialize(): array { - $parentProperties = parent::__sleep(); + if (self::class === (new \ReflectionMethod($this, '__sleep'))->class || self::class !== (new \ReflectionMethod($this, '__serialize'))->class) { + return array_filter([ + 'cascadingStrategy' => CascadingStrategy::NONE !== $this->cascadingStrategy ? $this->cascadingStrategy : null, + 'traversalStrategy' => TraversalStrategy::IMPLICIT !== $this->traversalStrategy ? $this->traversalStrategy : null, + ] + parent::__serialize() + [ + 'getters' => $this->getters, + 'groupSequence' => $this->groupSequence, + 'groupSequenceProvider' => $this->groupSequenceProvider, + 'groupProvider' => $this->groupProvider, + 'members' => $this->members, + 'name' => $this->name, + 'properties' => $this->properties, + 'defaultGroup' => $this->defaultGroup, + ]); + } - // Don't store the cascading strategy. Classes never cascade. - unset($parentProperties[array_search('cascadingStrategy', $parentProperties)]); + trigger_deprecation('symfony/validator', '7.4', 'Implementing "%s::__sleep()" is deprecated, use "__serialize()" instead.', get_debug_type($this)); - return array_merge($parentProperties, [ + $data = []; + foreach ($this->__sleep() as $key) { + try { + if (($r = new \ReflectionProperty($this, $key))->isInitialized($this)) { + $data[$key] = $r->getValue($this); + } + } catch (\ReflectionException) { + $data[$key] = $this->$key; + } + } + + return $data; + } + + /** + * @deprecated since Symfony 7.4, will be removed in 8.0 + */ + public function __sleep(): array + { + trigger_deprecation('symfony/validator', '7.4', 'Calling "%s::__sleep()" is deprecated, use "__serialize()" instead.', get_debug_type($this)); + + return [ + 'constraints', + 'constraintsByGroup', + 'traversalStrategy', + 'autoMappingStrategy', 'getters', 'groupSequence', 'groupSequenceProvider', @@ -135,7 +138,7 @@ public function __sleep(): array 'name', 'properties', 'defaultGroup', - ]); + ]; } public function getClassName(): string @@ -178,13 +181,7 @@ public function addConstraint(Constraint $constraint): static $this->checkConstraint($constraint); if ($constraint instanceof Traverse) { - if ($constraint->traverse) { - // If traverse is true, traversal should be explicitly enabled - $this->traversalStrategy = TraversalStrategy::TRAVERSE; - } else { - // If traverse is false, traversal should be explicitly disabled - $this->traversalStrategy = TraversalStrategy::NONE; - } + $this->traversalStrategy = $constraint->traverse ? TraversalStrategy::TRAVERSE : TraversalStrategy::NONE; // The constraint is not added return $this; @@ -209,6 +206,14 @@ public function addConstraint(Constraint $constraint): static $constraint->addImplicitGroupName($this->getDefaultGroup()); + if ($constraint instanceof Valid && null === $constraint->groups) { + $this->cascadingStrategy = CascadingStrategy::CASCADE; + $this->traversalStrategy = $constraint->traverse ? TraversalStrategy::IMPLICIT : TraversalStrategy::NONE; + + // The constraint is not added + return $this; + } + parent::addConstraint($constraint); return $this; @@ -338,11 +343,8 @@ public function mergeConstraints(self $source): void $member = clone $member; foreach ($member->getConstraints() as $constraint) { - if (\in_array($constraint::DEFAULT_GROUP, $constraint->groups, true)) { - $member->constraintsByGroup[$this->getDefaultGroup()][] = $constraint; - } - $constraint->addImplicitGroupName($this->getDefaultGroup()); + $member->addConstraint($constraint); } if ($member instanceof MemberMetadata && !$member->isPrivate($this->name)) { @@ -464,6 +466,11 @@ public function getCascadingStrategy(): int return $this->cascadingStrategy; } + public function getTraversalStrategy(): int + { + return $this->traversalStrategy; + } + private function addPropertyMetadata(PropertyMetadataInterface $metadata): void { $property = $metadata->getPropertyName(); diff --git a/Mapping/GenericMetadata.php b/Mapping/GenericMetadata.php index 43992255c..ce743e84b 100644 --- a/Mapping/GenericMetadata.php +++ b/Mapping/GenericMetadata.php @@ -30,21 +30,13 @@ class GenericMetadata implements MetadataInterface { /** * @var Constraint[] - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getConstraints()} and {@link findConstraints()} instead. */ - public array $constraints = []; + private array $constraints = []; /** * @var array - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link findConstraints()} instead. */ - public array $constraintsByGroup = []; + private array $constraintsByGroup = []; /** * The strategy for cascading objects. @@ -52,12 +44,8 @@ class GenericMetadata implements MetadataInterface * By default, objects are not cascaded. * * @var CascadingStrategy::* - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getCascadingStrategy()} instead. */ - public int $cascadingStrategy = CascadingStrategy::NONE; + private int $cascadingStrategy = CascadingStrategy::NONE; /** * The strategy for traversing traversable objects. @@ -65,31 +53,51 @@ class GenericMetadata implements MetadataInterface * By default, traversable objects are not traversed. * * @var TraversalStrategy::* - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getTraversalStrategy()} instead. */ - public int $traversalStrategy = TraversalStrategy::NONE; + private int $traversalStrategy = TraversalStrategy::NONE; /** * Is auto-mapping enabled? * * @var AutoMappingStrategy::* - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getAutoMappingStrategy()} instead. */ - public int $autoMappingStrategy = AutoMappingStrategy::NONE; + private int $autoMappingStrategy = AutoMappingStrategy::NONE; + + public function __serialize(): array + { + if (self::class === (new \ReflectionMethod($this, '__sleep'))->class || self::class !== (new \ReflectionMethod($this, '__serialize'))->class) { + return array_filter([ + 'constraints' => $this->constraints, + 'constraintsByGroup' => $this->constraintsByGroup, + 'cascadingStrategy' => CascadingStrategy::NONE !== $this->cascadingStrategy ? $this->cascadingStrategy : null, + 'traversalStrategy' => TraversalStrategy::NONE !== $this->traversalStrategy ? $this->traversalStrategy : null, + 'autoMappingStrategy' => AutoMappingStrategy::NONE !== $this->autoMappingStrategy ? $this->autoMappingStrategy : null, + ]); + } + + trigger_deprecation('symfony/validator', '7.4', 'Implementing "%s::__sleep()" is deprecated, use "__serialize()" instead.', get_debug_type($this)); + + $data = []; + foreach ($this->__sleep() as $key) { + try { + if (($r = new \ReflectionProperty($this, $key))->isInitialized($this)) { + $data[$key] = $r->getValue($this); + } + } catch (\ReflectionException) { + $data[$key] = $this->$key; + } + } + + return $data; + } /** - * Returns the names of the properties that should be serialized. - * - * @return string[] + * @deprecated since Symfony 7.4, will be replaced by `__serialize()` in 8.0 */ public function __sleep(): array { + trigger_deprecation('symfony/validator', '7.4', 'Calling "%s::__sleep()" is deprecated, use "__serialize()" instead.', get_debug_type($this)); + return [ 'constraints', 'constraintsByGroup', @@ -99,9 +107,6 @@ public function __sleep(): array ]; } - /** - * Clones this object. - */ public function __clone() { $constraints = $this->constraints; @@ -138,13 +143,9 @@ public function addConstraint(Constraint $constraint): static if ($constraint instanceof Valid && null === $constraint->groups) { $this->cascadingStrategy = CascadingStrategy::CASCADE; + $this->traversalStrategy = $constraint->traverse ? TraversalStrategy::IMPLICIT : TraversalStrategy::NONE; - if ($constraint->traverse) { - $this->traversalStrategy = TraversalStrategy::IMPLICIT; - } else { - $this->traversalStrategy = TraversalStrategy::NONE; - } - + // The constraint is not added return $this; } @@ -155,19 +156,23 @@ public function addConstraint(Constraint $constraint): static return $this; } - $this->constraints[] = $constraint; + if (!\in_array($constraint, $this->constraints, true)) { + $this->constraints[] = $constraint; + } foreach ($constraint->groups as $group) { - $this->constraintsByGroup[$group][] = $constraint; + if (!\in_array($constraint, $this->constraintsByGroup[$group] ??= [], true)) { + $this->constraintsByGroup[$group][] = $constraint; + } } return $this; } /** - * Adds an list of constraints. + * Adds a list of constraints. * - * @param Constraint[] $constraints The constraints to add + * @param Constraint[] $constraints * * @return $this */ diff --git a/Mapping/Loader/AbstractLoader.php b/Mapping/Loader/AbstractLoader.php index ebff64fd5..d6b1ab777 100644 --- a/Mapping/Loader/AbstractLoader.php +++ b/Mapping/Loader/AbstractLoader.php @@ -94,10 +94,16 @@ protected function newConstraint(string $name, mixed $options = null): Constrain } if (1 === \count($options) && isset($options['value'])) { + if (\func_num_args() < 3 || !func_get_arg(2)) { + trigger_deprecation('symfony/validator', '7.4', 'Using the "value" option to configure the "%s" constraint is deprecated.', $className); + } + return new $className($options['value']); } if (array_is_list($options)) { + trigger_deprecation('symfony/validator', '7.4', 'Configuring the "%s" without passing its option names is deprecated.', $className); + return new $className($options); } @@ -105,6 +111,8 @@ protected function newConstraint(string $name, mixed $options = null): Constrain return new $className(...$options); } catch (\Error $e) { if (str_starts_with($e->getMessage(), 'Unknown named parameter ')) { + trigger_deprecation('symfony/validator', '7.4', 'Using option names not matching the named arguments of the "%s" constraint is deprecated.', $className); + return new $className($options); } diff --git a/Mapping/Loader/AttributeLoader.php b/Mapping/Loader/AttributeLoader.php index ddf0b78b7..0852823aa 100644 --- a/Mapping/Loader/AttributeLoader.php +++ b/Mapping/Loader/AttributeLoader.php @@ -27,9 +27,40 @@ */ class AttributeLoader implements LoaderInterface { + /** + * @param array $mappedClasses + */ + public function __construct( + private bool $allowAnyClass = true, + private array $mappedClasses = [], + ) { + } + + /** + * @return class-string[] + */ + public function getMappedClasses(): array + { + return array_keys($this->mappedClasses); + } + public function loadClassMetadata(ClassMetadata $metadata): bool { - $reflClass = $metadata->getReflectionClass(); + if (!$sourceClasses = $this->mappedClasses[$metadata->getClassName()] ??= $this->allowAnyClass ? [$metadata->getClassName()] : []) { + return false; + } + + $success = false; + foreach ($sourceClasses as $sourceClass) { + $reflClass = $metadata->getClassName() === $sourceClass ? $metadata->getReflectionClass() : new \ReflectionClass($sourceClass); + $success = $this->doLoadClassMetadata($reflClass, $metadata) || $success; + } + + return $success; + } + + public function doLoadClassMetadata(\ReflectionClass $reflClass, ClassMetadata $metadata): bool + { $className = $reflClass->name; $success = false; diff --git a/Mapping/Loader/XmlFileLoader.php b/Mapping/Loader/XmlFileLoader.php index 83a14b2e8..25af501d8 100644 --- a/Mapping/Loader/XmlFileLoader.php +++ b/Mapping/Loader/XmlFileLoader.php @@ -26,7 +26,7 @@ class XmlFileLoader extends FileLoader /** * The XML nodes of the mapping file. * - * @var \SimpleXMLElement[] + * @var array */ protected array $classes; @@ -55,7 +55,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool /** * Return the names of the classes mapped in this file. * - * @return string[] + * @return class-string[] */ public function getMappedClasses(): array { @@ -80,6 +80,8 @@ protected function parseConstraints(\SimpleXMLElement $nodes): array foreach ($nodes as $node) { if (\count($node) > 0) { if (\count($node->value) > 0) { + trigger_deprecation('symfony/validator', '7.4', 'Using the "value" XML element to configure an option for the "%s" is deprecated. Use the "option" element instead.', (string) $node['name']); + $options = [ 'value' => $this->parseValues($node->value), ]; @@ -100,7 +102,7 @@ protected function parseConstraints(\SimpleXMLElement $nodes): array $options['groups'] = (array) $options['groups']; } - $constraints[] = $this->newConstraint((string) $node['name'], $options); + $constraints[] = $this->newConstraint((string) $node['name'], $options, true); } return $constraints; diff --git a/Mapping/Loader/YamlFileLoader.php b/Mapping/Loader/YamlFileLoader.php index 78d1b0ef9..9c168aff6 100644 --- a/Mapping/Loader/YamlFileLoader.php +++ b/Mapping/Loader/YamlFileLoader.php @@ -56,7 +56,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool /** * Return the names of the classes mapped in this file. * - * @return string[] + * @return class-string[] */ public function getMappedClasses(): array { @@ -87,12 +87,14 @@ protected function parseNodes(array $nodes): array } if (null !== $options && (!\is_array($options) || array_is_list($options))) { + trigger_deprecation('symfony/validator', '7.4', 'Not using a YAML mapping of constraint option names to their values to configure the "%s" constraint is deprecated.', key($childNodes)); + $options = [ 'value' => $options, ]; } - $values[] = $this->newConstraint(key($childNodes), $options); + $values[] = $this->newConstraint(key($childNodes), $options, true); } else { if (\is_array($childNodes)) { $childNodes = $this->parseNodes($childNodes); diff --git a/Mapping/Loader/schema/validation.schema.json b/Mapping/Loader/schema/validation.schema.json new file mode 100644 index 000000000..0778929c4 --- /dev/null +++ b/Mapping/Loader/schema/validation.schema.json @@ -0,0 +1,123 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Symfony Validation Mapping Schema", + "description": "JSON schema for Symfony's validation mapping", + "type": "object", + "additionalProperties": false, + "properties": { + "namespaces": { + "type": "object", + "description": "Namespace aliases for constraint classes", + "additionalProperties": { + "type": "string", + "description": "Full namespace path" + } + } + }, + "patternProperties": { + "^[A-Za-z0-9\\\\_]+$": { + "type": "object", + "description": "Class metadata configuration", + "additionalProperties": false, + "properties": { + "group_sequence": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + }, + "description": "Array of validation group names" + }, + { + "type": "boolean" + } + ], + "description": "Validation group sequence or group sequence provider flag" + }, + "group_sequence_provider": { + "oneOf": [ + { + "type": "boolean", + "description": "Enable/disable group sequence provider" + }, + { + "type": "string", + "description": "Class name of the group sequence provider" + } + ] + }, + "constraints": { + "$ref": "#/definitions/constraintArray" + }, + "properties": { + "type": "object", + "description": "Property-level constraints", + "additionalProperties": { + "$ref": "#/definitions/constraintArrayOrNull" + } + }, + "getters": { + "type": "object", + "description": "Getter method constraints", + "additionalProperties": { + "$ref": "#/definitions/constraintArrayOrNull" + } + } + } + } + }, + "definitions": { + "constraintArray": { + "type": "array", + "description": "Array of constraints", + "items": { + "oneOf": [ + { + "type": "object", + "description": "Constraint with options", + "minProperties": 1, + "maxProperties": 1, + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } + }, + { + "type": "string", + "description": "Simple constraint name" + } + ] + } + }, + "constraintArrayOrNull": { + "oneOf": [ + { + "type": "null", + "description": "No constraints" + }, + { + "$ref": "#/definitions/constraintArray" + } + ] + } + } +} diff --git a/Mapping/MemberMetadata.php b/Mapping/MemberMetadata.php index 6df8add12..7487fa0ba 100644 --- a/Mapping/MemberMetadata.php +++ b/Mapping/MemberMetadata.php @@ -29,26 +29,9 @@ */ abstract class MemberMetadata extends GenericMetadata implements PropertyMetadataInterface { - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getClassName()} instead. - */ - public string $class; - - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getName()} instead. - */ - public string $name; - - /** - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. Use - * {@link getPropertyName()} instead. - */ - public string $property; + private string $class; + private string $name; + private string $property; /** * @var \ReflectionMethod[]|\ReflectionProperty[] @@ -76,17 +59,53 @@ public function addConstraint(Constraint $constraint): static return $this; } + public function __serialize(): array + { + if (self::class === (new \ReflectionMethod($this, '__sleep'))->class || self::class !== (new \ReflectionMethod($this, '__serialize'))->class) { + return parent::__serialize() + [ + 'class' => $this->class, + 'name' => $this->name, + 'property' => $this->property, + ]; + } + + trigger_deprecation('symfony/validator', '7.4', 'Implementing "%s::__sleep()" is deprecated, use "__serialize()" instead.', get_debug_type($this)); + + $data = []; + foreach ($this->__sleep() as $key) { + try { + if (($r = new \ReflectionProperty($this, $key))->isInitialized($this)) { + $data[$key] = $r->getValue($this); + } + } catch (\ReflectionException) { + $data[$key] = $this->$key; + } + } + + return $data; + } + + /** + * @deprecated since Symfony 7.4, will be replaced by `__serialize()` in 8.0 + */ public function __sleep(): array { - return array_merge(parent::__sleep(), [ + trigger_deprecation('symfony/validator', '7.4', 'Calling "%s::__sleep()" is deprecated, use "__serialize()" instead.', get_debug_type($this)); + + return [ + 'constraints', + 'constraintsByGroup', + 'cascadingStrategy', + 'traversalStrategy', + 'autoMappingStrategy', 'class', 'name', 'property', - ]); + ]; } /** - * Returns the name of the member. + * Returns the name of the property or its getter. */ public function getName(): string { @@ -103,33 +122,21 @@ public function getPropertyName(): string return $this->property; } - /** - * Returns whether this member is public. - */ public function isPublic(object|string $objectOrClassName): bool { return $this->getReflectionMember($objectOrClassName)->isPublic(); } - /** - * Returns whether this member is protected. - */ public function isProtected(object|string $objectOrClassName): bool { return $this->getReflectionMember($objectOrClassName)->isProtected(); } - /** - * Returns whether this member is private. - */ public function isPrivate(object|string $objectOrClassName): bool { return $this->getReflectionMember($objectOrClassName)->isPrivate(); } - /** - * Returns the reflection instance for accessing the member's value. - */ public function getReflectionMember(object|string $objectOrClassName): \ReflectionMethod|\ReflectionProperty { $className = \is_string($objectOrClassName) ? $objectOrClassName : $objectOrClassName::class; diff --git a/Resources/translations/validators.pt.xlf b/Resources/translations/validators.pt.xlf index 62c3c5fc2..6f628c740 100644 --- a/Resources/translations/validators.pt.xlf +++ b/Resources/translations/validators.pt.xlf @@ -472,87 +472,87 @@ This file is not a valid video. - Este ficheiro não é um vídeo válido. + Este ficheiro não é um vídeo válido. The size of the video could not be detected. - Não foi possível detetar o tamanho do vídeo. + Não foi possível detetar o tamanho do vídeo. The video width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px. - A largura do vídeo é demasiado grande ({{ width }}px). A largura máxima permitida é {{ max_width }}px. + A largura do vídeo é demasiado grande ({{ width }}px). A largura máxima permitida é {{ max_width }}px. The video width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. - A largura do vídeo é muito pequena ({{ width }}px). A largura mínima esperada é {{ min_width }}px. + A largura do vídeo é demasiado pequena ({{ width }}px). A largura mínima esperada é {{ min_width }}px. The video height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. - A altura do vídeo é demasiado grande ({{ height }}px). A altura máxima permitida é {{ max_height }}px. + A altura do vídeo é demasiado grande ({{ height }}px). A altura máxima permitida é {{ max_height }}px. The video height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. - A altura do vídeo é muito pequena ({{ height }}px). A altura mínima esperada é {{ min_height }}px. + A altura do vídeo é demasiado pequena ({{ height }}px). A altura mínima esperada é {{ min_height }}px. The video has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels. - O vídeo tem poucos píxeis ({{ pixels }}). A quantidade mínima esperada é {{ min_pixels }}. + O vídeo tem poucos píxeis ({{ pixels }}). A quantidade mínima esperada é {{ min_pixels }}. The video has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels. - O vídeo tem píxeis a mais ({{ pixels }}). A quantidade máxima esperada é {{ max_pixels }}. + O vídeo tem píxeis a mais ({{ pixels }}). A quantidade máxima esperada é {{ max_pixels }}. The video ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - A proporção do vídeo é muito grande ({{ ratio }}). A proporção máxima permitida é {{ max_ratio }}. + A proporção do vídeo é demasiado grande ({{ ratio }}). A proporção máxima permitida é {{ max_ratio }}. The video ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - A proporção do vídeo é muito pequena ({{ ratio }}). A proporção mínima esperada é {{ min_ratio }}. + A proporção do vídeo é demasiado pequena ({{ ratio }}). A proporção mínima esperada é {{ min_ratio }}. The video is square ({{ width }}x{{ height }}px). Square videos are not allowed. - O vídeo é quadrado ({{ width }}x{{ height }}px). Vídeos quadrados não são permitidos. + O vídeo é quadrado ({{ width }}x{{ height }}px). Vídeos quadrados não são permitidos. The video is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented videos are not allowed. - O vídeo está em modo paisagem ({{ width }}x{{ height }} px). Vídeos em paisagem não são permitidos. + O vídeo está em orientação horizontal ({{ width }}x{{ height }}px). Vídeos em orientação horizontal não são permitidos. The video is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented videos are not allowed. - O vídeo está em orientação vertical ({{ width }}x{{ height }}px). Vídeos em orientação vertical não são permitidos. + O vídeo está em orientação vertical ({{ width }}x{{ height }}px). Vídeos em orientação vertical não são permitidos. The video file is corrupted. - O ficheiro de vídeo está corrompido. + O ficheiro de vídeo está corrompido. The video contains multiple streams. Only one stream is allowed. - O vídeo contém vários fluxos. É permitido apenas um fluxo. + O vídeo contém vários fluxos. Apenas é permitido um fluxo. Unsupported video codec "{{ codec }}". - Codec de vídeo não suportado «{{ codec }}». + Codec de vídeo não suportado «{{ codec }}». Unsupported video container "{{ container }}". - Contentor de vídeo não suportado "{{ container }}". + Contentor de vídeo não suportado «{{ container }}». The image file is corrupted. - O ficheiro de imagem está corrompido. + O ficheiro de imagem está corrompido. The image has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels. - A imagem tem píxeis a menos ({{ pixels }}). A quantidade mínima esperada é {{ min_pixels }}. + A imagem tem píxeis a menos ({{ pixels }}). A quantidade mínima esperada é {{ min_pixels }}. The image has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels. - A imagem tem píxeis a mais ({{ pixels }}). A quantidade máxima esperada é {{ max_pixels }}. + A imagem tem píxeis a mais ({{ pixels }}). A quantidade máxima esperada é {{ max_pixels }}. This filename does not match the expected charset. - Este nome de ficheiro não corresponde ao conjunto de caracteres esperado. + Este nome de ficheiro não corresponde ao conjunto de caracteres esperado. diff --git a/Test/ConstraintValidatorTestCase.php b/Test/ConstraintValidatorTestCase.php index 2fe70eaef..d112f86f7 100644 --- a/Test/ConstraintValidatorTestCase.php +++ b/Test/ConstraintValidatorTestCase.php @@ -97,7 +97,7 @@ protected function tearDown(): void { $this->restoreDefaultTimezone(); - if (class_exists(\Locale::class)) { + if (class_exists(\Locale::class) && isset($this->defaultLocale)) { \Locale::setDefault($this->defaultLocale); } } diff --git a/Tests/Command/DebugCommandTest.php b/Tests/Command/DebugCommandTest.php index 660905eba..35f74a0c0 100644 --- a/Tests/Command/DebugCommandTest.php +++ b/Tests/Command/DebugCommandTest.php @@ -33,50 +33,50 @@ public function testOutputWithClassArgument() $this->assertSame(<< "1 + 1 = 2", | -| | | | "message" => "This value is not valid.", | -| | | | "negate" => true, | -| | | | "payload" => null, | -| | | | "values" => [] | -| | | | ] | -| code | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -| code | Symfony\Component\Validator\Constraints\NotBlank | Default, DummyClassOne | [ | -| | | | "allowNull" => false, | -| | | | "message" => "This value should not be blank.", | -| | | | "normalizer" => null, | -| | | | "payload" => null | -| | | | ] | -| email | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -| email | Symfony\Component\Validator\Constraints\Email | Default, DummyClassOne | [ | -| | | | "message" => "This value is not a valid email address.", | -| | | | "mode" => null, | -| | | | "normalizer" => null, | -| | | | "payload" => null | -| | | | ] | -| dummyClassTwo | property options | | [ | -| | | | "cascadeStrategy" => "Cascade", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "Implicit" | -| | | | ] | -+---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ - -TXT - , $tester->getDisplay(true) + Symfony\Component\Validator\Tests\Dummy\DummyClassOne + ----------------------------------------------------- + + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + | Property | Name | Groups | Options | + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + | - | Symfony\Component\Validator\Constraints\Expression | Default, DummyClassOne | [ | + | | | | "expression" => "1 + 1 = 2", | + | | | | "message" => "This value is not valid.", | + | | | | "negate" => true, | + | | | | "payload" => null, | + | | | | "values" => [] | + | | | | ] | + | code | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + | code | Symfony\Component\Validator\Constraints\NotBlank | Default, DummyClassOne | [ | + | | | | "allowNull" => false, | + | | | | "message" => "This value should not be blank.", | + | | | | "normalizer" => null, | + | | | | "payload" => null | + | | | | ] | + | email | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + | email | Symfony\Component\Validator\Constraints\Email | Default, DummyClassOne | [ | + | | | | "message" => "This value is not a valid email address.", | + | | | | "mode" => null, | + | | | | "normalizer" => null, | + | | | | "payload" => null | + | | | | ] | + | dummyClassTwo | property options | | [ | + | | | | "cascadeStrategy" => "Cascade", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "Implicit" | + | | | | ] | + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + + TXT, + $tester->getDisplay(true) ); } @@ -89,92 +89,92 @@ public function testOutputWithPathArgument() $this->assertSame(<< "1 + 1 = 2", | -| | | | "message" => "This value is not valid.", | -| | | | "negate" => true, | -| | | | "payload" => null, | -| | | | "values" => [] | -| | | | ] | -| code | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -| code | Symfony\Component\Validator\Constraints\NotBlank | Default, DummyClassOne | [ | -| | | | "allowNull" => false, | -| | | | "message" => "This value should not be blank.", | -| | | | "normalizer" => null, | -| | | | "payload" => null | -| | | | ] | -| email | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -| email | Symfony\Component\Validator\Constraints\Email | Default, DummyClassOne | [ | -| | | | "message" => "This value is not a valid email address.", | -| | | | "mode" => null, | -| | | | "normalizer" => null, | -| | | | "payload" => null | -| | | | ] | -| dummyClassTwo | property options | | [ | -| | | | "cascadeStrategy" => "Cascade", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "Implicit" | -| | | | ] | -+---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ - -Symfony\Component\Validator\Tests\Dummy\DummyClassTwo ------------------------------------------------------ - -+---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ -| Property | Name | Groups | Options | -+---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ -| - | Symfony\Component\Validator\Constraints\Expression | Default, DummyClassTwo | [ | -| | | | "expression" => "1 + 1 = 2", | -| | | | "message" => "This value is not valid.", | -| | | | "negate" => true, | -| | | | "payload" => null, | -| | | | "values" => [] | -| | | | ] | -| code | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -| code | Symfony\Component\Validator\Constraints\NotBlank | Default, DummyClassTwo | [ | -| | | | "allowNull" => false, | -| | | | "message" => "This value should not be blank.", | -| | | | "normalizer" => null, | -| | | | "payload" => null | -| | | | ] | -| email | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "None", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -| email | Symfony\Component\Validator\Constraints\Email | Default, DummyClassTwo | [ | -| | | | "message" => "This value is not a valid email address.", | -| | | | "mode" => null, | -| | | | "normalizer" => null, | -| | | | "payload" => null | -| | | | ] | -| dummyClassOne | property options | | [ | -| | | | "cascadeStrategy" => "None", | -| | | | "autoMappingStrategy" => "Disabled", | -| | | | "traversalStrategy" => "None" | -| | | | ] | -+---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ - -TXT - , $tester->getDisplay(true) + Symfony\Component\Validator\Tests\Dummy\DummyClassOne + ----------------------------------------------------- + + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + | Property | Name | Groups | Options | + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + | - | Symfony\Component\Validator\Constraints\Expression | Default, DummyClassOne | [ | + | | | | "expression" => "1 + 1 = 2", | + | | | | "message" => "This value is not valid.", | + | | | | "negate" => true, | + | | | | "payload" => null, | + | | | | "values" => [] | + | | | | ] | + | code | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + | code | Symfony\Component\Validator\Constraints\NotBlank | Default, DummyClassOne | [ | + | | | | "allowNull" => false, | + | | | | "message" => "This value should not be blank.", | + | | | | "normalizer" => null, | + | | | | "payload" => null | + | | | | ] | + | email | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + | email | Symfony\Component\Validator\Constraints\Email | Default, DummyClassOne | [ | + | | | | "message" => "This value is not a valid email address.", | + | | | | "mode" => null, | + | | | | "normalizer" => null, | + | | | | "payload" => null | + | | | | ] | + | dummyClassTwo | property options | | [ | + | | | | "cascadeStrategy" => "Cascade", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "Implicit" | + | | | | ] | + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + + Symfony\Component\Validator\Tests\Dummy\DummyClassTwo + ----------------------------------------------------- + + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + | Property | Name | Groups | Options | + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + | - | Symfony\Component\Validator\Constraints\Expression | Default, DummyClassTwo | [ | + | | | | "expression" => "1 + 1 = 2", | + | | | | "message" => "This value is not valid.", | + | | | | "negate" => true, | + | | | | "payload" => null, | + | | | | "values" => [] | + | | | | ] | + | code | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + | code | Symfony\Component\Validator\Constraints\NotBlank | Default, DummyClassTwo | [ | + | | | | "allowNull" => false, | + | | | | "message" => "This value should not be blank.", | + | | | | "normalizer" => null, | + | | | | "payload" => null | + | | | | ] | + | email | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "None", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + | email | Symfony\Component\Validator\Constraints\Email | Default, DummyClassTwo | [ | + | | | | "message" => "This value is not a valid email address.", | + | | | | "mode" => null, | + | | | | "normalizer" => null, | + | | | | "payload" => null | + | | | | ] | + | dummyClassOne | property options | | [ | + | | | | "cascadeStrategy" => "None", | + | | | | "autoMappingStrategy" => "Disabled", | + | | | | "traversalStrategy" => "None" | + | | | | ] | + +---------------+----------------------------------------------------+------------------------+------------------------------------------------------------+ + + TXT, + $tester->getDisplay(true) ); } @@ -188,9 +188,9 @@ public function testOutputWithInvalidClassArgument() $tester->execute(['class' => 'App\\NotFoundResource'], ['decorated' => false]); $this->assertStringContainsString(<<getDisplay(true) + Neither class nor path were found with "App\NotFoundResource" argument. + TXT, + $tester->getDisplay(true) ); } } diff --git a/Tests/ConstraintTest.php b/Tests/ConstraintTest.php index 80e33c7b7..a9398efd9 100644 --- a/Tests/ConstraintTest.php +++ b/Tests/ConstraintTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -25,12 +27,15 @@ use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithTypedProperty; use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithValue; use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithValueAsDefault; +use Symfony\Component\Validator\Tests\Fixtures\LegacyConstraintA; class ConstraintTest extends TestCase { + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetProperties() { - $constraint = new ConstraintA([ + $constraint = new LegacyConstraintA([ 'property1' => 'foo', 'property2' => 'bar', ]); @@ -39,24 +44,30 @@ public function testSetProperties() $this->assertEquals('bar', $constraint->property2); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetNotExistingPropertyThrowsException() { $this->expectException(InvalidOptionsException::class); - new ConstraintA([ + new LegacyConstraintA([ 'foo' => 'bar', ]); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testMagicPropertiesAreNotAllowed() { - $constraint = new ConstraintA(); + $constraint = new LegacyConstraintA(); $this->expectException(InvalidOptionsException::class); $constraint->foo = 'bar'; } + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidAndRequiredOptionsPassed() { $this->expectException(InvalidOptionsException::class); @@ -67,28 +78,36 @@ public function testInvalidAndRequiredOptionsPassed() ]); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetDefaultProperty() { - $constraint = new ConstraintA('foo'); + $constraint = new LegacyConstraintA('foo'); $this->assertEquals('foo', $constraint->property2); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetDefaultPropertyDoctrineStyle() { - $constraint = new ConstraintA(['value' => 'foo']); + $constraint = new LegacyConstraintA(['value' => 'foo']); $this->assertEquals('foo', $constraint->property2); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetDefaultPropertyDoctrineStylePlusOtherProperty() { - $constraint = new ConstraintA(['value' => 'foo', 'property1' => 'bar']); + $constraint = new LegacyConstraintA(['value' => 'foo', 'property1' => 'bar']); $this->assertEquals('foo', $constraint->property2); $this->assertEquals('bar', $constraint->property1); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetDefaultPropertyDoctrineStyleWhenDefaultPropertyIsNamedValue() { $constraint = new ConstraintWithValueAsDefault(['value' => 'foo']); @@ -97,6 +116,8 @@ public function testSetDefaultPropertyDoctrineStyleWhenDefaultPropertyIsNamedVal $this->assertNull($constraint->property); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testDontSetDefaultPropertyIfValuePropertyExists() { $constraint = new ConstraintWithValue(['value' => 'foo']); @@ -105,6 +126,8 @@ public function testDontSetDefaultPropertyIfValuePropertyExists() $this->assertNull($constraint->property); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetUndefinedDefaultProperty() { $this->expectException(ConstraintDefinitionException::class); @@ -112,6 +135,8 @@ public function testSetUndefinedDefaultProperty() new ConstraintB('foo'); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testRequiredOptionsMustBeDefined() { $this->expectException(MissingOptionsException::class); @@ -119,6 +144,8 @@ public function testRequiredOptionsMustBeDefined() new ConstraintC(); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testRequiredOptionsPassed() { $constraint = new ConstraintC(['option1' => 'default']); @@ -126,26 +153,32 @@ public function testRequiredOptionsPassed() $this->assertSame('default', $constraint->option1); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testGroupsAreConvertedToArray() { - $constraint = new ConstraintA(['groups' => 'Foo']); + $constraint = new LegacyConstraintA(['groups' => 'Foo']); $this->assertEquals(['Foo'], $constraint->groups); } public function testAddDefaultGroupAddsGroup() { - $constraint = new ConstraintA(['groups' => 'Default']); + $constraint = new ConstraintA(null, null, ['Default']); $constraint->addImplicitGroupName('Foo'); $this->assertEquals(['Default', 'Foo'], $constraint->groups); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testAllowsSettingZeroRequiredPropertyValue() { - $constraint = new ConstraintA(0); + $constraint = new LegacyConstraintA(0); $this->assertEquals(0, $constraint->property2); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testCanCreateConstraintWithNoDefaultOptionAndEmptyArray() { $constraint = new ConstraintB([]); @@ -169,7 +202,18 @@ public function testGetTargetsCanBeArray() public function testSerialize() { - $constraint = new ConstraintA([ + $constraint = new ConstraintA('foo', 'bar'); + + $restoredConstraint = unserialize(serialize($constraint)); + + $this->assertEquals($constraint, $restoredConstraint); + } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testSerializeDoctrineStyle() + { + $constraint = new LegacyConstraintA([ 'property1' => 'foo', 'property2' => 'bar', ]); @@ -181,14 +225,27 @@ public function testSerialize() public function testSerializeInitializesGroupsOptionToDefault() { - $constraint = new ConstraintA([ + $constraint = new ConstraintA('foo', 'bar'); + + $constraint = unserialize(serialize($constraint)); + + $expected = new ConstraintA('foo', 'bar', ['Default']); + + $this->assertEquals($expected, $constraint); + } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testSerializeInitializesGroupsOptionToDefaultDoctrineStyle() + { + $constraint = new LegacyConstraintA([ 'property1' => 'foo', 'property2' => 'bar', ]); $constraint = unserialize(serialize($constraint)); - $expected = new ConstraintA([ + $expected = new LegacyConstraintA([ 'property1' => 'foo', 'property2' => 'bar', 'groups' => 'Default', @@ -199,7 +256,18 @@ public function testSerializeInitializesGroupsOptionToDefault() public function testSerializeKeepsCustomGroups() { - $constraint = new ConstraintA([ + $constraint = new ConstraintA('foo', 'bar', ['MyGroup']); + + $constraint = unserialize(serialize($constraint)); + + $this->assertSame(['MyGroup'], $constraint->groups); + } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testSerializeKeepsCustomGroupsDoctrineStyle() + { + $constraint = new LegacyConstraintA([ 'property1' => 'foo', 'property2' => 'bar', 'groups' => 'MyGroup', @@ -216,35 +284,43 @@ public function testGetErrorNameForUnknownCode() Constraint::getErrorName(1); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testOptionsAsDefaultOption() { - $constraint = new ConstraintA($options = ['value1']); + $constraint = new LegacyConstraintA($options = ['value1']); $this->assertEquals($options, $constraint->property2); - $constraint = new ConstraintA($options = ['value1', 'property1' => 'value2']); + $constraint = new LegacyConstraintA($options = ['value1', 'property1' => 'value2']); $this->assertEquals($options, $constraint->property2); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidOptions() { $this->expectException(InvalidOptionsException::class); - $this->expectExceptionMessage('The options "0", "5" do not exist in constraint "Symfony\Component\Validator\Tests\Fixtures\ConstraintA".'); - new ConstraintA(['property2' => 'foo', 'bar', 5 => 'baz']); + $this->expectExceptionMessage('The options "0", "5" do not exist in constraint "Symfony\Component\Validator\Tests\Fixtures\LegacyConstraintA".'); + new LegacyConstraintA(['property2' => 'foo', 'bar', 5 => 'baz']); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testOptionsWithInvalidInternalPointer() { $options = ['property1' => 'foo']; next($options); next($options); - $constraint = new ConstraintA($options); + $constraint = new LegacyConstraintA($options); $this->assertEquals('foo', $constraint->property1); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testAttributeSetUndefinedDefaultOption() { $this->expectException(ConstraintDefinitionException::class); @@ -252,6 +328,8 @@ public function testAttributeSetUndefinedDefaultOption() new ConstraintB(['value' => 1]); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testStaticPropertiesAreNoOptions() { $this->expectException(InvalidOptionsException::class); @@ -261,6 +339,8 @@ public function testStaticPropertiesAreNoOptions() ]); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetTypedProperty() { $constraint = new ConstraintWithTypedProperty([ diff --git a/Tests/ConstraintValidatorTest.php b/Tests/ConstraintValidatorTest.php index d378ba292..3060bd23d 100644 --- a/Tests/ConstraintValidatorTest.php +++ b/Tests/ConstraintValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -20,9 +21,7 @@ class ConstraintValidatorTest extends TestCase { use IcuCompatibilityTrait; - /** - * @dataProvider formatValueProvider - */ + #[DataProvider('formatValueProvider')] public function testFormatValue(string $expected, mixed $value, int $format = 0) { \Locale::setDefault('en'); diff --git a/Tests/ConstraintViolationListTest.php b/Tests/ConstraintViolationListTest.php index 0d22bd85e..bc9472510 100644 --- a/Tests/ConstraintViolationListTest.php +++ b/Tests/ConstraintViolationListTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; @@ -107,25 +108,23 @@ public function testToString() ]); $expected = <<<'EOF' -Root: - Error 1 -Root.foo.bar: - Error 2 -Root[baz]: - Error 3 -foo.bar: - Error 4 -[baz]: - Error 5 - -EOF; + Root: + Error 1 + Root.foo.bar: + Error 2 + Root[baz]: + Error 3 + foo.bar: + Error 4 + [baz]: + Error 5 + + EOF; $this->assertEquals($expected, (string) $this->list); } - /** - * @dataProvider findByCodesProvider - */ + #[DataProvider('findByCodesProvider')] public function testFindByCodes($code, $violationsCount) { $violations = [ diff --git a/Tests/ConstraintViolationTest.php b/Tests/ConstraintViolationTest.php index dbac96a8a..2df314885 100644 --- a/Tests/ConstraintViolationTest.php +++ b/Tests/ConstraintViolationTest.php @@ -30,9 +30,9 @@ public function testToStringHandlesArrays() ); $expected = <<<'EOF' -Root.property.path: - Array -EOF; + Root.property.path: + Array + EOF; $this->assertSame($expected, (string) $violation); } @@ -49,9 +49,9 @@ public function testToStringHandlesArrayRoots() ); $expected = <<<'EOF' -Array.some_value: - 42 cannot be used here -EOF; + Array.some_value: + 42 cannot be used here + EOF; $this->assertSame($expected, (string) $violation); } @@ -70,9 +70,9 @@ public function testToStringHandlesCodes() ); $expected = <<<'EOF' -Array.some_value: - 42 cannot be used here (code 0) -EOF; + Array.some_value: + 42 cannot be used here (code 0) + EOF; $this->assertSame($expected, (string) $violation); } @@ -80,9 +80,9 @@ public function testToStringHandlesCodes() public function testToStringOmitsEmptyCodes() { $expected = <<<'EOF' -Array.some_value: - 42 cannot be used here -EOF; + Array.some_value: + 42 cannot be used here + EOF; $violation = new ConstraintViolation( '42 cannot be used here', @@ -124,9 +124,9 @@ public function testMessageCanBeStringableObject() ); $expected = <<<'EOF' -Root.property.path: - toString -EOF; + Root.property.path: + toString + EOF; $this->assertSame($expected, (string) $violation); $this->assertSame($message, $violation->getMessage()); } diff --git a/Tests/Constraints/AbstractComparisonTest.php b/Tests/Constraints/AbstractComparisonTest.php new file mode 100644 index 000000000..ef65f961e --- /dev/null +++ b/Tests/Constraints/AbstractComparisonTest.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\Constraints; + +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\AbstractComparison; + +class AbstractComparisonTest extends TestCase +{ + #[IgnoreDeprecations] + #[Group('legacy')] + public function testConstructorWithArrayOption() + { + $comparison = new class(['value' => 42, 'message' => 'my error']) extends AbstractComparison {}; + + $this->assertSame(42, $comparison->value); + $this->assertSame('my error', $comparison->message); + } +} diff --git a/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/Tests/Constraints/AbstractComparisonValidatorTestCase.php index 25fed976c..883db201c 100644 --- a/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -75,9 +76,7 @@ public static function provideInvalidConstraintOptions() ]; } - /** - * @dataProvider provideInvalidConstraintOptions - */ + #[DataProvider('provideInvalidConstraintOptions')] public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { $this->expectException(ConstraintDefinitionException::class); @@ -95,9 +94,7 @@ public function testThrowsConstraintExceptionIfBothValueAndPropertyPath() ]); } - /** - * @dataProvider provideValidComparisonsToPropertyPath - */ + #[DataProvider('provideValidComparisonsToPropertyPath')] public function testValidComparisonToPropertyPath($comparedValue) { $constraint = $this->createConstraint(['propertyPath' => 'value']); diff --git a/Tests/Constraints/AllValidatorTest.php b/Tests/Constraints/AllValidatorTest.php index ee6a29174..9c70c935c 100644 --- a/Tests/Constraints/AllValidatorTest.php +++ b/Tests/Constraints/AllValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\AllValidator; use Symfony\Component\Validator\Constraints\NotNull; @@ -38,9 +39,7 @@ public function testThrowsExceptionIfNotTraversable() $this->validator->validate('foo.barbar', new All(new Range(min: 4))); } - /** - * @dataProvider getValidArguments - */ + #[DataProvider('getValidArguments')] public function testWalkSingleConstraint($array) { $constraint = new Range(min: 4); @@ -56,9 +55,7 @@ public function testWalkSingleConstraint($array) $this->assertNoViolation(); } - /** - * @dataProvider getValidArguments - */ + #[DataProvider('getValidArguments')] public function testWalkMultipleConstraints($array) { $constraint1 = new Range(min: 4); diff --git a/Tests/Constraints/AtLeastOneOfValidatorTest.php b/Tests/Constraints/AtLeastOneOfValidatorTest.php index 22b53dd13..ff2d04dc2 100644 --- a/Tests/Constraints/AtLeastOneOfValidatorTest.php +++ b/Tests/Constraints/AtLeastOneOfValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\AtLeastOneOf; use Symfony\Component\Validator\Constraints\AtLeastOneOfValidator; use Symfony\Component\Validator\Constraints\Choice; @@ -54,9 +55,7 @@ protected function createValidator(): AtLeastOneOfValidator return new AtLeastOneOfValidator(); } - /** - * @dataProvider getValidCombinations - */ + #[DataProvider('getValidCombinations')] public function testValidCombinations($value, $constraints) { $this->assertCount(0, Validation::createValidator()->validate($value, new AtLeastOneOf($constraints))); @@ -96,9 +95,7 @@ public static function getValidCombinations() ]; } - /** - * @dataProvider getInvalidCombinations - */ + #[DataProvider('getInvalidCombinations')] public function testInvalidCombinationsWithDefaultMessage($value, $constraints) { $atLeastOneOf = new AtLeastOneOf(constraints: $constraints); @@ -118,9 +115,7 @@ public function testInvalidCombinationsWithDefaultMessage($value, $constraints) $this->assertEquals(new ConstraintViolation(implode('', $message), implode('', $message), [], $value, '', $value, null, AtLeastOneOf::AT_LEAST_ONE_OF_ERROR, $atLeastOneOf), $violations->get(0)); } - /** - * @dataProvider getInvalidCombinations - */ + #[DataProvider('getInvalidCombinations')] public function testInvalidCombinationsWithCustomMessage($value, $constraints) { $atLeastOneOf = new AtLeastOneOf( @@ -317,7 +312,7 @@ public function testValidateNestedAtLeaseOneOfConstraints() new Collection([ 'bar' => new AtLeastOneOf([ new Type('int'), - new Choice(['test1', 'test2']), + new Choice(choices: ['test1', 'test2']), ]), ]), new Collection([ diff --git a/Tests/Constraints/BicValidatorTest.php b/Tests/Constraints/BicValidatorTest.php index 315cb859e..2bb14a327 100644 --- a/Tests/Constraints/BicValidatorTest.php +++ b/Tests/Constraints/BicValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Bic; use Symfony\Component\Validator\Constraints\BicValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -78,7 +79,7 @@ public function testInvalidComparisonToPropertyPathFromAttribute() $classMetadata = new ClassMetadata(BicDummy::class); (new AttributeLoader())->loadClassMetadata($classMetadata); - [$constraint] = $classMetadata->properties['bic1']->constraints; + [$constraint] = $classMetadata->getPropertyMetadata('bic1')[0]->getConstraints(); $this->setObject(new BicDummy()); @@ -129,7 +130,7 @@ public function testInvalidComparisonToValueFromAttribute() $classMetadata = new ClassMetadata(BicDummy::class); (new AttributeLoader())->loadClassMetadata($classMetadata); - [$constraint] = $classMetadata->properties['bic1']->constraints; + [$constraint] = $classMetadata->getPropertyMetadata('bic1')[0]->getConstraints(); $this->validator->validate('UNCRIT2B912', $constraint); @@ -189,9 +190,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Bic()); } - /** - * @dataProvider getValidBics - */ + #[DataProvider('getValidBics')] public function testValidBics($bic) { $this->validator->validate($bic, new Bic()); @@ -212,9 +211,7 @@ public static function getValidBics() ]; } - /** - * @dataProvider getInvalidBics - */ + #[DataProvider('getInvalidBics')] public function testInvalidBics($bic, $code) { $constraint = new Bic( @@ -229,9 +226,7 @@ public function testInvalidBics($bic, $code) ->assertRaised(); } - /** - * @dataProvider getInvalidBics - */ + #[DataProvider('getInvalidBics')] public function testInvalidBicsNamed($bic, $code) { $constraint = new Bic(message: 'myMessage'); @@ -270,11 +265,10 @@ public static function getInvalidBics() } /** - * @dataProvider getValidBicSpecialCases - * * Some territories have their own ISO country code but can use another country code * for IBAN accounts. Example: "French Guiana" (country code "GF") can use FR too. */ + #[DataProvider('getValidBicSpecialCases')] public function testValidBicSpecialCases(string $bic, string $iban) { $constraint = new Bic(iban: $iban); @@ -313,9 +307,7 @@ public static function getValidBicSpecialCases() yield ['CAIXEABBXXX', 'ES79 2100 0813 6101 2345 6789']; } - /** - * @dataProvider getValidBicsWithNormalizerToUpper - */ + #[DataProvider('getValidBicsWithNormalizerToUpper')] public function testValidBicsWithNormalizerToUpper($bic) { $this->validator->validate($bic, new Bic(mode: Bic::VALIDATION_MODE_CASE_INSENSITIVE)); diff --git a/Tests/Constraints/BlankValidatorTest.php b/Tests/Constraints/BlankValidatorTest.php index 21d3fc83e..62ee839a5 100644 --- a/Tests/Constraints/BlankValidatorTest.php +++ b/Tests/Constraints/BlankValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Blank; use Symfony\Component\Validator\Constraints\BlankValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -36,9 +37,7 @@ public function testBlankIsValid() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValues($value, $valueAsString) { $constraint = new Blank( diff --git a/Tests/Constraints/CallbackTest.php b/Tests/Constraints/CallbackTest.php new file mode 100644 index 000000000..d7c758c17 --- /dev/null +++ b/Tests/Constraints/CallbackTest.php @@ -0,0 +1,29 @@ + + * + * 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\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Callback; + +class CallbackTest extends TestCase +{ + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new Callback(['callback' => 'validate']); + + $this->assertSame('validate', $constraint->callback); + } +} diff --git a/Tests/Constraints/CardSchemeTest.php b/Tests/Constraints/CardSchemeTest.php index a50930b9b..6c59556af 100644 --- a/Tests/Constraints/CardSchemeTest.php +++ b/Tests/Constraints/CardSchemeTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\CardScheme; use Symfony\Component\Validator\Exception\MissingOptionsException; @@ -25,15 +27,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame([CardScheme::MASTERCARD, CardScheme::VISA], $aConstraint->schemes); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame([CardScheme::AMEX], $bConstraint->schemes); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'CardSchemeDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame([CardScheme::DINERS], $cConstraint->schemes); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); @@ -46,6 +48,15 @@ public function testMissingSchemes() new CardScheme(null); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testSchemesInOptionsArray() + { + $constraint = new CardScheme(null, options: ['schemes' => [CardScheme::MASTERCARD]]); + + $this->assertSame([CardScheme::MASTERCARD], $constraint->schemes); + } } class CardSchemeDummy diff --git a/Tests/Constraints/CardSchemeValidatorTest.php b/Tests/Constraints/CardSchemeValidatorTest.php index d70457833..0426b557a 100644 --- a/Tests/Constraints/CardSchemeValidatorTest.php +++ b/Tests/Constraints/CardSchemeValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\CardScheme; use Symfony\Component\Validator\Constraints\CardSchemeValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -36,9 +37,7 @@ public function testEmptyStringIsValid() $this->assertNoViolation(); } - /** - * @dataProvider getValidNumbers - */ + #[DataProvider('getValidNumbers')] public function testValidNumbers($scheme, $number) { $this->validator->validate($number, new CardScheme(schemes: $scheme)); @@ -46,9 +45,7 @@ public function testValidNumbers($scheme, $number) $this->assertNoViolation(); } - /** - * @dataProvider getValidNumbers - */ + #[DataProvider('getValidNumbers')] public function testValidNumbersWithNewLine($scheme, $number) { $this->validator->validate($number."\n", new CardScheme(schemes: $scheme, message: 'myMessage')); @@ -69,9 +66,7 @@ public function testValidNumberWithOrderedArguments() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidNumbers - */ + #[DataProvider('getInvalidNumbers')] public function testInvalidNumbers($scheme, $number, $code) { $constraint = new CardScheme( diff --git a/Tests/Constraints/CascadeTest.php b/Tests/Constraints/CascadeTest.php index fc4d7ce0f..e33add926 100644 --- a/Tests/Constraints/CascadeTest.php +++ b/Tests/Constraints/CascadeTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Cascade; use Symfony\Component\Validator\Mapping\CascadingStrategy; @@ -35,9 +37,8 @@ public function testExcludeProperties() self::assertSame(['foo' => 0, 'bar' => 1], $constraint->exclude); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testExcludePropertiesDoctrineStyle() { $constraint = new Cascade(['exclude' => ['foo', 'bar']]); diff --git a/Tests/Constraints/CharsetTest.php b/Tests/Constraints/CharsetTest.php index 1b23a2ea7..4091c5ec3 100644 --- a/Tests/Constraints/CharsetTest.php +++ b/Tests/Constraints/CharsetTest.php @@ -47,10 +47,10 @@ public function testAttributes() $loader = new AttributeLoader(); $this->assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); $this->assertSame('UTF-8', $aConstraint->encodings); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); $this->assertSame(['ASCII', 'UTF-8'], $bConstraint->encodings); } } diff --git a/Tests/Constraints/CharsetValidatorTest.php b/Tests/Constraints/CharsetValidatorTest.php index 5c2f2c884..f9c782ea6 100644 --- a/Tests/Constraints/CharsetValidatorTest.php +++ b/Tests/Constraints/CharsetValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Charset; use Symfony\Component\Validator\Constraints\CharsetValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -24,9 +25,7 @@ protected function createValidator(): CharsetValidator return new CharsetValidator(); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function testEncodingIsValid(string|\Stringable $value, array|string $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); @@ -34,9 +33,7 @@ public function testEncodingIsValid(string|\Stringable $value, array|string $enc $this->assertNoViolation(); } - /** - * @dataProvider provideInvalidValues - */ + #[DataProvider('provideInvalidValues')] public function testInvalidValues(string $value, array|string $encodings) { $this->validator->validate($value, new Charset(encodings: $encodings)); @@ -48,9 +45,7 @@ public function testInvalidValues(string $value, array|string $encodings) ->assertRaised(); } - /** - * @dataProvider provideInvalidTypes - */ + #[DataProvider('provideInvalidTypes')] public function testNonStringValues(mixed $value) { $this->expectException(UnexpectedValueException::class); diff --git a/Tests/Constraints/ChoiceTest.php b/Tests/Constraints/ChoiceTest.php index 9c58dd107..89adb5228 100644 --- a/Tests/Constraints/ChoiceTest.php +++ b/Tests/Constraints/ChoiceTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Choice; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -19,6 +21,8 @@ class ChoiceTest extends TestCase { + #[IgnoreDeprecations] + #[Group('legacy')] public function testSetDefaultPropertyChoice() { $constraint = new ConstraintChoiceWithPreset('A'); @@ -33,24 +37,24 @@ public function testAttributes() self::assertTrue($loader->loadClassMetadata($metadata)); /** @var Choice $aConstraint */ - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame([1, 2], $aConstraint->choices); self::assertSame(['Default', 'ChoiceDummy'], $aConstraint->groups); /** @var Choice $bConstraint */ - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(['foo', 'bar'], $bConstraint->choices); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'ChoiceDummy'], $bConstraint->groups); /** @var Choice $cConstraint */ - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame([1, 2], $aConstraint->choices); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); /** @var Choice $stringIndexedConstraint */ - [$stringIndexedConstraint] = $metadata->properties['stringIndexed']->getConstraints(); + [$stringIndexedConstraint] = $metadata->getPropertyMetadata('stringIndexed')[0]->getConstraints(); self::assertSame(['one' => 1, 'two' => 2], $stringIndexedConstraint->choices); } } @@ -63,7 +67,7 @@ class ChoiceDummy #[Choice(choices: ['foo', 'bar'], message: 'myMessage')] private $b; - #[Choice([1, 2], groups: ['my_group'], payload: 'some attached data')] + #[Choice(choices: [1, 2], groups: ['my_group'], payload: 'some attached data')] private $c; #[Choice(choices: ['one' => 1, 'two' => 2])] diff --git a/Tests/Constraints/ChoiceValidatorTest.php b/Tests/Constraints/ChoiceValidatorTest.php index 39affe442..a2935323f 100644 --- a/Tests/Constraints/ChoiceValidatorTest.php +++ b/Tests/Constraints/ChoiceValidatorTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\Choice; use Symfony\Component\Validator\Constraints\ChoiceValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -74,27 +77,25 @@ public function testValidCallbackExpected() $this->validator->validate('foobar', new Choice(callback: 'abcd')); } - /** - * @dataProvider provideConstraintsWithChoicesArray - */ - public function testValidChoiceArray(Choice $constraint) + public function testValidChoiceArray() { - $this->validator->validate('bar', $constraint); + $this->validator->validate('bar', new Choice(choices: ['foo', 'bar'])); $this->assertNoViolation(); } - public static function provideConstraintsWithChoicesArray(): iterable + #[IgnoreDeprecations] + #[Group('legacy')] + public function testValidChoiceArrayFirstArgument() { - yield 'first argument' => [new Choice(['foo', 'bar'])]; - yield 'named arguments' => [new Choice(choices: ['foo', 'bar'])]; + $this->validator->validate('bar', new Choice(['foo', 'bar'])); + + $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider provideLegacyConstraintsWithChoicesArrayDoctrineStyle - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('provideLegacyConstraintsWithChoicesArrayDoctrineStyle')] public function testValidChoiceArrayDoctrineStyle(Choice $constraint) { $this->validator->validate('bar', $constraint); @@ -108,9 +109,7 @@ public static function provideLegacyConstraintsWithChoicesArrayDoctrineStyle(): yield 'Doctrine default option' => [new Choice(['value' => ['foo', 'bar']])]; } - /** - * @dataProvider provideConstraintsWithCallbackFunction - */ + #[DataProvider('provideConstraintsWithCallbackFunction')] public function testValidChoiceCallbackFunction(Choice $constraint) { $this->validator->validate('bar', $constraint); @@ -125,11 +124,9 @@ public static function provideConstraintsWithCallbackFunction(): iterable yield 'named arguments, static method' => [new Choice(callback: [__CLASS__, 'staticCallback'])]; } - /** - * @group legacy - * - * @dataProvider provideLegacyConstraintsWithCallbackFunctionDoctrineStyle - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('provideLegacyConstraintsWithCallbackFunctionDoctrineStyle')] public function testValidChoiceCallbackFunctionDoctrineStyle(Choice $constraint) { $this->validator->validate('bar', $constraint); @@ -193,9 +190,8 @@ public function testMultipleChoices() $this->assertNoViolation(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testMultipleChoicesDoctrineStyle() { $this->validator->validate(['baz', 'bar'], new Choice([ @@ -217,9 +213,8 @@ public function testInvalidChoice() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidChoiceDoctrineStyle() { $this->validator->validate('baz', new Choice(['choices' => ['foo', 'bar'], 'message' => 'myMessage'])); @@ -265,9 +260,8 @@ public function testInvalidChoiceMultiple() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidChoiceMultipleDoctrineStyle() { $this->validator->validate(['foo', 'baz'], new Choice([ @@ -305,9 +299,8 @@ public function testTooFewChoices() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testTooFewChoicesDoctrineStyle() { $value = ['foo']; @@ -350,9 +343,8 @@ public function testTooManyChoices() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testTooManyChoicesDoctrineStyle() { $value = ['foo', 'bar', 'moo']; diff --git a/Tests/Constraints/CidrTest.php b/Tests/Constraints/CidrTest.php index 25059104d..a6dd780fb 100644 --- a/Tests/Constraints/CidrTest.php +++ b/Tests/Constraints/CidrTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Cidr; use Symfony\Component\Validator\Constraints\Ip; @@ -65,9 +66,7 @@ public function testWithInvalidVersion() new Cidr(version: '8'); } - /** - * @dataProvider getValidMinMaxValues - */ + #[DataProvider('getValidMinMaxValues')] public function testWithValidMinMaxValues(string $ipVersion, int $netmaskMin, int $netmaskMax) { $cidrConstraint = new Cidr( @@ -81,9 +80,7 @@ public function testWithValidMinMaxValues(string $ipVersion, int $netmaskMin, in self::assertEquals($netmaskMax, $cidrConstraint->netmaskMax); } - /** - * @dataProvider getInvalidMinMaxValues - */ + #[DataProvider('getInvalidMinMaxValues')] public function testWithInvalidMinMaxValues(string $ipVersion, int $netmaskMin, int $netmaskMax) { $expectedMax = Ip::V4 == $ipVersion ? 32 : 128; @@ -134,19 +131,19 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(Ip::ALL, $aConstraint->version); self::assertSame(0, $aConstraint->netmaskMin); self::assertSame(128, $aConstraint->netmaskMax); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(Ip::V6, $bConstraint->version); self::assertSame('myMessage', $bConstraint->message); self::assertSame(10, $bConstraint->netmaskMin); self::assertSame(126, $bConstraint->netmaskMax); self::assertSame(['Default', 'CidrDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/CidrValidatorTest.php b/Tests/Constraints/CidrValidatorTest.php index 6dfdc4931..91229d74f 100644 --- a/Tests/Constraints/CidrValidatorTest.php +++ b/Tests/Constraints/CidrValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Cidr; use Symfony\Component\Validator\Constraints\CidrValidator; use Symfony\Component\Validator\Constraints\Ip; @@ -55,9 +56,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(123456, new Cidr()); } - /** - * @dataProvider getWithInvalidNetmask - */ + #[DataProvider('getWithInvalidNetmask')] public function testInvalidNetmask(string $cidr) { $this->validator->validate($cidr, new Cidr()); @@ -68,9 +67,7 @@ public function testInvalidNetmask(string $cidr) ->assertRaised(); } - /** - * @dataProvider getWithInvalidIps - */ + #[DataProvider('getWithInvalidIps')] public function testInvalidIpValue(string $cidr) { $this->validator->validate($cidr, new Cidr()); @@ -81,9 +78,7 @@ public function testInvalidIpValue(string $cidr) ->assertRaised(); } - /** - * @dataProvider getValid - */ + #[DataProvider('getValid')] public function testValidCidr(string|\Stringable $cidr, string $version) { $this->validator->validate($cidr, new Cidr(version: $version)); @@ -91,9 +86,7 @@ public function testValidCidr(string|\Stringable $cidr, string $version) $this->assertNoViolation(); } - /** - * @dataProvider getWithInvalidMasksAndIps - */ + #[DataProvider('getWithInvalidMasksAndIps')] public function testInvalidIpAddressAndNetmask(string|\Stringable $cidr) { $this->validator->validate($cidr, new Cidr()); @@ -103,9 +96,7 @@ public function testInvalidIpAddressAndNetmask(string|\Stringable $cidr) ->assertRaised(); } - /** - * @dataProvider getOutOfRangeNetmask - */ + #[DataProvider('getOutOfRangeNetmask')] public function testOutOfRangeNetmask(string $cidr, int $maxExpected, ?string $version = null, ?int $min = null, ?int $max = null) { $cidrConstraint = new Cidr( @@ -123,9 +114,7 @@ public function testOutOfRangeNetmask(string $cidr, int $maxExpected, ?string $v ->assertRaised(); } - /** - * @dataProvider getWithWrongVersion - */ + #[DataProvider('getWithWrongVersion')] public function testWrongVersion(string $cidr, string $version) { $this->validator->validate($cidr, new Cidr(version: $version)); diff --git a/Tests/Constraints/CollectionTest.php b/Tests/Constraints/CollectionTest.php index c1c32f90a..994a75081 100644 --- a/Tests/Constraints/CollectionTest.php +++ b/Tests/Constraints/CollectionTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\Email; @@ -26,9 +29,8 @@ */ class CollectionTest extends TestCase { - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testRejectNonConstraints() { $this->expectException(InvalidOptionsException::class); @@ -103,9 +105,8 @@ public function testConstraintHasDefaultGroupWithOptionalValues() $this->assertEquals(['Default'], $constraint->fields['bar']->groups); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testOnlySomeKeysAreKnowOptions() { $constraint = new Collection([ @@ -166,10 +167,8 @@ public function testEmptyFieldsInOptions() $this->assertSame('foo bar baz', $constraint->extraFieldsMessage); } - /** - * @testWith [[]] - * [null] - */ + #[TestWith([[]])] + #[TestWith([null])] public function testEmptyConstraintListForField(?array $fieldConstraint) { $constraint = new Collection( @@ -189,10 +188,8 @@ public function testEmptyConstraintListForField(?array $fieldConstraint) $this->assertSame('foo bar baz', $constraint->extraFieldsMessage); } - /** - * @testWith [[]] - * [null] - */ + #[TestWith([[]])] + #[TestWith([null])] public function testEmptyConstraintListForFieldInOptions(?array $fieldConstraint) { $constraint = new Collection( diff --git a/Tests/Constraints/CompositeTest.php b/Tests/Constraints/CompositeTest.php index 9329ef1a2..f2670dded 100644 --- a/Tests/Constraints/CompositeTest.php +++ b/Tests/Constraints/CompositeTest.php @@ -24,9 +24,11 @@ class ConcreteComposite extends Composite { public array|Constraint $constraints = []; - public function __construct(mixed $options = null, public array|Constraint $otherNested = []) + public function __construct(array|Constraint $constraints = [], public array|Constraint $otherNested = [], ?array $groups = null) { - parent::__construct($options); + $this->constraints = $constraints; + + parent::__construct(null, $groups); } protected function getCompositeOption(): array @@ -89,16 +91,14 @@ public function testMergeNestedGroupsIfNoExplicitParentGroup() public function testSetImplicitNestedGroupsIfExplicitParentGroup() { - $constraint = new ConcreteComposite([ - 'constraints' => [ + $constraint = new ConcreteComposite( + [ new NotNull(), new NotBlank(), ], - 'otherNested' => [ - new Length(exactly: 10), - ], - 'groups' => ['Default', 'Strict'], - ]); + new Length(exactly: 10), + ['Default', 'Strict'], + ); $this->assertEquals(['Default', 'Strict'], $constraint->groups); $this->assertEquals(['Default', 'Strict'], $constraint->constraints[0]->groups); @@ -108,16 +108,14 @@ public function testSetImplicitNestedGroupsIfExplicitParentGroup() public function testExplicitNestedGroupsMustBeSubsetOfExplicitParentGroups() { - $constraint = new ConcreteComposite([ - 'constraints' => [ + $constraint = new ConcreteComposite( + [ new NotNull(groups: ['Default']), new NotBlank(groups: ['Strict']), ], - 'otherNested' => [ - new Length(exactly: 10, groups: ['Strict']), - ], - 'groups' => ['Default', 'Strict'], - ]); + new Length(exactly: 10, groups: ['Strict']), + ['Default', 'Strict'] + ); $this->assertEquals(['Default', 'Strict'], $constraint->groups); $this->assertEquals(['Default'], $constraint->constraints[0]->groups); @@ -128,26 +126,13 @@ public function testExplicitNestedGroupsMustBeSubsetOfExplicitParentGroups() public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroups() { $this->expectException(ConstraintDefinitionException::class); - new ConcreteComposite([ - 'constraints' => [ - new NotNull(groups: ['Default', 'Foobar']), - ], - 'groups' => ['Default', 'Strict'], - ]); + new ConcreteComposite(new NotNull(groups: ['Default', 'Foobar']), [], ['Default', 'Strict']); } public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroupsInOtherNested() { $this->expectException(ConstraintDefinitionException::class); - new ConcreteComposite([ - 'constraints' => [ - new NotNull(groups: ['Default']), - ], - 'otherNested' => [ - new NotNull(groups: ['Default', 'Foobar']), - ], - 'groups' => ['Default', 'Strict'], - ]); + new ConcreteComposite(new NotNull(groups: ['Default']), new NotNull(groups: ['Default', 'Foobar']), ['Default', 'Strict']); } public function testImplicitGroupNamesAreForwarded() diff --git a/Tests/Constraints/CompoundTest.php b/Tests/Constraints/CompoundTest.php index 9b515a48c..7c08d84b0 100644 --- a/Tests/Constraints/CompoundTest.php +++ b/Tests/Constraints/CompoundTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Compound; use Symfony\Component\Validator\Constraints\Length; @@ -19,9 +21,8 @@ class CompoundTest extends TestCase { - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testItCannotRedefineConstraintsOption() { $this->expectException(ConstraintDefinitionException::class); @@ -38,6 +39,8 @@ public function testGroupsAndPayload() $this->assertSame($payload, $compound->payload); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testGroupsAndPayloadInOptionsArray() { $payload = new \stdClass(); @@ -47,6 +50,8 @@ public function testGroupsAndPayloadInOptionsArray() $this->assertSame($payload, $compound->payload); } + #[IgnoreDeprecations] + #[Group('legacy')] public function testCanDependOnNormalizedOptions() { $constraint = new ForwardingOptionCompound($min = 3); diff --git a/Tests/Constraints/CountTest.php b/Tests/Constraints/CountTest.php index 42843e12d..0f59baf8c 100644 --- a/Tests/Constraints/CountTest.php +++ b/Tests/Constraints/CountTest.php @@ -24,12 +24,12 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(42, $aConstraint->min); self::assertSame(42, $aConstraint->max); self::assertNull($aConstraint->divisibleBy); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(1, $bConstraint->min); self::assertSame(4711, $bConstraint->max); self::assertNull($bConstraint->divisibleBy); @@ -37,7 +37,7 @@ public function testAttributes() self::assertSame('myMaxMessage', $bConstraint->maxMessage); self::assertSame(['Default', 'CountDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->min); self::assertNull($cConstraint->max); self::assertSame(10, $cConstraint->divisibleBy); diff --git a/Tests/Constraints/CountValidatorTestCase.php b/Tests/Constraints/CountValidatorTestCase.php index da319cd8b..93f91fd9b 100644 --- a/Tests/Constraints/CountValidatorTestCase.php +++ b/Tests/Constraints/CountValidatorTestCase.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\Count; use Symfony\Component\Validator\Constraints\CountValidator; use Symfony\Component\Validator\Constraints\DivisibleBy; @@ -69,11 +72,9 @@ public static function getFiveOrMoreElements() ]; } - /** - * @group legacy - * - * @dataProvider getThreeOrLessElements - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getThreeOrLessElements')] public function testValidValuesMax($value) { $constraint = new Count(['max' => 3]); @@ -82,9 +83,7 @@ public function testValidValuesMax($value) $this->assertNoViolation(); } - /** - * @dataProvider getThreeOrLessElements - */ + #[DataProvider('getThreeOrLessElements')] public function testValidValuesMaxNamed($value) { $constraint = new Count(max: 3); @@ -93,11 +92,9 @@ public function testValidValuesMaxNamed($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getFiveOrMoreElements - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getFiveOrMoreElements')] public function testValidValuesMin($value) { $constraint = new Count(['min' => 5]); @@ -106,9 +103,7 @@ public function testValidValuesMin($value) $this->assertNoViolation(); } - /** - * @dataProvider getFiveOrMoreElements - */ + #[DataProvider('getFiveOrMoreElements')] public function testValidValuesMinNamed($value) { $constraint = new Count(min: 5); @@ -117,11 +112,9 @@ public function testValidValuesMinNamed($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getFourElements - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getFourElements')] public function testValidValuesExact($value) { $constraint = new Count(4); @@ -130,9 +123,7 @@ public function testValidValuesExact($value) $this->assertNoViolation(); } - /** - * @dataProvider getFourElements - */ + #[DataProvider('getFourElements')] public function testValidValuesExactNamed($value) { $constraint = new Count(exactly: 4); @@ -141,11 +132,9 @@ public function testValidValuesExactNamed($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getFiveOrMoreElements - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getFiveOrMoreElements')] public function testTooManyValues($value) { $constraint = new Count([ @@ -164,9 +153,7 @@ public function testTooManyValues($value) ->assertRaised(); } - /** - * @dataProvider getFiveOrMoreElements - */ + #[DataProvider('getFiveOrMoreElements')] public function testTooManyValuesNamed($value) { $constraint = new Count(max: 4, maxMessage: 'myMessage'); @@ -182,11 +169,9 @@ public function testTooManyValuesNamed($value) ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getThreeOrLessElements - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getThreeOrLessElements')] public function testTooFewValues($value) { $constraint = new Count([ @@ -205,9 +190,7 @@ public function testTooFewValues($value) ->assertRaised(); } - /** - * @dataProvider getThreeOrLessElements - */ + #[DataProvider('getThreeOrLessElements')] public function testTooFewValuesNamed($value) { $constraint = new Count(min: 4, minMessage: 'myMessage'); @@ -223,11 +206,9 @@ public function testTooFewValuesNamed($value) ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getFiveOrMoreElements - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getFiveOrMoreElements')] public function testTooManyValuesExact($value) { $constraint = new Count([ @@ -247,9 +228,7 @@ public function testTooManyValuesExact($value) ->assertRaised(); } - /** - * @dataProvider getFiveOrMoreElements - */ + #[DataProvider('getFiveOrMoreElements')] public function testTooManyValuesExactNamed($value) { $constraint = new Count(exactly: 4, exactMessage: 'myMessage'); @@ -265,9 +244,7 @@ public function testTooManyValuesExactNamed($value) ->assertRaised(); } - /** - * @dataProvider getThreeOrLessElements - */ + #[DataProvider('getThreeOrLessElements')] public function testTooFewValuesExact($value) { $constraint = new Count( diff --git a/Tests/Constraints/CountryTest.php b/Tests/Constraints/CountryTest.php index 4bd85933d..201dfaa61 100644 --- a/Tests/Constraints/CountryTest.php +++ b/Tests/Constraints/CountryTest.php @@ -24,15 +24,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertFalse($aConstraint->alpha3); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertTrue($bConstraint->alpha3); self::assertSame(['Default', 'CountryDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/CountryValidatorTest.php b/Tests/Constraints/CountryValidatorTest.php index d6fa0c35e..f22ad46a7 100644 --- a/Tests/Constraints/CountryValidatorTest.php +++ b/Tests/Constraints/CountryValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Validator\Constraints\Country; use Symfony\Component\Validator\Constraints\CountryValidator; @@ -60,9 +61,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Country()); } - /** - * @dataProvider getValidCountries - */ + #[DataProvider('getValidCountries')] public function testValidCountries($country) { $this->validator->validate($country, new Country()); @@ -79,9 +78,7 @@ public static function getValidCountries() ]; } - /** - * @dataProvider getInvalidCountries - */ + #[DataProvider('getInvalidCountries')] public function testInvalidCountries($country) { $constraint = new Country(message: 'myMessage'); @@ -102,9 +99,7 @@ public static function getInvalidCountries() ]; } - /** - * @dataProvider getValidAlpha3Countries - */ + #[DataProvider('getValidAlpha3Countries')] public function testValidAlpha3Countries($country) { $this->validator->validate($country, new Country(alpha3: true)); @@ -121,9 +116,7 @@ public static function getValidAlpha3Countries() ]; } - /** - * @dataProvider getInvalidAlpha3Countries - */ + #[DataProvider('getInvalidAlpha3Countries')] public function testInvalidAlpha3Countries($country) { $constraint = new Country( diff --git a/Tests/Constraints/CssColorTest.php b/Tests/Constraints/CssColorTest.php index e1e3e2501..72479363c 100644 --- a/Tests/Constraints/CssColorTest.php +++ b/Tests/Constraints/CssColorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\CssColor; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -27,15 +29,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame([CssColor::HEX_LONG, CssColor::HEX_SHORT], $aConstraint->formats); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame([CssColor::HEX_LONG], $bConstraint->formats); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'CssColorDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame([CssColor::HEX_SHORT], $cConstraint->formats); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); @@ -60,6 +62,15 @@ public function testMissingPatternDoctrineStyle() CssColor::HSLA, ], $constraint->formats); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new CssColor(['formats' => CssColor::RGB]); + + $this->assertSame(CssColor::RGB, $constraint->formats); + } } class CssColorDummy diff --git a/Tests/Constraints/CssColorValidatorTest.php b/Tests/Constraints/CssColorValidatorTest.php index bc1087c47..857fac605 100644 --- a/Tests/Constraints/CssColorValidatorTest.php +++ b/Tests/Constraints/CssColorValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\CssColor; use Symfony\Component\Validator\Constraints\CssColorValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -44,18 +45,14 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new CssColor(CssColor::HEX_LONG)); } - /** - * @dataProvider getValidAnyColor - */ + #[DataProvider('getValidAnyColor')] public function testValidAnyColor($cssColor) { $this->validator->validate($cssColor, new CssColor()); $this->assertNoViolation(); } - /** - * @dataProvider getValidAnyColor - */ + #[DataProvider('getValidAnyColor')] public function testValidAnyColorWithNewLine($cssColor) { $this->validator->validate($cssColor."\n", new CssColor([], 'myMessage')); @@ -85,9 +82,7 @@ public static function getValidAnyColor(): array ]; } - /** - * @dataProvider getValidHexLongColors - */ + #[DataProvider('getValidHexLongColors')] public function testValidHexLongColors($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::HEX_LONG)); @@ -99,9 +94,7 @@ public static function getValidHexLongColors(): array return [['#ABCDEF'], ['#abcdef'], ['#C0FFEE'], ['#c0ffee'], ['#501311']]; } - /** - * @dataProvider getValidHexLongColorsWithAlpha - */ + #[DataProvider('getValidHexLongColorsWithAlpha')] public function testValidHexLongColorsWithAlpha($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::HEX_LONG_WITH_ALPHA)); @@ -113,9 +106,7 @@ public static function getValidHexLongColorsWithAlpha(): array return [['#ABCDEF00'], ['#abcdef01'], ['#C0FFEE02'], ['#c0ffee03'], ['#501311FF']]; } - /** - * @dataProvider getValidHexShortColors - */ + #[DataProvider('getValidHexShortColors')] public function testValidHexShortColors($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::HEX_SHORT)); @@ -127,9 +118,7 @@ public static function getValidHexShortColors(): array return [['#F4B'], ['#FAB'], ['#f4b'], ['#fab']]; } - /** - * @dataProvider getValidHexShortColorsWithAlpha - */ + #[DataProvider('getValidHexShortColorsWithAlpha')] public function testValidHexShortColorsWithAlpha($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::HEX_SHORT_WITH_ALPHA)); @@ -141,9 +130,7 @@ public static function getValidHexShortColorsWithAlpha(): array return [['#F4B1'], ['#FAB1'], ['#f4b1'], ['#fab1']]; } - /** - * @dataProvider getValidBasicNamedColors - */ + #[DataProvider('getValidBasicNamedColors')] public function testValidBasicNamedColors($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::BASIC_NAMED_COLORS)); @@ -158,9 +145,7 @@ public static function getValidBasicNamedColors(): array ]; } - /** - * @dataProvider getValidExtendedNamedColors - */ + #[DataProvider('getValidExtendedNamedColors')] public function testValidExtendedNamedColors($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::EXTENDED_NAMED_COLORS)); @@ -175,9 +160,7 @@ public static function getValidExtendedNamedColors(): array ]; } - /** - * @dataProvider getValidSystemColors - */ + #[DataProvider('getValidSystemColors')] public function testValidSystemColors($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::SYSTEM_COLORS)); @@ -193,9 +176,7 @@ public static function getValidSystemColors(): array ]; } - /** - * @dataProvider getValidKeywords - */ + #[DataProvider('getValidKeywords')] public function testValidKeywords($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::KEYWORDS)); @@ -207,9 +188,7 @@ public static function getValidKeywords(): array return [['transparent'], ['currentColor']]; } - /** - * @dataProvider getValidRGB - */ + #[DataProvider('getValidRGB')] public function testValidRGB($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::RGB)); @@ -225,9 +204,7 @@ public static function getValidRGB(): array ]; } - /** - * @dataProvider getValidRGBA - */ + #[DataProvider('getValidRGBA')] public function testValidRGBA($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::RGBA)); @@ -244,9 +221,7 @@ public static function getValidRGBA(): array ]; } - /** - * @dataProvider getValidHSL - */ + #[DataProvider('getValidHSL')] public function testValidHSL($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::HSL)); @@ -262,9 +237,7 @@ public static function getValidHSL(): array ]; } - /** - * @dataProvider getValidHSLA - */ + #[DataProvider('getValidHSLA')] public function testValidHSLA($cssColor) { $this->validator->validate($cssColor, new CssColor(CssColor::HSLA)); @@ -281,9 +254,7 @@ public static function getValidHSLA(): array ]; } - /** - * @dataProvider getInvalidHexColors - */ + #[DataProvider('getInvalidHexColors')] public function testInvalidHexColors($cssColor) { $constraint = new CssColor([CssColor::HEX_LONG, CssColor::HEX_LONG_WITH_ALPHA], 'myMessage'); @@ -300,9 +271,7 @@ public static function getInvalidHexColors(): array return [['ABCDEF'], ['abcdef'], ['#K0FFEE'], ['#k0ffee'], ['#_501311'], ['ABCDEF00'], ['abcdefcc'], ['#K0FFEE33'], ['#k0ffeecc'], ['#_50131100'], ['#FAℬ'], ['#Ⅎab'], ['#F4️⃣B'], ['#f(4)b'], ['#907;']]; } - /** - * @dataProvider getInvalidShortHexColors - */ + #[DataProvider('getInvalidShortHexColors')] public function testInvalidShortHexColors($cssColor) { $this->validator->validate($cssColor, new CssColor([CssColor::HEX_SHORT, CssColor::HEX_SHORT_WITH_ALPHA], 'myMessage')); @@ -318,9 +287,7 @@ public static function getInvalidShortHexColors(): array return [['ABC'], ['ABCD'], ['abc'], ['abcd'], ['#K0F'], ['#K0FF'], ['#k0f'], ['#k0ff'], ['#_50'], ['#_501']]; } - /** - * @dataProvider getInvalidNamedColors - */ + #[DataProvider('getInvalidNamedColors')] public function testInvalidNamedColors($cssColor) { $this->validator->validate($cssColor, new CssColor([ @@ -341,9 +308,7 @@ public static function getInvalidNamedColors(): array return [['fabpot'], ['ngrekas'], ['symfony'], ['FABPOT'], ['NGREKAS'], ['SYMFONY'], [new StringableValue('SYMFONY')]]; } - /** - * @dataProvider getInvalidRGB - */ + #[DataProvider('getInvalidRGB')] public function testInvalidRGB($cssColor) { $this->validator->validate($cssColor, new CssColor([ @@ -364,9 +329,7 @@ public static function getInvalidRGB(): array return [['rgb(999,999,999)'], ['rgb(-99,-99,-99)'], ['rgb(a,b,c)'], ['rgb(99 99, 9 99, 99 9)']]; } - /** - * @dataProvider getInvalidRGBA - */ + #[DataProvider('getInvalidRGBA')] public function testInvalidRGBA($cssColor) { $this->validator->validate($cssColor, new CssColor([ @@ -392,9 +355,7 @@ public static function getInvalidRGBA(): array ]; } - /** - * @dataProvider getInvalidHSL - */ + #[DataProvider('getInvalidHSL')] public function testInvalidHSL($cssColor) { $this->validator->validate($cssColor, new CssColor([ @@ -415,9 +376,7 @@ public static function getInvalidHSL(): array return [['hsl(1000, 1000%, 20000%)'], ['hsl(-100, -10%, -2%)'], ['hsl(a, b, c)'], ['hsl(a, b%, c%)'], ['hsl( 99 99% , 9 99% , 99 9%)']]; } - /** - * @dataProvider getInvalidHSLA - */ + #[DataProvider('getInvalidHSLA')] public function testInvalidHSLA($cssColor) { $this->validator->validate($cssColor, new CssColor([ @@ -444,9 +403,7 @@ public static function getInvalidHSLA(): array ]; } - /** - * @dataProvider getInvalidFormats - */ + #[DataProvider('getInvalidFormats')] public function testUnknownFormatAsStringThrowsException($formats) { $this->expectException(\InvalidArgumentException::class); diff --git a/Tests/Constraints/CurrencyTest.php b/Tests/Constraints/CurrencyTest.php index 6cfb6bfc3..356799c45 100644 --- a/Tests/Constraints/CurrencyTest.php +++ b/Tests/Constraints/CurrencyTest.php @@ -24,11 +24,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'CurrencyDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/CurrencyValidatorTest.php b/Tests/Constraints/CurrencyValidatorTest.php index af4c20f8e..a101ce72f 100644 --- a/Tests/Constraints/CurrencyValidatorTest.php +++ b/Tests/Constraints/CurrencyValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Validator\Constraints\Currency; use Symfony\Component\Validator\Constraints\CurrencyValidator; @@ -60,9 +61,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Currency()); } - /** - * @dataProvider getValidCurrencies - */ + #[DataProvider('getValidCurrencies')] public function testValidCurrencies($currency) { $this->validator->validate($currency, new Currency()); @@ -70,9 +69,7 @@ public function testValidCurrencies($currency) $this->assertNoViolation(); } - /** - * @dataProvider getValidCurrencies - **/ + #[DataProvider('getValidCurrencies')] public function testValidCurrenciesWithCountrySpecificLocale($currency) { IntlTestHelper::requireFullIntl($this); @@ -95,9 +92,7 @@ public static function getValidCurrencies() ]; } - /** - * @dataProvider getInvalidCurrencies - */ + #[DataProvider('getInvalidCurrencies')] public function testInvalidCurrencies($currency) { $constraint = new Currency(message: 'myMessage'); @@ -110,9 +105,7 @@ public function testInvalidCurrencies($currency) ->assertRaised(); } - /** - * @dataProvider getInvalidCurrencies - */ + #[DataProvider('getInvalidCurrencies')] public function testInvalidCurrenciesNamed($currency) { $constraint = new Currency(message: 'myMessage'); diff --git a/Tests/Constraints/DateTest.php b/Tests/Constraints/DateTest.php index 83c75328e..cdff353da 100644 --- a/Tests/Constraints/DateTest.php +++ b/Tests/Constraints/DateTest.php @@ -24,11 +24,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'DateDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/DateTimeTest.php b/Tests/Constraints/DateTimeTest.php index 172d1a290..d144fc34a 100644 --- a/Tests/Constraints/DateTimeTest.php +++ b/Tests/Constraints/DateTimeTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\DateTime; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,19 +26,28 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame('Y-m-d H:i:s', $aConstraint->format); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('d.m.Y', $bConstraint->format); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'DateTimeDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame('m/d/Y', $cConstraint->format); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new DateTime(['format' => 'm/d/Y']); + + $this->assertSame('m/d/Y', $constraint->format); + } } class DateTimeDummy diff --git a/Tests/Constraints/DateTimeValidatorTest.php b/Tests/Constraints/DateTimeValidatorTest.php index 383f06215..b1361a4cb 100644 --- a/Tests/Constraints/DateTimeValidatorTest.php +++ b/Tests/Constraints/DateTimeValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\DateTime; use Symfony\Component\Validator\Constraints\DateTimeValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -58,9 +59,7 @@ public function testDateTimeWithDefaultFormat() ->assertRaised(); } - /** - * @dataProvider getValidDateTimes - */ + #[DataProvider('getValidDateTimes')] public function testValidDateTimes($format, $dateTime) { $constraint = new DateTime(format: $format); @@ -81,9 +80,7 @@ public static function getValidDateTimes() ]; } - /** - * @dataProvider getInvalidDateTimes - */ + #[DataProvider('getInvalidDateTimes')] public function testInvalidDateTimes($format, $dateTime, $code) { $constraint = new DateTime( diff --git a/Tests/Constraints/DateValidatorTest.php b/Tests/Constraints/DateValidatorTest.php index 65909ef83..4cc41eb2d 100644 --- a/Tests/Constraints/DateValidatorTest.php +++ b/Tests/Constraints/DateValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Date; use Symfony\Component\Validator\Constraints\DateValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -43,9 +44,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Date()); } - /** - * @dataProvider getValidDates - */ + #[DataProvider('getValidDates')] public function testValidDates($date) { $this->validator->validate($date, new Date()); @@ -53,9 +52,7 @@ public function testValidDates($date) $this->assertNoViolation(); } - /** - * @dataProvider getValidDates - */ + #[DataProvider('getValidDates')] public function testValidDatesWithNewLine(string $date) { $this->validator->validate($date."\n", new Date(message: 'myMessage')); @@ -75,9 +72,7 @@ public static function getValidDates() ]; } - /** - * @dataProvider getInvalidDates - */ + #[DataProvider('getInvalidDates')] public function testInvalidDates($date, $code) { $constraint = new Date(message: 'myMessage'); diff --git a/Tests/Constraints/DisableAutoMappingTest.php b/Tests/Constraints/DisableAutoMappingTest.php index e7b6a8db7..e8dbed4eb 100644 --- a/Tests/Constraints/DisableAutoMappingTest.php +++ b/Tests/Constraints/DisableAutoMappingTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\DisableAutoMapping; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -23,9 +25,8 @@ */ class DisableAutoMappingTest extends TestCase { - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testGroups() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/DivisibleByTest.php b/Tests/Constraints/DivisibleByTest.php index bfe0a6cac..9c0f1f9fa 100644 --- a/Tests/Constraints/DivisibleByTest.php +++ b/Tests/Constraints/DivisibleByTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\DivisibleBy; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'DivisibleByDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new DivisibleBy(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class DivisibleByDummy diff --git a/Tests/Constraints/DivisibleByValidatorTest.php b/Tests/Constraints/DivisibleByValidatorTest.php index be96ad2b4..a181cfa01 100644 --- a/Tests/Constraints/DivisibleByValidatorTest.php +++ b/Tests/Constraints/DivisibleByValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\DivisibleBy; use Symfony\Component\Validator\Constraints\DivisibleByValidator; @@ -90,9 +91,7 @@ public static function provideInvalidComparisons(): array ]; } - /** - * @dataProvider throwsOnNonNumericValuesProvider - */ + #[DataProvider('throwsOnNonNumericValuesProvider')] public function testThrowsOnNonNumericValues(string $expectedGivenType, $value, $comparedValue) { $this->expectException(UnexpectedValueException::class); diff --git a/Tests/Constraints/EmailTest.php b/Tests/Constraints/EmailTest.php index 9436b4bd6..b57784b89 100644 --- a/Tests/Constraints/EmailTest.php +++ b/Tests/Constraints/EmailTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -54,9 +56,8 @@ public function testNormalizerCanBeSet() $this->assertEquals('trim', $email->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -64,9 +65,8 @@ public function testInvalidNormalizerThrowsException() new Email(['normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -79,16 +79,16 @@ public function testAttribute() $metadata = new ClassMetadata(EmailDummy::class); (new AttributeLoader())->loadClassMetadata($metadata); - [$aConstraint] = $metadata->properties['a']->constraints; + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertNull($aConstraint->mode); self::assertNull($aConstraint->normalizer); - [$bConstraint] = $metadata->properties['b']->constraints; + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(Email::VALIDATION_MODE_HTML5, $bConstraint->mode); self::assertSame('trim', $bConstraint->normalizer); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/EmailValidatorTest.php b/Tests/Constraints/EmailValidatorTest.php index 483b534e6..47e90f077 100644 --- a/Tests/Constraints/EmailValidatorTest.php +++ b/Tests/Constraints/EmailValidatorTest.php @@ -11,14 +11,14 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Constraints\EmailValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; -/** - * @group dns-sensitive - */ +#[Group('dns-sensitive')] class EmailValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): EmailValidator @@ -60,9 +60,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Email()); } - /** - * @dataProvider getValidEmails - */ + #[DataProvider('getValidEmails')] public function testValidEmails($email) { $this->validator->validate($email, new Email()); @@ -70,9 +68,7 @@ public function testValidEmails($email) $this->assertNoViolation(); } - /** - * @dataProvider getValidEmails - */ + #[DataProvider('getValidEmails')] public function testValidEmailsWithNewLine($email) { $this->validator->validate($email."\n", new Email()); @@ -92,9 +88,7 @@ public static function getValidEmails() ]; } - /** - * @dataProvider getValidEmailsWithWhitespaces - */ + #[DataProvider('getValidEmailsWithWhitespaces')] public function testValidNormalizedEmails($email) { $this->validator->validate($email, new Email(normalizer: 'trim')); @@ -110,9 +104,7 @@ public static function getValidEmailsWithWhitespaces() ]; } - /** - * @dataProvider getValidEmailsHtml5 - */ + #[DataProvider('getValidEmailsHtml5')] public function testValidEmailsHtml5($email) { $this->validator->validate($email, new Email(mode: Email::VALIDATION_MODE_HTML5)); @@ -130,9 +122,7 @@ public static function getValidEmailsHtml5() ]; } - /** - * @dataProvider getInvalidEmails - */ + #[DataProvider('getInvalidEmails')] public function testInvalidEmails($email) { $constraint = new Email(message: 'myMessage'); @@ -155,9 +145,7 @@ public static function getInvalidEmails() ]; } - /** - * @dataProvider getInvalidHtml5Emails - */ + #[DataProvider('getInvalidHtml5Emails')] public function testInvalidHtml5Emails($email) { $constraint = new Email( @@ -195,9 +183,7 @@ public static function getInvalidHtml5Emails() ]; } - /** - * @dataProvider getInvalidAllowNoTldEmails - */ + #[DataProvider('getInvalidAllowNoTldEmails')] public function testInvalidAllowNoTldEmails($email) { $constraint = new Email( @@ -265,9 +251,7 @@ public function testUnknownModesOnValidateTriggerException() $this->validator->validate('example@example..com', $constraint); } - /** - * @dataProvider getInvalidEmailsForStrictChecks - */ + #[DataProvider('getInvalidEmailsForStrictChecks')] public function testStrictWithInvalidEmails($email) { $constraint = new Email( diff --git a/Tests/Constraints/EnableAutoMappingTest.php b/Tests/Constraints/EnableAutoMappingTest.php index 525a62ed5..a1c44c37d 100644 --- a/Tests/Constraints/EnableAutoMappingTest.php +++ b/Tests/Constraints/EnableAutoMappingTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\EnableAutoMapping; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -23,9 +25,8 @@ */ class EnableAutoMappingTest extends TestCase { - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testGroups() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/EqualToTest.php b/Tests/Constraints/EqualToTest.php index 56e40dbdd..9d7cf1dfc 100644 --- a/Tests/Constraints/EqualToTest.php +++ b/Tests/Constraints/EqualToTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\EqualTo; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'EqualToDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new EqualTo(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class EqualToDummy diff --git a/Tests/Constraints/ExpressionSyntaxTest.php b/Tests/Constraints/ExpressionSyntaxTest.php index 8731a5d85..745a49c10 100644 --- a/Tests/Constraints/ExpressionSyntaxTest.php +++ b/Tests/Constraints/ExpressionSyntaxTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\ExpressionSyntax; use Symfony\Component\Validator\Constraints\ExpressionSyntaxValidator; @@ -26,9 +29,7 @@ public function testValidatedByStandardValidator() self::assertSame(ExpressionSyntaxValidator::class, $constraint->validatedBy()); } - /** - * @dataProvider provideServiceValidatedConstraints - */ + #[DataProvider('provideServiceValidatedConstraints')] public function testValidatedByService(ExpressionSyntax $constraint) { self::assertSame('my_service', $constraint->validatedBy()); @@ -41,12 +42,11 @@ public static function provideServiceValidatedConstraints(): iterable $metadata = new ClassMetadata(ExpressionSyntaxDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - yield 'attribute' => [$metadata->properties['b']->constraints[0]]; + yield 'attribute' => [$metadata->getPropertyMetadata('b')[0]->getConstraints()[0]]; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testValidatedByServiceDoctrineStyle() { $constraint = new ExpressionSyntax(['service' => 'my_service']); @@ -59,16 +59,16 @@ public function testAttributes() $metadata = new ClassMetadata(ExpressionSyntaxDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertNull($aConstraint->service); self::assertNull($aConstraint->allowedVariables); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('my_service', $bConstraint->service); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'ExpressionSyntaxDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['foo', 'bar'], $cConstraint->allowedVariables); self::assertSame(['my_group'], $cConstraint->groups); } diff --git a/Tests/Constraints/ExpressionTest.php b/Tests/Constraints/ExpressionTest.php index 07abb071d..d8f9fee9c 100644 --- a/Tests/Constraints/ExpressionTest.php +++ b/Tests/Constraints/ExpressionTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Exception\MissingOptionsException; @@ -24,18 +26,18 @@ public function testAttributes() $metadata = new ClassMetadata(ExpressionDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame('value == "1"', $aConstraint->expression); self::assertSame([], $aConstraint->values); self::assertTrue($aConstraint->negate); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('value == "1"', $bConstraint->expression); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'ExpressionDummy'], $bConstraint->groups); self::assertTrue($bConstraint->negate); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame('value == someVariable', $cConstraint->expression); self::assertSame(['someVariable' => 42], $cConstraint->values); self::assertSame(['foo'], $cConstraint->groups); @@ -51,9 +53,8 @@ public function testMissingPattern() new Expression(null); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testMissingPatternDoctrineStyle() { $this->expectException(MissingOptionsException::class); @@ -61,6 +62,26 @@ public function testMissingPatternDoctrineStyle() new Expression([]); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testInitializeWithOptionsArray() + { + $constraint = new Expression([ + 'expression' => '!this.getParent().get("field2").getData()', + ]); + + $this->assertSame('!this.getParent().get("field2").getData()', $constraint->expression); + } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testExpressionInOptionsArray() + { + $constraint = new Expression(null, options: ['expression' => '!this.getParent().get("field2").getData()']); + + $this->assertSame('!this.getParent().get("field2").getData()', $constraint->expression); + } } class ExpressionDummy diff --git a/Tests/Constraints/ExpressionValidatorTest.php b/Tests/Constraints/ExpressionValidatorTest.php index 21c9eb630..8ac513e1b 100644 --- a/Tests/Constraints/ExpressionValidatorTest.php +++ b/Tests/Constraints/ExpressionValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; @@ -355,9 +356,7 @@ public function testIsValidExpressionInvalid() $this->assertCount(2, $this->context->getViolations()); } - /** - * @dataProvider provideCompileIsValid - */ + #[DataProvider('provideCompileIsValid')] public function testCompileIsValid(string $expression, array $names, string $expected) { $expressionLanguage = new ExpressionLanguage(); diff --git a/Tests/Constraints/FileTest.php b/Tests/Constraints/FileTest.php index 3e03f7881..2926ecec6 100644 --- a/Tests/Constraints/FileTest.php +++ b/Tests/Constraints/FileTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -20,9 +21,7 @@ class FileTest extends TestCase { - /** - * @dataProvider provideValidSizes - */ + #[DataProvider('provideValidSizes')] public function testMaxSize($maxSize, $bytes, $binaryFormat) { $file = new File(maxSize: $maxSize); @@ -41,9 +40,7 @@ public function testMagicIsset() $this->assertFalse($file->__isset('toto')); } - /** - * @dataProvider provideValidSizes - */ + #[DataProvider('provideValidSizes')] public function testMaxSizeCanBeSetAfterInitialization($maxSize, $bytes, $binaryFormat) { $file = new File(); @@ -53,9 +50,7 @@ public function testMaxSizeCanBeSetAfterInitialization($maxSize, $bytes, $binary $this->assertSame($binaryFormat, $file->binaryFormat); } - /** - * @dataProvider provideInvalidSizes - */ + #[DataProvider('provideInvalidSizes')] public function testInvalidValueForMaxSizeThrowsExceptionAfterInitialization($maxSize) { $file = new File(maxSize: 1000); @@ -65,9 +60,7 @@ public function testInvalidValueForMaxSizeThrowsExceptionAfterInitialization($ma $file->maxSize = $maxSize; } - /** - * @dataProvider provideInvalidSizes - */ + #[DataProvider('provideInvalidSizes')] public function testMaxSizeCannotBeSetToInvalidValueAfterInitialization($maxSize) { $file = new File(maxSize: 1000); @@ -105,9 +98,7 @@ public function testInvalidFilenameCountUnitThrowsException() $file = new File(filenameCountUnit: 'nonExistentCountUnit'); } - /** - * @dataProvider provideInValidSizes - */ + #[DataProvider('provideInValidSizes')] public function testInvalidMaxSize($maxSize) { $this->expectException(ConstraintDefinitionException::class); @@ -146,9 +137,7 @@ public static function provideInvalidSizes() ]; } - /** - * @dataProvider provideFormats - */ + #[DataProvider('provideFormats')] public function testBinaryFormat($maxSize, $guessedFormat, $binaryFormat) { $file = new File(maxSize: $maxSize, binaryFormat: $guessedFormat); @@ -176,15 +165,15 @@ public function testAttributes() $metadata = new ClassMetadata(FileDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertNull($aConstraint->maxSize); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(100, $bConstraint->maxSize); self::assertSame('myMessage', $bConstraint->notFoundMessage); self::assertSame(['Default', 'FileDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(100000, $cConstraint->maxSize); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); diff --git a/Tests/Constraints/FileValidatorTestCase.php b/Tests/Constraints/FileValidatorTestCase.php index b1ebf530e..993570788 100644 --- a/Tests/Constraints/FileValidatorTestCase.php +++ b/Tests/Constraints/FileValidatorTestCase.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Constraints\FileValidator; @@ -159,9 +162,7 @@ public static function provideMaxSizeExceededTests() ]; } - /** - * @dataProvider provideMaxSizeExceededTests - */ + #[DataProvider('provideMaxSizeExceededTests')] public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix) { fseek($this->file, $bytesWritten - 1, \SEEK_SET); @@ -211,9 +212,7 @@ public static function provideMaxSizeNotExceededTests() ]; } - /** - * @dataProvider provideMaxSizeNotExceededTests - */ + #[DataProvider('provideMaxSizeNotExceededTests')] public function testMaxSizeNotExceeded($bytesWritten, $limit) { fseek($this->file, $bytesWritten - 1, \SEEK_SET); @@ -258,9 +257,7 @@ public static function provideBinaryFormatTests() ]; } - /** - * @dataProvider provideBinaryFormatTests - */ + #[DataProvider('provideBinaryFormatTests')] public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsString, $limitAsString, $suffix) { fseek($this->file, $bytesWritten - 1, \SEEK_SET); @@ -375,9 +372,8 @@ public function testInvalidMimeType() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidMimeTypeDoctrineStyle() { $file = $this @@ -451,9 +447,8 @@ public function testDisallowEmpty() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testDisallowEmptyDoctrineStyle() { ftruncate($this->file, 0); @@ -469,9 +464,7 @@ public function testDisallowEmptyDoctrineStyle() ->assertRaised(); } - /** - * @dataProvider uploadedFileErrorProvider - */ + #[DataProvider('uploadedFileErrorProvider')] public function testUploadedFileError($error, $message, array $params = [], $maxSize = null) { $file = new UploadedFile(tempnam(sys_get_temp_dir(), 'file-validator-test-'), 'originalName', 'mime', $error); @@ -544,9 +537,7 @@ public function testNegativeMaxSize() $file->maxSize = -1; } - /** - * @dataProvider providerValidExtension - */ + #[DataProvider('providerValidExtension')] public function testExtensionValid(string $name) { $path = __DIR__.'/Fixtures/'.$name; @@ -573,9 +564,7 @@ public static function providerValidExtension(): iterable yield ['uppercased-extension.TXT']; } - /** - * @dataProvider provideInvalidExtension - */ + #[DataProvider('provideInvalidExtension')] public function testExtensionInvalid(string $name, string $extension) { $path = __DIR__.'/Fixtures/'.$name; @@ -672,9 +661,7 @@ public function testUploadedFileExtensions() $this->assertNoViolation(); } - /** - * @dataProvider provideFilenameMaxLengthIsTooLong - */ + #[DataProvider('provideFilenameMaxLengthIsTooLong')] public function testFilenameMaxLengthIsTooLong(File $constraintFile, string $filename, string $messageViolation) { file_put_contents($this->path, '1'); @@ -718,9 +705,7 @@ public static function provideFilenameMaxLengthIsTooLong(): \Generator ]; } - /** - * @dataProvider provideFilenameCountUnit - */ + #[DataProvider('provideFilenameCountUnit')] public function testValidCountUnitFilenameMaxLength(int $maxLength, string $countUnit) { file_put_contents($this->path, '1'); @@ -731,9 +716,7 @@ public function testValidCountUnitFilenameMaxLength(int $maxLength, string $coun $this->assertNoViolation(); } - /** - * @dataProvider provideFilenameCharset - */ + #[DataProvider('provideFilenameCharset')] public function testFilenameCharset(string $filename, string $charset, bool $isValid) { file_put_contents($this->path, '1'); diff --git a/Tests/Constraints/Fixtures/test.mp4 b/Tests/Constraints/Fixtures/test.mp4 new file mode 100644 index 000000000..d251e5def Binary files /dev/null and b/Tests/Constraints/Fixtures/test.mp4 differ diff --git a/Tests/Constraints/Fixtures/test_16by9.mp4 b/Tests/Constraints/Fixtures/test_16by9.mp4 new file mode 100644 index 000000000..deb4587f8 Binary files /dev/null and b/Tests/Constraints/Fixtures/test_16by9.mp4 differ diff --git a/Tests/Constraints/Fixtures/test_4by3.mp4 b/Tests/Constraints/Fixtures/test_4by3.mp4 new file mode 100644 index 000000000..243809ed2 Binary files /dev/null and b/Tests/Constraints/Fixtures/test_4by3.mp4 differ diff --git a/Tests/Constraints/Fixtures/test_corrupted.mp4 b/Tests/Constraints/Fixtures/test_corrupted.mp4 new file mode 100644 index 000000000..a03d94003 Binary files /dev/null and b/Tests/Constraints/Fixtures/test_corrupted.mp4 differ diff --git a/Tests/Constraints/Fixtures/test_landscape.mp4 b/Tests/Constraints/Fixtures/test_landscape.mp4 new file mode 100644 index 000000000..4781be732 Binary files /dev/null and b/Tests/Constraints/Fixtures/test_landscape.mp4 differ diff --git a/Tests/Constraints/Fixtures/test_portrait.mp4 b/Tests/Constraints/Fixtures/test_portrait.mp4 new file mode 100644 index 000000000..d4528785d Binary files /dev/null and b/Tests/Constraints/Fixtures/test_portrait.mp4 differ diff --git a/Tests/Constraints/GreaterThanOrEqualTest.php b/Tests/Constraints/GreaterThanOrEqualTest.php index 270ac5296..96ebe737d 100644 --- a/Tests/Constraints/GreaterThanOrEqualTest.php +++ b/Tests/Constraints/GreaterThanOrEqualTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'GreaterThanOrEqualDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new GreaterThanOrEqual(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class GreaterThanOrEqualDummy diff --git a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php index 47f190851..7431f18ee 100644 --- a/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php +++ b/Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GreaterThanOrEqualValidator; use Symfony\Component\Validator\Constraints\PositiveOrZero; @@ -61,9 +64,8 @@ public static function provideInvalidComparisons(): array ]; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -72,9 +74,8 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new PositiveOrZero(['propertyPath' => 'field']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); @@ -83,9 +84,7 @@ public function testThrowsConstraintExceptionIfValue() return new PositiveOrZero(['value' => 0]); } - /** - * @dataProvider provideInvalidConstraintOptions - */ + #[DataProvider('provideInvalidConstraintOptions')] public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { $this->markTestSkipped('Value option always set for PositiveOrZero constraint'); @@ -101,9 +100,7 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); } - /** - * @dataProvider provideValidComparisonsToPropertyPath - */ + #[DataProvider('provideValidComparisonsToPropertyPath')] public function testValidComparisonToPropertyPath($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in PositiveOrZero constraint'); diff --git a/Tests/Constraints/GreaterThanTest.php b/Tests/Constraints/GreaterThanTest.php index 8dd797cf4..07c0fe0e7 100644 --- a/Tests/Constraints/GreaterThanTest.php +++ b/Tests/Constraints/GreaterThanTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\GreaterThan; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'GreaterThanDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new GreaterThan(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class GreaterThanDummy diff --git a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php index 6b58bff85..74062a164 100644 --- a/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php +++ b/Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GreaterThanValidator; use Symfony\Component\Validator\Constraints\Positive; @@ -58,9 +61,8 @@ public static function provideInvalidComparisons(): array ]; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -69,9 +71,8 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new Positive(['propertyPath' => 'field']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); @@ -80,9 +81,7 @@ public function testThrowsConstraintExceptionIfValue() return new Positive(['value' => 0]); } - /** - * @dataProvider provideInvalidConstraintOptions - */ + #[DataProvider('provideInvalidConstraintOptions')] public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { $this->markTestSkipped('Value option always set for Positive constraint.'); @@ -103,9 +102,7 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); } - /** - * @dataProvider provideValidComparisonsToPropertyPath - */ + #[DataProvider('provideValidComparisonsToPropertyPath')] public function testValidComparisonToPropertyPath($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in Positive constraint'); diff --git a/Tests/Constraints/GroupSequenceTest.php b/Tests/Constraints/GroupSequenceTest.php index 7803a73cc..f27a95fd0 100644 --- a/Tests/Constraints/GroupSequenceTest.php +++ b/Tests/Constraints/GroupSequenceTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\GroupSequence; @@ -26,8 +28,12 @@ public function testCreate() $this->assertSame(['Group 1', 'Group 2'], $sequence->groups); } + #[Group('legacy')] + #[IgnoreDeprecations] public function testCreateDoctrineStyle() { + $this->expectUserDeprecationMessage('Since symfony/validator 7.4: Support for passing an array of options to "Symfony\Component\Validator\Constraints\GroupSequence::__construct()" is deprecated.'); + $sequence = new GroupSequence(['value' => ['Group 1', 'Group 2']]); $this->assertSame(['Group 1', 'Group 2'], $sequence->groups); diff --git a/Tests/Constraints/HostnameTest.php b/Tests/Constraints/HostnameTest.php index f6189c8d9..a01c256a5 100644 --- a/Tests/Constraints/HostnameTest.php +++ b/Tests/Constraints/HostnameTest.php @@ -24,15 +24,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertTrue($aConstraint->requireTld); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertFalse($bConstraint->requireTld); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'HostnameDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/HostnameValidatorTest.php b/Tests/Constraints/HostnameValidatorTest.php index 2471fe0b5..54544ed0a 100644 --- a/Tests/Constraints/HostnameValidatorTest.php +++ b/Tests/Constraints/HostnameValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Hostname; use Symfony\Component\Validator\Constraints\HostnameValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -42,9 +43,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Hostname()); } - /** - * @dataProvider getValidMultilevelDomains - */ + #[DataProvider('getValidMultilevelDomains')] public function testValidTldDomainsPassValidationIfTldRequired($domain) { $this->validator->validate($domain, new Hostname()); @@ -52,9 +51,7 @@ public function testValidTldDomainsPassValidationIfTldRequired($domain) $this->assertNoViolation(); } - /** - * @dataProvider getValidMultilevelDomains - */ + #[DataProvider('getValidMultilevelDomains')] public function testValidTldDomainsPassValidationIfTldNotRequired($domain) { $this->validator->validate($domain, new Hostname(requireTld: false)); @@ -76,9 +73,7 @@ public static function getValidMultilevelDomains() ]; } - /** - * @dataProvider getInvalidDomains - */ + #[DataProvider('getInvalidDomains')] public function testInvalidDomainsRaiseViolationIfTldRequired($domain) { $this->validator->validate($domain, new Hostname(message: 'myMessage')); @@ -89,9 +84,7 @@ public function testInvalidDomainsRaiseViolationIfTldRequired($domain) ->assertRaised(); } - /** - * @dataProvider getInvalidDomains - */ + #[DataProvider('getInvalidDomains')] public function testInvalidDomainsRaiseViolationIfTldNotRequired($domain) { $this->validator->validate($domain, new Hostname( @@ -116,9 +109,7 @@ public static function getInvalidDomains() ]; } - /** - * @dataProvider getReservedDomains - */ + #[DataProvider('getReservedDomains')] public function testReservedDomainsPassValidationIfTldNotRequired($domain) { $this->validator->validate($domain, new Hostname(requireTld: false)); @@ -126,9 +117,7 @@ public function testReservedDomainsPassValidationIfTldNotRequired($domain) $this->assertNoViolation(); } - /** - * @dataProvider getReservedDomains - */ + #[DataProvider('getReservedDomains')] public function testReservedDomainsRaiseViolationIfTldRequired($domain) { $this->validator->validate($domain, new Hostname( @@ -169,9 +158,7 @@ public function testReservedDomainsRaiseViolationIfTldRequiredNamed() ->assertRaised(); } - /** - * @dataProvider getTopLevelDomains - */ + #[DataProvider('getTopLevelDomains')] public function testTopLevelDomainsPassValidationIfTldNotRequired($domain) { $this->validator->validate($domain, new Hostname(requireTld: false)); @@ -179,9 +166,7 @@ public function testTopLevelDomainsPassValidationIfTldNotRequired($domain) $this->assertNoViolation(); } - /** - * @dataProvider getTopLevelDomains - */ + #[DataProvider('getTopLevelDomains')] public function testTopLevelDomainsRaiseViolationIfTldRequired($domain) { $this->validator->validate($domain, new Hostname( diff --git a/Tests/Constraints/IbanValidatorTest.php b/Tests/Constraints/IbanValidatorTest.php index 184924d5e..f2a596606 100644 --- a/Tests/Constraints/IbanValidatorTest.php +++ b/Tests/Constraints/IbanValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Iban; use Symfony\Component\Validator\Constraints\IbanValidator; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -38,9 +39,7 @@ public function testEmptyStringIsValid() $this->assertNoViolation(); } - /** - * @dataProvider getValidIbans - */ + #[DataProvider('getValidIbans')] public function testValidIbans($iban) { $this->validator->validate($iban, new Iban()); @@ -48,9 +47,7 @@ public function testValidIbans($iban) $this->assertNoViolation(); } - /** - * @dataProvider getValidIbans - */ + #[DataProvider('getValidIbans')] public function testValidIbansWithNewLine(string $iban) { $this->validator->validate($iban."\n", new Iban()); @@ -193,9 +190,7 @@ public static function getValidIbans() ]; } - /** - * @dataProvider getIbansWithInvalidFormat - */ + #[DataProvider('getIbansWithInvalidFormat')] public function testIbansWithInvalidFormat($iban) { $this->assertViolationRaised($iban, Iban::INVALID_FORMAT_ERROR); @@ -312,9 +307,7 @@ public static function getIbansWithInvalidFormat() ]; } - /** - * @dataProvider getIbansWithValidFormatButIncorrectChecksum - */ + #[DataProvider('getIbansWithValidFormatButIncorrectChecksum')] public function testIbansWithValidFormatButIncorrectChecksum($iban) { $this->assertViolationRaised($iban, Iban::CHECKSUM_FAILED_ERROR); @@ -430,9 +423,7 @@ public static function getIbansWithValidFormatButIncorrectChecksum() ]; } - /** - * @dataProvider getUnsupportedCountryCodes - */ + #[DataProvider('getUnsupportedCountryCodes')] public function testIbansWithUnsupportedCountryCode($countryCode) { $this->assertViolationRaised($countryCode.'260211000000230064016', Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR); @@ -454,9 +445,7 @@ public function testIbansWithInvalidCharacters() $this->assertViolationRaised('CH930076201162385295]', Iban::INVALID_CHARACTERS_ERROR); } - /** - * @dataProvider getIbansWithInvalidCountryCode - */ + #[DataProvider('getIbansWithInvalidCountryCode')] public function testIbansWithInvalidCountryCode($iban) { $this->assertViolationRaised($iban, Iban::INVALID_COUNTRY_CODE_ERROR); @@ -467,7 +456,7 @@ public function testLoadFromAttribute() $classMetadata = new ClassMetadata(IbanDummy::class); (new AttributeLoader())->loadClassMetadata($classMetadata); - [$constraint] = $classMetadata->properties['iban']->constraints; + [$constraint] = $classMetadata->getPropertyMetadata('iban')[0]->getConstraints(); $this->validator->validate('DE89 3704 0044 0532 0130 01', $constraint); diff --git a/Tests/Constraints/IdenticalToTest.php b/Tests/Constraints/IdenticalToTest.php index 6eb7044c6..589fab43b 100644 --- a/Tests/Constraints/IdenticalToTest.php +++ b/Tests/Constraints/IdenticalToTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\IdenticalTo; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'IdenticalToDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new IdenticalTo(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class IdenticalToDummy diff --git a/Tests/Constraints/ImageTest.php b/Tests/Constraints/ImageTest.php index df5b14c07..60641dd4c 100644 --- a/Tests/Constraints/ImageTest.php +++ b/Tests/Constraints/ImageTest.php @@ -24,20 +24,20 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertNull($aConstraint->minWidth); self::assertNull($aConstraint->maxWidth); self::assertNull($aConstraint->minHeight); self::assertNull($aConstraint->maxHeight); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(50, $bConstraint->minWidth); self::assertSame(200, $bConstraint->maxWidth); self::assertSame(50, $bConstraint->minHeight); self::assertSame(200, $bConstraint->maxHeight); self::assertSame(['Default', 'ImageDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(100000, $cConstraint->maxSize); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); diff --git a/Tests/Constraints/ImageValidatorTest.php b/Tests/Constraints/ImageValidatorTest.php index 7a7aa9197..18956036e 100644 --- a/Tests/Constraints/ImageValidatorTest.php +++ b/Tests/Constraints/ImageValidatorTest.php @@ -11,6 +11,10 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\Mime\MimeTypes; use Symfony\Component\Validator\Constraints\Image; @@ -18,10 +22,9 @@ use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; /** - * @requires extension fileinfo - * * @extends ConstraintValidatorTestCase */ +#[RequiresPhpExtension('fileinfo')] class ImageValidatorTest extends ConstraintValidatorTestCase { protected string $path; @@ -87,9 +90,9 @@ public function testFileNotFound() /** * Checks that the logic from FileValidator still works. - * - * @group legacy */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testFileNotFoundDoctrineStyle() { $this->validator->validate('foobar', new Image([ @@ -127,9 +130,8 @@ public function testWidthTooSmall() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testWidthTooSmallDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -155,9 +157,8 @@ public function testWidthTooBig() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testWidthTooBigDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -183,9 +184,8 @@ public function testHeightTooSmall() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testHeightTooSmallDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -211,9 +211,8 @@ public function testHeightTooBig() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testHeightTooBigDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -241,9 +240,8 @@ public function testPixelsTooFew() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testPixelsTooFewDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -273,9 +271,8 @@ public function testPixelsTooMany() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testPixelsTooManyDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -303,9 +300,8 @@ public function testRatioTooSmall() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testRatioTooSmallDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -331,9 +327,8 @@ public function testRatioTooBig() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testRatioTooBigDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -386,9 +381,8 @@ public function testSquareNotAllowed() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testSquareNotAllowedDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -414,9 +408,8 @@ public function testLandscapeNotAllowed() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testLandscapeNotAllowedDoctrineStyle() { $this->validator->validate($this->imageLandscape, new Image([ @@ -442,9 +435,8 @@ public function testPortraitNotAllowed() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testPortraitNotAllowedDoctrineStyle() { $this->validator->validate($this->imagePortrait, new Image([ @@ -478,9 +470,8 @@ public function testCorrupted() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testCorruptedDoctrineStyle() { if (!\function_exists('imagecreatefromstring')) { @@ -534,9 +525,8 @@ public function testInvalidMimeTypeWithNarrowedSet() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidMimeTypeWithNarrowedSetDoctrineStyle() { $this->validator->validate($this->image, new Image([ @@ -555,7 +545,7 @@ public function testInvalidMimeTypeWithNarrowedSetDoctrineStyle() ->assertRaised(); } - /** @dataProvider provideSvgWithViolation */ + #[DataProvider('provideSvgWithViolation')] public function testSvgWithViolation(string $image, Image $constraint, string $violation, array $parameters = []) { $this->validator->validate($image, $constraint); @@ -655,7 +645,7 @@ public static function provideSvgWithViolation(): iterable ]; } - /** @dataProvider provideSvgWithoutViolation */ + #[DataProvider('provideSvgWithoutViolation')] public function testSvgWithoutViolation(string $image, Image $constraint) { $this->validator->validate($image, $constraint); @@ -691,9 +681,7 @@ public static function provideSvgWithoutViolation(): iterable ]; } - /** - * @dataProvider providerValidExtension - */ + #[DataProvider('providerValidExtension')] public function testExtensionValid(string $name) { if (!class_exists(MimeTypes::class)) { @@ -713,9 +701,7 @@ public static function providerValidExtension(): iterable yield ['test.png.gif']; } - /** - * @dataProvider provideInvalidExtension - */ + #[DataProvider('provideInvalidExtension')] public function testExtensionInvalid(string $name, string $extension) { $path = __DIR__.'/Fixtures/'.$name; diff --git a/Tests/Constraints/InvalidComparisonToValueTestTrait.php b/Tests/Constraints/InvalidComparisonToValueTestTrait.php index 5b261d504..0f833edf4 100644 --- a/Tests/Constraints/InvalidComparisonToValueTestTrait.php +++ b/Tests/Constraints/InvalidComparisonToValueTestTrait.php @@ -11,13 +11,12 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Intl\Util\IntlTestHelper; trait InvalidComparisonToValueTestTrait { - /** - * @dataProvider provideAllInvalidComparisons - */ + #[DataProvider('provideAllInvalidComparisons')] public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $comparedValue, $comparedValueString, $comparedValueType) { // Conversion of dates to string differs between ICU versions diff --git a/Tests/Constraints/IpTest.php b/Tests/Constraints/IpTest.php index 2d740ae88..7a9f64b13 100644 --- a/Tests/Constraints/IpTest.php +++ b/Tests/Constraints/IpTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Ip; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -29,9 +31,8 @@ public function testNormalizerCanBeSet() $this->assertEquals('trim', $ip->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -39,9 +40,8 @@ public function testInvalidNormalizerThrowsException() new Ip(['normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -55,16 +55,16 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(Ip::V4, $aConstraint->version); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(Ip::V6, $bConstraint->version); self::assertSame('myMessage', $bConstraint->message); self::assertSame('trim', $bConstraint->normalizer); self::assertSame(['Default', 'IpDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/IpValidatorTest.php b/Tests/Constraints/IpValidatorTest.php index e37d61bb6..e839dce84 100644 --- a/Tests/Constraints/IpValidatorTest.php +++ b/Tests/Constraints/IpValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Ip; use Symfony\Component\Validator\Constraints\IpValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -50,9 +51,7 @@ public function testInvalidValidatorVersion() new Ip(version: 666); } - /** - * @dataProvider getValidIpsV4 - */ + #[DataProvider('getValidIpsV4')] public function testValidIpsV4($ip) { $this->validator->validate($ip, new Ip(version: Ip::V4)); @@ -74,9 +73,7 @@ public static function getValidIpsV4() ]; } - /** - * @dataProvider getValidIpsV4WithWhitespaces - */ + #[DataProvider('getValidIpsV4WithWhitespaces')] public function testValidIpsV4WithWhitespaces($ip) { $this->validator->validate($ip, new Ip( @@ -109,9 +106,7 @@ public static function getValidIpsV4WithWhitespaces() ]; } - /** - * @dataProvider getValidIpsV6 - */ + #[DataProvider('getValidIpsV6')] public function testValidIpsV6($ip) { $this->validator->validate($ip, new Ip(version: Ip::V6)); @@ -144,9 +139,7 @@ public static function getValidIpsV6() ]; } - /** - * @dataProvider getValidIpsAll - */ + #[DataProvider('getValidIpsAll')] public function testValidIpsAll($ip) { $this->validator->validate($ip, new Ip(version: Ip::ALL)); @@ -159,9 +152,7 @@ public static function getValidIpsAll() return array_merge(self::getValidIpsV4(), self::getValidIpsV6()); } - /** - * @dataProvider getInvalidIpsV4 - */ + #[DataProvider('getInvalidIpsV4')] public function testInvalidIpsV4($ip) { $constraint = new Ip( @@ -177,9 +168,7 @@ public function testInvalidIpsV4($ip) ->assertRaised(); } - /** - * @dataProvider getValidPublicIpsV4 - */ + #[DataProvider('getValidPublicIpsV4')] public function testInvalidNoPublicIpsV4($ip) { $constraint = new Ip( @@ -219,9 +208,7 @@ public static function getInvalidIpsV4() ]; } - /** - * @dataProvider getValidPrivateIpsV4 - */ + #[DataProvider('getValidPrivateIpsV4')] public function testValidPrivateIpsV4($ip) { $this->validator->validate($ip, new Ip(version: Ip::V4_ONLY_PRIVATE)); @@ -229,9 +216,7 @@ public function testValidPrivateIpsV4($ip) $this->assertNoViolation(); } - /** - * @dataProvider getValidPrivateIpsV4 - */ + #[DataProvider('getValidPrivateIpsV4')] public function testInvalidPrivateIpsV4($ip) { $constraint = new Ip( @@ -247,9 +232,7 @@ public function testInvalidPrivateIpsV4($ip) ->assertRaised(); } - /** - * @dataProvider getInvalidPrivateIpsV4 - */ + #[DataProvider('getInvalidPrivateIpsV4')] public function testInvalidOnlyPrivateIpsV4($ip) { $constraint = new Ip( @@ -279,9 +262,7 @@ public static function getInvalidPrivateIpsV4() return array_merge(self::getValidPublicIpsV4(), self::getValidReservedIpsV4()); } - /** - * @dataProvider getValidReservedIpsV4 - */ + #[DataProvider('getValidReservedIpsV4')] public function testValidReservedIpsV4($ip) { $this->validator->validate($ip, new Ip(version: Ip::V4_ONLY_RESERVED)); @@ -289,9 +270,7 @@ public function testValidReservedIpsV4($ip) $this->assertNoViolation(); } - /** - * @dataProvider getValidReservedIpsV4 - */ + #[DataProvider('getValidReservedIpsV4')] public function testInvalidReservedIpsV4($ip) { $constraint = new Ip( @@ -307,9 +286,7 @@ public function testInvalidReservedIpsV4($ip) ->assertRaised(); } - /** - * @dataProvider getInvalidReservedIpsV4 - */ + #[DataProvider('getInvalidReservedIpsV4')] public function testInvalidOnlyReservedIpsV4($ip) { $constraint = new Ip( @@ -339,9 +316,7 @@ public static function getInvalidReservedIpsV4() return array_merge(self::getValidPublicIpsV4(), self::getValidPrivateIpsV4()); } - /** - * @dataProvider getInvalidPublicIpsV4 - */ + #[DataProvider('getInvalidPublicIpsV4')] public function testInvalidPublicIpsV4($ip) { $constraint = new Ip( @@ -362,9 +337,7 @@ public static function getInvalidPublicIpsV4() return array_merge(self::getValidPrivateIpsV4(), self::getValidReservedIpsV4()); } - /** - * @dataProvider getInvalidIpsV6 - */ + #[DataProvider('getInvalidIpsV6')] public function testInvalidIpsV6($ip) { $constraint = new Ip( @@ -399,9 +372,7 @@ public static function getInvalidIpsV6() ]; } - /** - * @dataProvider getInvalidPrivateIpsV6 - */ + #[DataProvider('getInvalidPrivateIpsV6')] public function testInvalidPrivateIpsV6($ip) { $constraint = new Ip( @@ -426,9 +397,7 @@ public static function getInvalidPrivateIpsV6() ]; } - /** - * @dataProvider getInvalidReservedIpsV6 - */ + #[DataProvider('getInvalidReservedIpsV6')] public function testInvalidReservedIpsV6($ip) { $constraint = new Ip( @@ -452,9 +421,7 @@ public static function getInvalidReservedIpsV6() return self::getInvalidIpsV6(); } - /** - * @dataProvider getInvalidPublicIpsV6 - */ + #[DataProvider('getInvalidPublicIpsV6')] public function testInvalidPublicIpsV6($ip) { $constraint = new Ip( @@ -475,9 +442,7 @@ public static function getInvalidPublicIpsV6() return array_merge(self::getInvalidPrivateIpsV6(), self::getInvalidReservedIpsV6()); } - /** - * @dataProvider getInvalidIpsAll - */ + #[DataProvider('getInvalidIpsAll')] public function testInvalidIpsAll($ip) { $constraint = new Ip( @@ -498,9 +463,7 @@ public static function getInvalidIpsAll() return array_merge(self::getInvalidIpsV4(), self::getInvalidIpsV6()); } - /** - * @dataProvider getInvalidPrivateIpsAll - */ + #[DataProvider('getInvalidPrivateIpsAll')] public function testInvalidPrivateIpsAll($ip) { $constraint = new Ip( @@ -521,9 +484,7 @@ public static function getInvalidPrivateIpsAll() return array_merge(self::getValidPrivateIpsV4(), self::getInvalidPrivateIpsV6()); } - /** - * @dataProvider getInvalidReservedIpsAll - */ + #[DataProvider('getInvalidReservedIpsAll')] public function testInvalidReservedIpsAll($ip) { $constraint = new Ip( @@ -544,9 +505,7 @@ public static function getInvalidReservedIpsAll() return array_merge(self::getValidReservedIpsV4(), self::getInvalidReservedIpsV6()); } - /** - * @dataProvider getInvalidPublicIpsAll - */ + #[DataProvider('getInvalidPublicIpsAll')] public function testInvalidPublicIpsAll($ip) { $constraint = new Ip( diff --git a/Tests/Constraints/IsFalseValidatorTest.php b/Tests/Constraints/IsFalseValidatorTest.php index c6e2ccef6..5e1eae795 100644 --- a/Tests/Constraints/IsFalseValidatorTest.php +++ b/Tests/Constraints/IsFalseValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\IsFalse; use Symfony\Component\Validator\Constraints\IsFalseValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -46,9 +48,8 @@ public function testTrueIsInvalid() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testTrueIsInvalidDoctrineStyle() { $constraint = new IsFalse([ diff --git a/Tests/Constraints/IsNullValidatorTest.php b/Tests/Constraints/IsNullValidatorTest.php index ed6beffc4..556f9aed0 100644 --- a/Tests/Constraints/IsNullValidatorTest.php +++ b/Tests/Constraints/IsNullValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\IsNull; use Symfony\Component\Validator\Constraints\IsNullValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -29,9 +30,7 @@ public function testNullIsValid() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValues($value, $valueAsString) { $constraint = new IsNull(message: 'myMessage'); @@ -44,9 +43,7 @@ public function testInvalidValues($value, $valueAsString) ->assertRaised(); } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValuesNamed($value, $valueAsString) { $constraint = new IsNull(message: 'myMessage'); diff --git a/Tests/Constraints/IsTrueValidatorTest.php b/Tests/Constraints/IsTrueValidatorTest.php index 4a9eb7702..543549314 100644 --- a/Tests/Constraints/IsTrueValidatorTest.php +++ b/Tests/Constraints/IsTrueValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Validator\Constraints\IsTrueValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -46,9 +48,8 @@ public function testFalseIsInvalid() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testFalseIsInvalidDoctrineStyle() { $this->validator->validate(false, new IsTrue([ diff --git a/Tests/Constraints/IsbnTest.php b/Tests/Constraints/IsbnTest.php index d11088376..f11d67d1e 100644 --- a/Tests/Constraints/IsbnTest.php +++ b/Tests/Constraints/IsbnTest.php @@ -24,15 +24,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertNull($aConstraint->type); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(Isbn::ISBN_13, $bConstraint->type); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'IsbnDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/IsbnValidatorTest.php b/Tests/Constraints/IsbnValidatorTest.php index 3ae3864d5..737d9de0c 100644 --- a/Tests/Constraints/IsbnValidatorTest.php +++ b/Tests/Constraints/IsbnValidatorTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\Isbn; use Symfony\Component\Validator\Constraints\IsbnValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -145,9 +148,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), $constraint); } - /** - * @dataProvider getValidIsbn10 - */ + #[DataProvider('getValidIsbn10')] public function testValidIsbn10($isbn) { $constraint = new Isbn(type: 'isbn10'); @@ -157,11 +158,9 @@ public function testValidIsbn10($isbn) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getInvalidIsbn10 - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getInvalidIsbn10')] public function testInvalidIsbn10($isbn, $code) { $constraint = new Isbn([ @@ -190,9 +189,7 @@ public function testInvalidIsbn10Named() ->assertRaised(); } - /** - * @dataProvider getValidIsbn13 - */ + #[DataProvider('getValidIsbn13')] public function testValidIsbn13($isbn) { $constraint = new Isbn(type: 'isbn13'); @@ -202,11 +199,9 @@ public function testValidIsbn13($isbn) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getInvalidIsbn13 - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getInvalidIsbn13')] public function testInvalidIsbn13($isbn, $code) { $constraint = new Isbn([ @@ -222,9 +217,7 @@ public function testInvalidIsbn13($isbn, $code) ->assertRaised(); } - /** - * @dataProvider getInvalidIsbn13 - */ + #[DataProvider('getInvalidIsbn13')] public function testInvalidIsbn13Named($isbn, $code) { $constraint = new Isbn( @@ -240,9 +233,7 @@ public function testInvalidIsbn13Named($isbn, $code) ->assertRaised(); } - /** - * @dataProvider getValidIsbn - */ + #[DataProvider('getValidIsbn')] public function testValidIsbnAny($isbn) { $constraint = new Isbn(); @@ -252,9 +243,7 @@ public function testValidIsbnAny($isbn) $this->assertNoViolation(); } - /** - * @dataProvider getInvalidIsbn10 - */ + #[DataProvider('getInvalidIsbn10')] public function testInvalidIsbnAnyIsbn10($isbn, $code) { $constraint = new Isbn(bothIsbnMessage: 'myMessage'); @@ -272,9 +261,7 @@ public function testInvalidIsbnAnyIsbn10($isbn, $code) ->assertRaised(); } - /** - * @dataProvider getInvalidIsbn13 - */ + #[DataProvider('getInvalidIsbn13')] public function testInvalidIsbnAnyIsbn13($isbn, $code) { $constraint = new Isbn(bothIsbnMessage: 'myMessage'); diff --git a/Tests/Constraints/IsinTest.php b/Tests/Constraints/IsinTest.php index 05d24a32d..d182dfc9a 100644 --- a/Tests/Constraints/IsinTest.php +++ b/Tests/Constraints/IsinTest.php @@ -24,11 +24,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'IsinDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/IsinValidatorTest.php b/Tests/Constraints/IsinValidatorTest.php index b1ac3be20..3ba01a960 100644 --- a/Tests/Constraints/IsinValidatorTest.php +++ b/Tests/Constraints/IsinValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Isin; use Symfony\Component\Validator\Constraints\IsinValidator; use Symfony\Component\Validator\Constraints\Luhn; @@ -37,9 +38,7 @@ public function testEmptyStringIsValid() $this->assertNoViolation(); } - /** - * @dataProvider getValidIsin - */ + #[DataProvider('getValidIsin')] public function testValidIsin($isin) { $this->validator->validate($isin, new Isin()); @@ -63,9 +62,7 @@ public static function getValidIsin() ]; } - /** - * @dataProvider getIsinWithInvalidLenghFormat - */ + #[DataProvider('getIsinWithInvalidLenghFormat')] public function testIsinWithInvalidFormat($isin) { $this->assertViolationRaised($isin, Isin::INVALID_LENGTH_ERROR); @@ -88,9 +85,7 @@ public static function getIsinWithInvalidLenghFormat() ]; } - /** - * @dataProvider getIsinWithInvalidPattern - */ + #[DataProvider('getIsinWithInvalidPattern')] public function testIsinWithInvalidPattern($isin) { $this->assertViolationRaised($isin, Isin::INVALID_PATTERN_ERROR); @@ -106,9 +101,7 @@ public static function getIsinWithInvalidPattern() ]; } - /** - * @dataProvider getIsinWithValidFormatButIncorrectChecksum - */ + #[DataProvider('getIsinWithValidFormatButIncorrectChecksum')] public function testIsinWithValidFormatButIncorrectChecksum($isin) { $this->expectViolationsAt(0, $isin, new Luhn()); diff --git a/Tests/Constraints/IssnTest.php b/Tests/Constraints/IssnTest.php index c21648bd6..e32278a6e 100644 --- a/Tests/Constraints/IssnTest.php +++ b/Tests/Constraints/IssnTest.php @@ -24,17 +24,17 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertFalse($aConstraint->caseSensitive); self::assertFalse($aConstraint->requireHyphen); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertTrue($bConstraint->caseSensitive); self::assertTrue($bConstraint->requireHyphen); self::assertSame(['Default', 'IssnDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/IssnValidatorTest.php b/Tests/Constraints/IssnValidatorTest.php index 6351ab620..bd5844e1e 100644 --- a/Tests/Constraints/IssnValidatorTest.php +++ b/Tests/Constraints/IssnValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Issn; use Symfony\Component\Validator\Constraints\IssnValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -114,9 +115,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), $constraint); } - /** - * @dataProvider getValidLowerCasedIssn - */ + #[DataProvider('getValidLowerCasedIssn')] public function testCaseSensitiveIssns($issn) { $constraint = new Issn( @@ -132,9 +131,7 @@ public function testCaseSensitiveIssns($issn) ->assertRaised(); } - /** - * @dataProvider getValidNonHyphenatedIssn - */ + #[DataProvider('getValidNonHyphenatedIssn')] public function testRequireHyphenIssns($issn) { $constraint = new Issn( @@ -150,9 +147,7 @@ public function testRequireHyphenIssns($issn) ->assertRaised(); } - /** - * @dataProvider getValidIssn - */ + #[DataProvider('getValidIssn')] public function testValidIssn($issn) { $constraint = new Issn(); @@ -162,9 +157,7 @@ public function testValidIssn($issn) $this->assertNoViolation(); } - /** - * @dataProvider getInvalidIssn - */ + #[DataProvider('getInvalidIssn')] public function testInvalidIssn($issn, $code) { $constraint = new Issn(message: 'myMessage'); diff --git a/Tests/Constraints/JsonTest.php b/Tests/Constraints/JsonTest.php index 17208425f..34fdd5b23 100644 --- a/Tests/Constraints/JsonTest.php +++ b/Tests/Constraints/JsonTest.php @@ -24,11 +24,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'JsonDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/JsonValidatorTest.php b/Tests/Constraints/JsonValidatorTest.php index 123cb95fe..50d30c5a3 100644 --- a/Tests/Constraints/JsonValidatorTest.php +++ b/Tests/Constraints/JsonValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Json; use Symfony\Component\Validator\Constraints\JsonValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -22,9 +23,7 @@ protected function createValidator(): JsonValidator return new JsonValidator(); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testJsonIsValid($value) { $this->validator->validate($value, new Json()); @@ -32,9 +31,7 @@ public function testJsonIsValid($value) $this->assertNoViolation(); } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValues($value) { $constraint = new Json(message: 'myMessageTest'); diff --git a/Tests/Constraints/LanguageTest.php b/Tests/Constraints/LanguageTest.php index fe0c5e711..fbc83b4e3 100644 --- a/Tests/Constraints/LanguageTest.php +++ b/Tests/Constraints/LanguageTest.php @@ -24,15 +24,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertFalse($aConstraint->alpha3); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertTrue($bConstraint->alpha3); self::assertSame(['Default', 'LanguageDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/LanguageValidatorTest.php b/Tests/Constraints/LanguageValidatorTest.php index 9522fba75..2e725670f 100644 --- a/Tests/Constraints/LanguageValidatorTest.php +++ b/Tests/Constraints/LanguageValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Validator\Constraints\Language; use Symfony\Component\Validator\Constraints\LanguageValidator; @@ -60,9 +61,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Language()); } - /** - * @dataProvider getValidLanguages - */ + #[DataProvider('getValidLanguages')] public function testValidLanguages($language) { $this->validator->validate($language, new Language()); @@ -78,9 +77,7 @@ public static function getValidLanguages() ]; } - /** - * @dataProvider getInvalidLanguages - */ + #[DataProvider('getInvalidLanguages')] public function testInvalidLanguages($language) { $constraint = new Language(message: 'myMessage'); @@ -101,9 +98,7 @@ public static function getInvalidLanguages() ]; } - /** - * @dataProvider getValidAlpha3Languages - */ + #[DataProvider('getValidAlpha3Languages')] public function testValidAlpha3Languages($language) { $this->validator->validate($language, new Language(alpha3: true)); @@ -120,9 +115,7 @@ public static function getValidAlpha3Languages() ]; } - /** - * @dataProvider getInvalidAlpha3Languages - */ + #[DataProvider('getInvalidAlpha3Languages')] public function testInvalidAlpha3Languages($language) { $constraint = new Language( diff --git a/Tests/Constraints/LengthTest.php b/Tests/Constraints/LengthTest.php index 6e292cb35..b72ecb7f0 100644 --- a/Tests/Constraints/LengthTest.php +++ b/Tests/Constraints/LengthTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -33,9 +35,8 @@ public function testNormalizerCanBeSet() $this->assertEquals('trim', $length->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -43,9 +44,8 @@ public function testInvalidNormalizerThrowsException() new Length(['min' => 0, 'max' => 10, 'normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -95,11 +95,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(42, $aConstraint->min); self::assertSame(42, $aConstraint->max); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(1, $bConstraint->min); self::assertSame(4711, $bConstraint->max); self::assertSame('myMinMessage', $bConstraint->minMessage); @@ -108,7 +108,7 @@ public function testAttributes() self::assertSame('ISO-8859-15', $bConstraint->charset); self::assertSame(['Default', 'LengthDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/LengthValidatorTest.php b/Tests/Constraints/LengthValidatorTest.php index 10f61f50b..21d9b2807 100644 --- a/Tests/Constraints/LengthValidatorTest.php +++ b/Tests/Constraints/LengthValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\LengthValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -40,6 +41,8 @@ public function testEmptyStringIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '""') ->setParameter('{{ limit }}', $limit) + ->setParameter('{{ min }}', $limit) + ->setParameter('{{ max }}', $limit) ->setParameter('{{ value_length }}', 0) ->setInvalidValue('') ->setPlural($limit) @@ -113,10 +116,8 @@ public static function getThreeCharactersWithWhitespaces() ]; } - /** - * @dataProvider getFiveOrMoreCharacters - */ - public function testValidValuesMin(int|string $value) + #[DataProvider('getFiveOrMoreCharacters')] + public function testValidValuesMin(int|string $value, int $valueLength) { $constraint = new Length(min: 5); $this->validator->validate($value, $constraint); @@ -124,10 +125,8 @@ public function testValidValuesMin(int|string $value) $this->assertNoViolation(); } - /** - * @dataProvider getThreeOrLessCharacters - */ - public function testValidValuesMax(int|string $value) + #[DataProvider('getThreeOrLessCharacters')] + public function testValidValuesMax(int|string $value, int $valueLength) { $constraint = new Length(max: 3); $this->validator->validate($value, $constraint); @@ -135,9 +134,7 @@ public function testValidValuesMax(int|string $value) $this->assertNoViolation(); } - /** - * @dataProvider getFourCharacters - */ + #[DataProvider('getFourCharacters')] public function testValidValuesExact(int|string $value) { $constraint = new Length(4); @@ -146,9 +143,7 @@ public function testValidValuesExact(int|string $value) $this->assertNoViolation(); } - /** - * @dataProvider getThreeCharactersWithWhitespaces - */ + #[DataProvider('getThreeCharactersWithWhitespaces')] public function testValidNormalizedValues($value) { $constraint = new Length(min: 3, max: 3, normalizer: 'trim'); @@ -181,9 +176,7 @@ public function testValidBytesValues() $this->assertNoViolation(); } - /** - * @dataProvider getThreeOrLessCharacters - */ + #[DataProvider('getThreeOrLessCharacters')] public function testInvalidValuesMin(int|string $value, int $valueLength) { $constraint = new Length( @@ -196,6 +189,7 @@ public function testInvalidValuesMin(int|string $value, int $valueLength) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ min }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -203,9 +197,7 @@ public function testInvalidValuesMin(int|string $value, int $valueLength) ->assertRaised(); } - /** - * @dataProvider getThreeOrLessCharacters - */ + #[DataProvider('getThreeOrLessCharacters')] public function testInvalidValuesMinNamed(int|string $value, int $valueLength) { $constraint = new Length(min: 4, minMessage: 'myMessage'); @@ -215,6 +207,7 @@ public function testInvalidValuesMinNamed(int|string $value, int $valueLength) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ min }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -222,9 +215,7 @@ public function testInvalidValuesMinNamed(int|string $value, int $valueLength) ->assertRaised(); } - /** - * @dataProvider getFiveOrMoreCharacters - */ + #[DataProvider('getFiveOrMoreCharacters')] public function testInvalidValuesMax(int|string $value, int $valueLength) { $constraint = new Length( @@ -237,6 +228,7 @@ public function testInvalidValuesMax(int|string $value, int $valueLength) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ max }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -244,9 +236,7 @@ public function testInvalidValuesMax(int|string $value, int $valueLength) ->assertRaised(); } - /** - * @dataProvider getFiveOrMoreCharacters - */ + #[DataProvider('getFiveOrMoreCharacters')] public function testInvalidValuesMaxNamed(int|string $value, int $valueLength) { $constraint = new Length(max: 4, maxMessage: 'myMessage'); @@ -256,6 +246,7 @@ public function testInvalidValuesMaxNamed(int|string $value, int $valueLength) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ max }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -263,9 +254,7 @@ public function testInvalidValuesMaxNamed(int|string $value, int $valueLength) ->assertRaised(); } - /** - * @dataProvider getThreeOrLessCharacters - */ + #[DataProvider('getThreeOrLessCharacters')] public function testInvalidValuesExactLessThanFour(int|string $value, int $valueLength) { $constraint = new Length( @@ -279,6 +268,8 @@ public function testInvalidValuesExactLessThanFour(int|string $value, int $value $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ min }}', 4) + ->setParameter('{{ max }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -286,9 +277,7 @@ public function testInvalidValuesExactLessThanFour(int|string $value, int $value ->assertRaised(); } - /** - * @dataProvider getThreeOrLessCharacters - */ + #[DataProvider('getThreeOrLessCharacters')] public function testInvalidValuesExactLessThanFourNamed(int|string $value, int $valueLength) { $constraint = new Length(exactly: 4, exactMessage: 'myMessage'); @@ -298,6 +287,8 @@ public function testInvalidValuesExactLessThanFourNamed(int|string $value, int $ $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ min }}', 4) + ->setParameter('{{ max }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -305,9 +296,7 @@ public function testInvalidValuesExactLessThanFourNamed(int|string $value, int $ ->assertRaised(); } - /** - * @dataProvider getFiveOrMoreCharacters - */ + #[DataProvider('getFiveOrMoreCharacters')] public function testInvalidValuesExactMoreThanFour(int|string $value, int $valueLength) { $constraint = new Length( @@ -321,6 +310,8 @@ public function testInvalidValuesExactMoreThanFour(int|string $value, int $value $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ limit }}', 4) + ->setParameter('{{ min }}', 4) + ->setParameter('{{ max }}', 4) ->setParameter('{{ value_length }}', $valueLength) ->setInvalidValue($value) ->setPlural(4) @@ -328,9 +319,7 @@ public function testInvalidValuesExactMoreThanFour(int|string $value, int $value ->assertRaised(); } - /** - * @dataProvider getOneCharset - */ + #[DataProvider('getOneCharset')] public function testOneCharset($value, $charset, $isValid) { $constraint = new Length( @@ -363,6 +352,8 @@ public function testInvalidValuesExactDefaultCountUnitWithGraphemeInput() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'."A\u{0300}".'"') ->setParameter('{{ limit }}', 1) + ->setParameter('{{ min }}', 1) + ->setParameter('{{ max }}', 1) ->setParameter('{{ value_length }}', 2) ->setInvalidValue("A\u{0300}") ->setPlural(1) @@ -379,6 +370,8 @@ public function testInvalidValuesExactBytesCountUnitWithGraphemeInput() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'."A\u{0300}".'"') ->setParameter('{{ limit }}', 1) + ->setParameter('{{ min }}', 1) + ->setParameter('{{ max }}', 1) ->setParameter('{{ value_length }}', 3) ->setInvalidValue("A\u{0300}") ->setPlural(1) diff --git a/Tests/Constraints/LessThanOrEqualTest.php b/Tests/Constraints/LessThanOrEqualTest.php index c738e8123..1659fd1ec 100644 --- a/Tests/Constraints/LessThanOrEqualTest.php +++ b/Tests/Constraints/LessThanOrEqualTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\LessThanOrEqual; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'LessThanOrEqualDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new LessThanOrEqual(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class LessThanOrEqualDummy diff --git a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php index 685bb58a6..318a33df3 100644 --- a/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php +++ b/Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\LessThanOrEqualValidator; use Symfony\Component\Validator\Constraints\NegativeOrZero; @@ -59,9 +62,8 @@ public static function provideInvalidComparisons(): array ]; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -70,9 +72,8 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new NegativeOrZero(['propertyPath' => 'field']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); @@ -81,9 +82,7 @@ public function testThrowsConstraintExceptionIfValue() return new NegativeOrZero(['value' => 0]); } - /** - * @dataProvider provideInvalidConstraintOptions - */ + #[DataProvider('provideInvalidConstraintOptions')] public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { $this->markTestSkipped('Value option always set for NegativeOrZero constraint'); @@ -104,9 +103,7 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); } - /** - * @dataProvider provideValidComparisonsToPropertyPath - */ + #[DataProvider('provideValidComparisonsToPropertyPath')] public function testValidComparisonToPropertyPath($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint'); diff --git a/Tests/Constraints/LessThanTest.php b/Tests/Constraints/LessThanTest.php index 47dadaf6a..effffba71 100644 --- a/Tests/Constraints/LessThanTest.php +++ b/Tests/Constraints/LessThanTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\LessThan; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'LessThanDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new LessThan(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class LessThanDummy diff --git a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php index 5174a951d..5bb0629f8 100644 --- a/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php +++ b/Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\LessThanValidator; use Symfony\Component\Validator\Constraints\Negative; @@ -58,9 +61,8 @@ public static function provideInvalidComparisons(): array ]; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -69,9 +71,8 @@ public function testThrowsConstraintExceptionIfPropertyPath() return new Negative(['propertyPath' => 'field']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfValue() { $this->expectException(ConstraintDefinitionException::class); @@ -80,9 +81,7 @@ public function testThrowsConstraintExceptionIfValue() return new Negative(['value' => 0]); } - /** - * @dataProvider provideInvalidConstraintOptions - */ + #[DataProvider('provideInvalidConstraintOptions')] public function testThrowsConstraintExceptionIfNoValueOrPropertyPath($options) { $this->markTestSkipped('Value option always set for Negative constraint'); @@ -103,9 +102,7 @@ public function testInvalidValuePath() $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); } - /** - * @dataProvider provideValidComparisonsToPropertyPath - */ + #[DataProvider('provideValidComparisonsToPropertyPath')] public function testValidComparisonToPropertyPath($comparedValue) { $this->markTestSkipped('PropertyPath option is not used in Negative constraint'); diff --git a/Tests/Constraints/LocaleTest.php b/Tests/Constraints/LocaleTest.php index b6beb13dd..d0664cda7 100644 --- a/Tests/Constraints/LocaleTest.php +++ b/Tests/Constraints/LocaleTest.php @@ -24,15 +24,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertTrue($aConstraint->canonicalize); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertFalse($bConstraint->canonicalize); self::assertSame(['Default', 'LocaleDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/LocaleValidatorTest.php b/Tests/Constraints/LocaleValidatorTest.php index 3b3819512..56253d3ac 100644 --- a/Tests/Constraints/LocaleValidatorTest.php +++ b/Tests/Constraints/LocaleValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Locale; use Symfony\Component\Validator\Constraints\LocaleValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -43,9 +44,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Locale()); } - /** - * @dataProvider getValidLocales - */ + #[DataProvider('getValidLocales')] public function testValidLocales($locale) { $this->validator->validate($locale, new Locale()); @@ -66,9 +65,7 @@ public static function getValidLocales() ]; } - /** - * @dataProvider getInvalidLocales - */ + #[DataProvider('getInvalidLocales')] public function testInvalidLocales($locale) { $constraint = new Locale(message: 'myMessage'); @@ -102,9 +99,7 @@ public function testTooLongLocale() ->assertRaised(); } - /** - * @dataProvider getUncanonicalizedLocales - */ + #[DataProvider('getUncanonicalizedLocales')] public function testValidLocalesWithCanonicalization(string $locale) { $constraint = new Locale(message: 'myMessage'); @@ -114,9 +109,7 @@ public function testValidLocalesWithCanonicalization(string $locale) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocales - */ + #[DataProvider('getValidLocales')] public function testValidLocalesWithoutCanonicalization(string $locale) { $constraint = new Locale( @@ -129,9 +122,7 @@ public function testValidLocalesWithoutCanonicalization(string $locale) $this->assertNoViolation(); } - /** - * @dataProvider getUncanonicalizedLocales - */ + #[DataProvider('getUncanonicalizedLocales')] public function testInvalidLocalesWithoutCanonicalization(string $locale) { $constraint = new Locale( diff --git a/Tests/Constraints/LuhnTest.php b/Tests/Constraints/LuhnTest.php index f144785d2..ea9aecda1 100644 --- a/Tests/Constraints/LuhnTest.php +++ b/Tests/Constraints/LuhnTest.php @@ -24,11 +24,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'LuhnDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/LuhnValidatorTest.php b/Tests/Constraints/LuhnValidatorTest.php index 9eb33bde6..1f946c22d 100644 --- a/Tests/Constraints/LuhnValidatorTest.php +++ b/Tests/Constraints/LuhnValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Luhn; use Symfony\Component\Validator\Constraints\LuhnValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -37,9 +38,7 @@ public function testEmptyStringIsValid() $this->assertNoViolation(); } - /** - * @dataProvider getValidNumbers - */ + #[DataProvider('getValidNumbers')] public function testValidNumbers($number) { $this->validator->validate($number, new Luhn()); @@ -71,9 +70,7 @@ public static function getValidNumbers() ]; } - /** - * @dataProvider getInvalidNumbers - */ + #[DataProvider('getInvalidNumbers')] public function testInvalidNumbers($number, $code) { $constraint = new Luhn(message: 'myMessage'); @@ -97,9 +94,7 @@ public static function getInvalidNumbers() ]; } - /** - * @dataProvider getInvalidTypes - */ + #[DataProvider('getInvalidTypes')] public function testInvalidTypes($number) { $this->expectException(UnexpectedValueException::class); diff --git a/Tests/Constraints/MacAddressTest.php b/Tests/Constraints/MacAddressTest.php index 94cbfedd4..77c106883 100644 --- a/Tests/Constraints/MacAddressTest.php +++ b/Tests/Constraints/MacAddressTest.php @@ -34,17 +34,17 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->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(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(MacAddress::LOCAL_UNICAST, $bConstraint->type); self::assertSame(['Default', 'MacAddressDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/MacAddressValidatorTest.php b/Tests/Constraints/MacAddressValidatorTest.php index 5abb7487b..84ff05cb6 100644 --- a/Tests/Constraints/MacAddressValidatorTest.php +++ b/Tests/Constraints/MacAddressValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\MacAddress; use Symfony\Component\Validator\Constraints\MacAddressValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -53,9 +54,7 @@ public function testInvalidValidatorType() new MacAddress(type: 666); } - /** - * @dataProvider getValidMacs - */ + #[DataProvider('getValidMacs')] public function testValidMac($mac) { $this->validator->validate($mac, new MacAddress()); @@ -63,9 +62,7 @@ public function testValidMac($mac) $this->assertNoViolation(); } - /** - * @dataProvider getNotValidMacs - */ + #[DataProvider('getNotValidMacs')] public function testNotValidMac($mac) { $this->validator->validate($mac, new MacAddress()); @@ -153,12 +150,10 @@ public static function getValidBroadcastMacs(): array ]; } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testValidAllNoBroadcastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::ALL_NO_BROADCAST)); @@ -166,9 +161,7 @@ public function testValidAllNoBroadcastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidBroadcastMacs')] public function testInvalidAllNoBroadcastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::ALL_NO_BROADCAST); @@ -181,11 +174,9 @@ public function testInvalidAllNoBroadcastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidBroadcastMacs')] public function testValidLocalMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_ALL)); @@ -193,10 +184,8 @@ public function testValidLocalMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testInvalidLocalMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_ALL); @@ -209,10 +198,8 @@ public function testInvalidLocalMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] public function testValidLocalNoBroadcastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_NO_BROADCAST)); @@ -220,11 +207,9 @@ public function testValidLocalNoBroadcastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] + #[DataProvider('getValidBroadcastMacs')] public function testInvalidLocalNoBroadcastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_NO_BROADCAST); @@ -237,9 +222,7 @@ public function testInvalidLocalNoBroadcastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalUnicastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] public function testValidLocalUnicastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_UNICAST)); @@ -247,11 +230,9 @@ public function testValidLocalUnicastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testInvalidLocalUnicastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_UNICAST); @@ -264,10 +245,8 @@ public function testInvalidLocalUnicastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidBroadcastMacs')] public function testValidLocalMulticastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_MULTICAST)); @@ -275,11 +254,9 @@ public function testValidLocalMulticastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testInvalidLocalMulticastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_MULTICAST); @@ -292,9 +269,7 @@ public function testInvalidLocalMulticastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalMulticastMacs - */ + #[DataProvider('getValidLocalMulticastMacs')] public function testValidLocalMulticastNoBroadcastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::LOCAL_MULTICAST_NO_BROADCAST)); @@ -302,12 +277,10 @@ public function testValidLocalMulticastNoBroadcastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] + #[DataProvider('getValidBroadcastMacs')] public function testInvalidLocalMulticastNoBroadcastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::LOCAL_MULTICAST_NO_BROADCAST); @@ -320,10 +293,8 @@ public function testInvalidLocalMulticastNoBroadcastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testValidUniversalMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::UNIVERSAL_ALL)); @@ -331,10 +302,8 @@ public function testValidUniversalMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] public function testInvalidUniversalMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::UNIVERSAL_ALL); @@ -347,9 +316,7 @@ public function testInvalidUniversalMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidUniversalUnicastMacs - */ + #[DataProvider('getValidUniversalUnicastMacs')] public function testValidUniversalUnicastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::UNIVERSAL_UNICAST)); @@ -357,11 +324,9 @@ public function testValidUniversalUnicastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testInvalidUniversalUnicastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::UNIVERSAL_UNICAST); @@ -374,9 +339,7 @@ public function testInvalidUniversalUnicastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidUniversalMulticastMacs')] public function testValidUniversalMulticastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::UNIVERSAL_MULTICAST)); @@ -384,11 +347,9 @@ public function testValidUniversalMulticastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalUnicastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] public function testInvalidUniversalMulticastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::UNIVERSAL_MULTICAST); @@ -401,10 +362,8 @@ public function testInvalidUniversalMulticastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidUniversalUnicastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] public function testUnicastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::UNICAST_ALL)); @@ -412,10 +371,8 @@ public function testUnicastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testInvalidUnicastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::UNICAST_ALL); @@ -428,11 +385,9 @@ public function testInvalidUnicastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalMulticastMacs - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] + #[DataProvider('getValidBroadcastMacs')] public function testMulticastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::MULTICAST_ALL)); @@ -440,10 +395,8 @@ public function testMulticastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidUniversalUnicastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] public function testInvalidMulticastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::MULTICAST_ALL); @@ -456,10 +409,8 @@ public function testInvalidMulticastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testMulticastNoBroadcastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::MULTICAST_NO_BROADCAST)); @@ -467,11 +418,9 @@ public function testMulticastNoBroadcastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidBroadcastMacs')] public function testInvalidMulticastNoBroadcastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::MULTICAST_NO_BROADCAST); @@ -484,9 +433,7 @@ public function testInvalidMulticastNoBroadcastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidBroadcastMacs - */ + #[DataProvider('getValidBroadcastMacs')] public function testBroadcastMacs($mac) { $this->validator->validate($mac, new MacAddress(type: MacAddress::BROADCAST)); @@ -494,12 +441,10 @@ public function testBroadcastMacs($mac) $this->assertNoViolation(); } - /** - * @dataProvider getValidLocalUnicastMacs - * @dataProvider getValidLocalMulticastMacs - * @dataProvider getValidUniversalUnicastMacs - * @dataProvider getValidUniversalMulticastMacs - */ + #[DataProvider('getValidLocalUnicastMacs')] + #[DataProvider('getValidLocalMulticastMacs')] + #[DataProvider('getValidUniversalUnicastMacs')] + #[DataProvider('getValidUniversalMulticastMacs')] public function testInvalidBroadcastMacs($mac) { $constraint = new MacAddress('myMessage', type: MacAddress::BROADCAST); @@ -512,9 +457,7 @@ public function testInvalidBroadcastMacs($mac) ->assertRaised(); } - /** - * @dataProvider getValidMacsWithWhitespaces - */ + #[DataProvider('getValidMacsWithWhitespaces')] public function testValidMacsWithWhitespaces($mac) { $this->validator->validate($mac, new MacAddress(normalizer: 'trim')); @@ -534,9 +477,7 @@ public static function getValidMacsWithWhitespaces(): array ]; } - /** - * @dataProvider getInvalidMacs - */ + #[DataProvider('getInvalidMacs')] public function testInvalidMacs($mac) { $constraint = new MacAddress('myMessage'); diff --git a/Tests/Constraints/NegativeOrZeroTest.php b/Tests/Constraints/NegativeOrZeroTest.php index 109713f07..40920a7be 100644 --- a/Tests/Constraints/NegativeOrZeroTest.php +++ b/Tests/Constraints/NegativeOrZeroTest.php @@ -24,12 +24,12 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(0, $aConstraint->value); self::assertNull($aConstraint->propertyPath); self::assertSame(['Default', 'NegativeOrZeroDummy'], $aConstraint->groups); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['foo'], $bConstraint->groups); } diff --git a/Tests/Constraints/NegativeTest.php b/Tests/Constraints/NegativeTest.php index 4a3c67da9..5e040f643 100644 --- a/Tests/Constraints/NegativeTest.php +++ b/Tests/Constraints/NegativeTest.php @@ -24,12 +24,12 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(0, $aConstraint->value); self::assertNull($aConstraint->propertyPath); self::assertSame(['Default', 'NegativeDummy'], $aConstraint->groups); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['foo'], $bConstraint->groups); } diff --git a/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php b/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php index c38a431f5..57c9fd0a9 100644 --- a/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php +++ b/Tests/Constraints/NoSuspiciousCharactersValidatorTest.php @@ -11,15 +11,16 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Symfony\Component\Validator\Constraints\NoSuspiciousCharacters; use Symfony\Component\Validator\Constraints\NoSuspiciousCharactersValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; /** - * @requires extension intl - * * @extends ConstraintValidatorTestCase */ +#[RequiresPhpExtension('intl')] class NoSuspiciousCharactersValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): NoSuspiciousCharactersValidator @@ -27,9 +28,7 @@ protected function createValidator(): NoSuspiciousCharactersValidator return new NoSuspiciousCharactersValidator(); } - /** - * @dataProvider provideNonSuspiciousStrings - */ + #[DataProvider('provideNonSuspiciousStrings')] public function testNonSuspiciousStrings(string $string, array $options = []) { $this->validator->validate($string, new NoSuspiciousCharacters(...$options)); @@ -53,9 +52,7 @@ public static function provideNonSuspiciousStrings(): iterable ]; } - /** - * @dataProvider provideSuspiciousStrings - */ + #[DataProvider('provideSuspiciousStrings')] public function testSuspiciousStrings(string $string, array $options, array $errors) { $this->validator->validate($string, new NoSuspiciousCharacters(...$options)); diff --git a/Tests/Constraints/NotBlankTest.php b/Tests/Constraints/NotBlankTest.php index d04a65f1c..60ff878c3 100644 --- a/Tests/Constraints/NotBlankTest.php +++ b/Tests/Constraints/NotBlankTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -35,19 +37,18 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertFalse($aConstraint->allowNull); self::assertNull($aConstraint->normalizer); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertTrue($bConstraint->allowNull); self::assertSame('trim', $bConstraint->normalizer); self::assertSame('myMessage', $bConstraint->message); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -55,9 +56,8 @@ public function testInvalidNormalizerThrowsException() new NotBlank(['normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/NotBlankValidatorTest.php b/Tests/Constraints/NotBlankValidatorTest.php index 42d5f3a60..274a4d6cf 100644 --- a/Tests/Constraints/NotBlankValidatorTest.php +++ b/Tests/Constraints/NotBlankValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotBlankValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -22,9 +23,7 @@ protected function createValidator(): NotBlankValidator return new NotBlankValidator(); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues($value) { $this->validator->validate($value, new NotBlank()); @@ -117,9 +116,7 @@ public function testAllowNullFalse() ->assertRaised(); } - /** - * @dataProvider getWhitespaces - */ + #[DataProvider('getWhitespaces')] public function testNormalizedStringIsInvalid($value) { $constraint = new NotBlank( diff --git a/Tests/Constraints/NotCompromisedPasswordTest.php b/Tests/Constraints/NotCompromisedPasswordTest.php index 0ca6dcb2a..3be19c404 100644 --- a/Tests/Constraints/NotCompromisedPasswordTest.php +++ b/Tests/Constraints/NotCompromisedPasswordTest.php @@ -34,17 +34,17 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(1, $aConstraint->threshold); self::assertFalse($aConstraint->skipOnError); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(42, $bConstraint->threshold); self::assertTrue($bConstraint->skipOnError); self::assertSame(['Default', 'NotCompromisedPasswordDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php index 11c325d53..7b9d7f49f 100644 --- a/Tests/Constraints/NotCompromisedPasswordValidatorTest.php +++ b/Tests/Constraints/NotCompromisedPasswordValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\Luhn; use Symfony\Component\Validator\Constraints\NotCompromisedPassword; use Symfony\Component\Validator\Constraints\NotCompromisedPasswordValidator; @@ -102,9 +104,8 @@ public function testThresholdNotReached() $this->assertNoViolation(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThresholdNotReachedDoctrineStyle() { $this->validator->validate(self::PASSWORD_LEAKED, new NotCompromisedPassword(['threshold' => 10])); @@ -216,9 +217,8 @@ public function testApiErrorSkipped() $this->validator->validate(self::PASSWORD_TRIGGERING_AN_ERROR, new NotCompromisedPassword(skipOnError: true)); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testApiErrorSkippedDoctrineStyle() { $this->expectNotToPerformAssertions(); diff --git a/Tests/Constraints/NotEqualToTest.php b/Tests/Constraints/NotEqualToTest.php index ff36b95cb..941348e8e 100644 --- a/Tests/Constraints/NotEqualToTest.php +++ b/Tests/Constraints/NotEqualToTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\NotEqualTo; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'NotEqualToDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new NotEqualTo(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class NotEqualToDummy diff --git a/Tests/Constraints/NotIdenticalToTest.php b/Tests/Constraints/NotIdenticalToTest.php index de34f05eb..e9714f9c9 100644 --- a/Tests/Constraints/NotIdenticalToTest.php +++ b/Tests/Constraints/NotIdenticalToTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\NotIdenticalTo; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -24,21 +26,30 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(2, $aConstraint->value); self::assertNull($aConstraint->propertyPath); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(4711, $bConstraint->value); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'NotIdenticalToDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertNull($cConstraint->value); self::assertSame('b', $cConstraint->propertyPath); self::assertSame('myMessage', $cConstraint->message); self::assertSame(['foo'], $cConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new NotIdenticalTo(['value' => 5]); + + $this->assertSame(5, $constraint->value); + } } class NotIdenticalToDummy diff --git a/Tests/Constraints/NotNullValidatorTest.php b/Tests/Constraints/NotNullValidatorTest.php index fec2ec12a..5468fc3b3 100644 --- a/Tests/Constraints/NotNullValidatorTest.php +++ b/Tests/Constraints/NotNullValidatorTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\NotNullValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -22,9 +25,7 @@ protected function createValidator(): NotNullValidator return new NotNullValidator(); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues($value) { $this->validator->validate($value, new NotNull()); @@ -52,9 +53,8 @@ public function testNullIsInvalid() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testNullIsInvalidDoctrineStyle() { $this->validator->validate(null, new NotNull([ diff --git a/Tests/Constraints/PasswordStrengthValidatorTest.php b/Tests/Constraints/PasswordStrengthValidatorTest.php index fb063f4a7..c96976c87 100644 --- a/Tests/Constraints/PasswordStrengthValidatorTest.php +++ b/Tests/Constraints/PasswordStrengthValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\PasswordStrength; use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -23,9 +24,7 @@ protected function createValidator(): PasswordStrengthValidator return new PasswordStrengthValidator(); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues(string|\Stringable $value, int $expectedStrength) { $this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength)); @@ -53,9 +52,7 @@ public static function getValidValues(): iterable yield [new StringableValue('How-is-this'), PasswordStrength::STRENGTH_WEAK]; } - /** - * @dataProvider provideInvalidConstraints - */ + #[DataProvider('provideInvalidConstraints')] public function testThePasswordIsWeak(PasswordStrength $constraint, string $password, string $expectedMessage, string $expectedCode, string $strength) { $this->validator->validate($password, $constraint); @@ -93,9 +90,7 @@ public static function provideInvalidConstraints(): iterable ]; } - /** - * @dataProvider getPasswordValues - */ + #[DataProvider('getPasswordValues')] public function testStrengthEstimator(string $password, int $expectedStrength) { self::assertSame($expectedStrength, PasswordStrengthValidator::estimateStrength((string) $password)); diff --git a/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php b/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php index 3d24e8c5b..33347e6fb 100644 --- a/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php +++ b/Tests/Constraints/PasswordStrengthValidatorWithClosureTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\PasswordStrength; use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -33,9 +34,7 @@ protected function createValidator(): PasswordStrengthValidator }); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues(string|\Stringable $value, int $expectedStrength) { $this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength)); @@ -62,9 +61,7 @@ public static function getValidValues(): iterable yield [new StringableValue('HeloW0rld'), PasswordStrength::STRENGTH_WEAK]; } - /** - * @dataProvider provideInvalidConstraints - */ + #[DataProvider('provideInvalidConstraints')] public function testThePasswordIsWeak(PasswordStrength $constraint, string $password, string $expectedMessage, string $expectedCode, string $strength) { $this->validator->validate($password, $constraint); diff --git a/Tests/Constraints/PositiveOrZeroTest.php b/Tests/Constraints/PositiveOrZeroTest.php index b13c803aa..847e41816 100644 --- a/Tests/Constraints/PositiveOrZeroTest.php +++ b/Tests/Constraints/PositiveOrZeroTest.php @@ -24,12 +24,12 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(0, $aConstraint->value); self::assertNull($aConstraint->propertyPath); self::assertSame(['Default', 'PositiveOrZeroDummy'], $aConstraint->groups); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['foo'], $bConstraint->groups); } diff --git a/Tests/Constraints/PositiveTest.php b/Tests/Constraints/PositiveTest.php index f06e61653..ee88893e5 100644 --- a/Tests/Constraints/PositiveTest.php +++ b/Tests/Constraints/PositiveTest.php @@ -24,12 +24,12 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(0, $aConstraint->value); self::assertNull($aConstraint->propertyPath); self::assertSame(['Default', 'PositiveDummy'], $aConstraint->groups); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['foo'], $bConstraint->groups); } diff --git a/Tests/Constraints/RangeTest.php b/Tests/Constraints/RangeTest.php index 01481e8bc..7847285fa 100644 --- a/Tests/Constraints/RangeTest.php +++ b/Tests/Constraints/RangeTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -18,9 +20,8 @@ class RangeTest extends TestCase { - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfBothMinLimitAndPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -38,9 +39,8 @@ public function testThrowsConstraintExceptionIfBothMinLimitAndPropertyPathNamed( new Range(min: 'min', minPropertyPath: 'minPropertyPath'); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintExceptionIfBothMaxLimitAndPropertyPath() { $this->expectException(ConstraintDefinitionException::class); @@ -92,9 +92,8 @@ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMaxMess new Range(min: 'min', max: 'max', maxMessage: 'maxMessage'); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMessageAndMaxMessageOptions() { $this->expectException(ConstraintDefinitionException::class); @@ -107,9 +106,8 @@ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMess ]); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMessageOptions() { $this->expectException(ConstraintDefinitionException::class); @@ -121,9 +119,8 @@ public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMinMess ]); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testThrowsConstraintDefinitionExceptionIfBothMinAndMaxAndMaxMessageOptions() { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/RangeValidatorTest.php b/Tests/Constraints/RangeValidatorTest.php index 423c8d460..5e13a8cb3 100644 --- a/Tests/Constraints/RangeValidatorTest.php +++ b/Tests/Constraints/RangeValidatorTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Constraints\RangeValidator; @@ -69,11 +72,9 @@ public static function getMoreThanTwenty(): array ]; } - /** - * @group legacy - * - * @dataProvider getTenToTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getTenToTwenty')] public function testValidValuesMin($value) { $constraint = new Range(['min' => 10]); @@ -82,9 +83,7 @@ public function testValidValuesMin($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMinNamed($value) { $constraint = new Range(min: 10); @@ -93,11 +92,9 @@ public function testValidValuesMinNamed($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getTenToTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getTenToTwenty')] public function testValidValuesMax($value) { $constraint = new Range(['max' => 20]); @@ -106,9 +103,7 @@ public function testValidValuesMax($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMaxNamed($value) { $constraint = new Range(max: 20); @@ -117,11 +112,9 @@ public function testValidValuesMaxNamed($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getTenToTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getTenToTwenty')] public function testValidValuesMinMax($value) { $constraint = new Range(['min' => 10, 'max' => 20]); @@ -130,9 +123,7 @@ public function testValidValuesMinMax($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMinMaxNamed($value) { $constraint = new Range(min: 10, max: 20); @@ -141,11 +132,9 @@ public function testValidValuesMinMaxNamed($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getLessThanTen - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getLessThanTen')] public function testInvalidValuesMin($value, $formattedValue) { $constraint = new Range([ @@ -162,9 +151,7 @@ public function testInvalidValuesMin($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getLessThanTen - */ + #[DataProvider('getLessThanTen')] public function testInvalidValuesMinNamed($value, $formattedValue) { $constraint = new Range(min: 10, minMessage: 'myMessage'); @@ -178,11 +165,9 @@ public function testInvalidValuesMinNamed($value, $formattedValue) ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getMoreThanTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesMax($value, $formattedValue) { $constraint = new Range([ @@ -199,9 +184,7 @@ public function testInvalidValuesMax($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getMoreThanTwenty - */ + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesMaxNamed($value, $formattedValue) { $constraint = new Range(max: 20, maxMessage: 'myMessage'); @@ -215,11 +198,9 @@ public function testInvalidValuesMaxNamed($value, $formattedValue) ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getMoreThanTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesCombinedMax($value, $formattedValue) { $constraint = new Range([ @@ -238,9 +219,7 @@ public function testInvalidValuesCombinedMax($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getMoreThanTwenty - */ + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesCombinedMaxNamed($value, $formattedValue) { $constraint = new Range(min: 10, max: 20, notInRangeMessage: 'myNotInRangeMessage'); @@ -255,11 +234,9 @@ public function testInvalidValuesCombinedMaxNamed($value, $formattedValue) ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getLessThanTen - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getLessThanTen')] public function testInvalidValuesCombinedMin($value, $formattedValue) { $constraint = new Range([ @@ -278,9 +255,7 @@ public function testInvalidValuesCombinedMin($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getLessThanTen - */ + #[DataProvider('getLessThanTen')] public function testInvalidValuesCombinedMinNamed($value, $formattedValue) { $constraint = new Range(min: 10, max: 20, notInRangeMessage: 'myNotInRangeMessage'); @@ -354,9 +329,7 @@ public static function getLaterThanTwentiethMarch2014(): array return $tests; } - /** - * @dataProvider getTenthToTwentiethMarch2014 - */ + #[DataProvider('getTenthToTwentiethMarch2014')] public function testValidDatesMin($value) { $constraint = new Range(min: 'March 10, 2014'); @@ -365,9 +338,7 @@ public function testValidDatesMin($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenthToTwentiethMarch2014 - */ + #[DataProvider('getTenthToTwentiethMarch2014')] public function testValidDatesMax($value) { $constraint = new Range(max: 'March 20, 2014'); @@ -376,9 +347,7 @@ public function testValidDatesMax($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenthToTwentiethMarch2014 - */ + #[DataProvider('getTenthToTwentiethMarch2014')] public function testValidDatesMinMax($value) { $constraint = new Range(min: 'March 10, 2014', max: 'March 20, 2014'); @@ -387,9 +356,7 @@ public function testValidDatesMinMax($value) $this->assertNoViolation(); } - /** - * @dataProvider getSoonerThanTenthMarch2014 - */ + #[DataProvider('getSoonerThanTenthMarch2014')] public function testInvalidDatesMin(\DateTimeInterface $value, string $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -410,9 +377,7 @@ public function testInvalidDatesMin(\DateTimeInterface $value, string $dateTimeA ->assertRaised(); } - /** - * @dataProvider getLaterThanTwentiethMarch2014 - */ + #[DataProvider('getLaterThanTwentiethMarch2014')] public function testInvalidDatesMax(\DateTimeInterface $value, string $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -433,9 +398,7 @@ public function testInvalidDatesMax(\DateTimeInterface $value, string $dateTimeA ->assertRaised(); } - /** - * @dataProvider getLaterThanTwentiethMarch2014 - */ + #[DataProvider('getLaterThanTwentiethMarch2014')] public function testInvalidDatesCombinedMax(\DateTimeInterface $value, string $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -458,9 +421,7 @@ public function testInvalidDatesCombinedMax(\DateTimeInterface $value, string $d ->assertRaised(); } - /** - * @dataProvider getSoonerThanTenthMarch2014 - */ + #[DataProvider('getSoonerThanTenthMarch2014')] public function testInvalidDatesCombinedMin($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -597,9 +558,7 @@ public function testNonNumericWithNonParsableDatetimeMinAndMax() ->assertRaised(); } - /** - * @dataProvider throwsOnInvalidStringDatesProvider - */ + #[DataProvider('throwsOnInvalidStringDatesProvider')] public function testThrowsOnInvalidStringDates($expectedMessage, $value, $min, $max) { $this->expectException(ConstraintDefinitionException::class); @@ -634,11 +593,9 @@ public function testNoViolationOnNullObjectWithPropertyPaths() $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getTenToTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getTenToTwenty')] public function testValidValuesMinPropertyPath($value) { $this->setObject(new Limit(10)); @@ -650,9 +607,7 @@ public function testValidValuesMinPropertyPath($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMinPropertyPathNamed($value) { $this->setObject(new Limit(10)); @@ -662,9 +617,7 @@ public function testValidValuesMinPropertyPathNamed($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMaxPropertyPath($value) { $this->setObject(new Limit(20)); @@ -676,9 +629,7 @@ public function testValidValuesMaxPropertyPath($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMaxPropertyPathNamed($value) { $this->setObject(new Limit(20)); @@ -688,9 +639,7 @@ public function testValidValuesMaxPropertyPathNamed($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenToTwenty - */ + #[DataProvider('getTenToTwenty')] public function testValidValuesMinMaxPropertyPath($value) { $this->setObject(new MinMax(10, 20)); @@ -703,9 +652,7 @@ public function testValidValuesMinMaxPropertyPath($value) $this->assertNoViolation(); } - /** - * @dataProvider getLessThanTen - */ + #[DataProvider('getLessThanTen')] public function testInvalidValuesMinPropertyPath($value, $formattedValue) { $this->setObject(new Limit(10)); @@ -725,9 +672,7 @@ public function testInvalidValuesMinPropertyPath($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getMoreThanTwenty - */ + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesMaxPropertyPath($value, $formattedValue) { $this->setObject(new Limit(20)); @@ -747,11 +692,9 @@ public function testInvalidValuesMaxPropertyPath($value, $formattedValue) ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getMoreThanTwenty - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesCombinedMaxPropertyPath($value, $formattedValue) { $this->setObject(new MinMax(10, 20)); @@ -774,9 +717,7 @@ public function testInvalidValuesCombinedMaxPropertyPath($value, $formattedValue ->assertRaised(); } - /** - * @dataProvider getMoreThanTwenty - */ + #[DataProvider('getMoreThanTwenty')] public function testInvalidValuesCombinedMaxPropertyPathNamed($value, $formattedValue) { $this->setObject(new MinMax(10, 20)); @@ -799,11 +740,9 @@ public function testInvalidValuesCombinedMaxPropertyPathNamed($value, $formatted ->assertRaised(); } - /** - * @group legacy - * - * @dataProvider getLessThanTen - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getLessThanTen')] public function testInvalidValuesCombinedMinPropertyPath($value, $formattedValue) { $this->setObject(new MinMax(10, 20)); @@ -826,9 +765,7 @@ public function testInvalidValuesCombinedMinPropertyPath($value, $formattedValue ->assertRaised(); } - /** - * @dataProvider getLessThanTen - */ + #[DataProvider('getLessThanTen')] public function testInvalidValuesCombinedMinPropertyPathNamed($value, $formattedValue) { $this->setObject(new MinMax(10, 20)); @@ -851,9 +788,7 @@ public function testInvalidValuesCombinedMinPropertyPathNamed($value, $formatted ->assertRaised(); } - /** - * @dataProvider getLessThanTen - */ + #[DataProvider('getLessThanTen')] public function testViolationOnNullObjectWithDefinedMin($value, $formattedValue) { $this->setObject(null); @@ -872,9 +807,7 @@ public function testViolationOnNullObjectWithDefinedMin($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getMoreThanTwenty - */ + #[DataProvider('getMoreThanTwenty')] public function testViolationOnNullObjectWithDefinedMax($value, $formattedValue) { $this->setObject(null); @@ -893,9 +826,7 @@ public function testViolationOnNullObjectWithDefinedMax($value, $formattedValue) ->assertRaised(); } - /** - * @dataProvider getTenthToTwentiethMarch2014 - */ + #[DataProvider('getTenthToTwentiethMarch2014')] public function testValidDatesMinPropertyPath($value) { $this->setObject(new Limit('March 10, 2014')); @@ -905,9 +836,7 @@ public function testValidDatesMinPropertyPath($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenthToTwentiethMarch2014 - */ + #[DataProvider('getTenthToTwentiethMarch2014')] public function testValidDatesMaxPropertyPath($value) { $this->setObject(new Limit('March 20, 2014')); @@ -918,9 +847,7 @@ public function testValidDatesMaxPropertyPath($value) $this->assertNoViolation(); } - /** - * @dataProvider getTenthToTwentiethMarch2014 - */ + #[DataProvider('getTenthToTwentiethMarch2014')] public function testValidDatesMinMaxPropertyPath($value) { $this->setObject(new MinMax('March 10, 2014', 'March 20, 2014')); @@ -931,9 +858,7 @@ public function testValidDatesMinMaxPropertyPath($value) $this->assertNoViolation(); } - /** - * @dataProvider getSoonerThanTenthMarch2014 - */ + #[DataProvider('getSoonerThanTenthMarch2014')] public function testInvalidDatesMinPropertyPath($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -957,9 +882,7 @@ public function testInvalidDatesMinPropertyPath($value, $dateTimeAsString) ->assertRaised(); } - /** - * @dataProvider getLaterThanTwentiethMarch2014 - */ + #[DataProvider('getLaterThanTwentiethMarch2014')] public function testInvalidDatesMaxPropertyPath($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -983,9 +906,7 @@ public function testInvalidDatesMaxPropertyPath($value, $dateTimeAsString) ->assertRaised(); } - /** - * @dataProvider getLaterThanTwentiethMarch2014 - */ + #[DataProvider('getLaterThanTwentiethMarch2014')] public function testInvalidDatesCombinedMaxPropertyPath($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -1012,9 +933,7 @@ public function testInvalidDatesCombinedMaxPropertyPath($value, $dateTimeAsStrin ->assertRaised(); } - /** - * @dataProvider getSoonerThanTenthMarch2014 - */ + #[DataProvider('getSoonerThanTenthMarch2014')] public function testInvalidDatesCombinedMinPropertyPath($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions @@ -1083,9 +1002,7 @@ public static function provideMessageIfMinAndMaxSet(): array ]; } - /** - * @dataProvider provideMessageIfMinAndMaxSet - */ + #[DataProvider('provideMessageIfMinAndMaxSet')] public function testMessageIfMinAndMaxSet(array $constraintExtraOptions, int $value, string $expectedMessage, string $expectedCode) { $constraint = new Range(...array_merge(['min' => 1, 'max' => 10], $constraintExtraOptions)); diff --git a/Tests/Constraints/RegexTest.php b/Tests/Constraints/RegexTest.php index 853e0d785..6fabc4958 100644 --- a/Tests/Constraints/RegexTest.php +++ b/Tests/Constraints/RegexTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Regex; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -65,9 +68,7 @@ public static function provideHtmlPatterns() ]; } - /** - * @dataProvider provideHtmlPatterns - */ + #[DataProvider('provideHtmlPatterns')] public function testGetHtmlPattern($pattern, $htmlPattern, $match = true) { $constraint = new Regex( @@ -97,9 +98,8 @@ public function testNormalizerCanBeSet() $this->assertEquals('trim', $regex->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -107,9 +107,8 @@ public function testInvalidNormalizerThrowsException() new Regex(['pattern' => '/^[0-9]+$/', 'normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -123,19 +122,19 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame('/^[0-9]+$/', $aConstraint->pattern); self::assertTrue($aConstraint->match); self::assertNull($aConstraint->normalizer); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame('/^[0-9]+$/', $bConstraint->pattern); self::assertSame('[0-9]+', $bConstraint->htmlPattern); self::assertFalse($bConstraint->match); self::assertSame(['Default', 'RegexDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } @@ -148,9 +147,8 @@ public function testMissingPattern() new Regex(null); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testMissingPatternDoctrineStyle() { $this->expectException(MissingOptionsException::class); @@ -158,6 +156,15 @@ public function testMissingPatternDoctrineStyle() new Regex([]); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testPatternInOptionsArray() + { + $constraint = new Regex(null, options: ['pattern' => '/^[0-9]+$/']); + + $this->assertSame('/^[0-9]+$/', $constraint->pattern); + } } class RegexDummy diff --git a/Tests/Constraints/RegexValidatorTest.php b/Tests/Constraints/RegexValidatorTest.php index bafc752c3..a2ea4c6d5 100644 --- a/Tests/Constraints/RegexValidatorTest.php +++ b/Tests/Constraints/RegexValidatorTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\Regex; use Symfony\Component\Validator\Constraints\RegexValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -43,9 +46,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Regex(pattern: '/^[0-9]+$/')); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues($value) { $constraint = new Regex(pattern: '/^[0-9]+$/'); @@ -54,11 +55,9 @@ public function testValidValues($value) $this->assertNoViolation(); } - /** - * @group legacy - * - * @dataProvider getValidValuesWithWhitespaces - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getValidValuesWithWhitespaces')] public function testValidValuesWithWhitespaces($value) { $constraint = new Regex(['pattern' => '/^[0-9]+$/', 'normalizer' => 'trim']); @@ -67,9 +66,7 @@ public function testValidValuesWithWhitespaces($value) $this->assertNoViolation(); } - /** - * @dataProvider getValidValuesWithWhitespaces - */ + #[DataProvider('getValidValuesWithWhitespaces')] public function testValidValuesWithWhitespacesNamed($value) { $constraint = new Regex(pattern: '/^[0-9]+$/', normalizer: 'trim'); @@ -106,11 +103,9 @@ public static function getValidValuesWithWhitespaces() ]; } - /** - * @group legacy - * - * @dataProvider getInvalidValues - */ + #[IgnoreDeprecations] + #[Group('legacy')] + #[DataProvider('getInvalidValues')] public function testInvalidValues($value) { $constraint = new Regex([ @@ -127,9 +122,7 @@ public function testInvalidValues($value) ->assertRaised(); } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValuesNamed($value) { $constraint = new Regex(pattern: '/^[0-9]+$/', message: 'myMessage'); diff --git a/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php b/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php index ac40492fd..938b44a47 100644 --- a/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php +++ b/Tests/Constraints/ThrowsOnInvalidStringDatesTestTrait.php @@ -11,14 +11,13 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\AbstractComparison; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; trait ThrowsOnInvalidStringDatesTestTrait { - /** - * @dataProvider throwsOnInvalidStringDatesProvider - */ + #[DataProvider('throwsOnInvalidStringDatesProvider')] public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value) { $this->expectException(ConstraintDefinitionException::class); diff --git a/Tests/Constraints/TimeTest.php b/Tests/Constraints/TimeTest.php index 2e3132039..140b72d8f 100644 --- a/Tests/Constraints/TimeTest.php +++ b/Tests/Constraints/TimeTest.php @@ -24,11 +24,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'TimeDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/TimeValidatorTest.php b/Tests/Constraints/TimeValidatorTest.php index 7c1a9feb9..82ea054b1 100644 --- a/Tests/Constraints/TimeValidatorTest.php +++ b/Tests/Constraints/TimeValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Time; use Symfony\Component\Validator\Constraints\TimeValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -50,9 +51,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Time()); } - /** - * @dataProvider getValidTimes - */ + #[DataProvider('getValidTimes')] public function testValidTimes($time) { $this->validator->validate($time, new Time()); @@ -60,9 +59,7 @@ public function testValidTimes($time) $this->assertNoViolation(); } - /** - * @dataProvider getValidTimes - */ + #[DataProvider('getValidTimes')] public function testValidTimesWithNewLine(string $time) { $this->validator->validate($time."\n", new Time()); @@ -82,9 +79,7 @@ public static function getValidTimes() ]; } - /** - * @dataProvider getValidTimesWithoutSeconds - */ + #[DataProvider('getValidTimesWithoutSeconds')] public function testValidTimesWithoutSeconds(string $time) { $this->validator->validate($time, new Time(withSeconds: false)); @@ -92,9 +87,7 @@ public function testValidTimesWithoutSeconds(string $time) $this->assertNoViolation(); } - /** - * @dataProvider getValidTimesWithoutSeconds - */ + #[DataProvider('getValidTimesWithoutSeconds')] public function testValidTimesWithoutSecondsWithNewLine(string $time) { $this->validator->validate($time."\n", new Time(withSeconds: false)); @@ -114,9 +107,7 @@ public static function getValidTimesWithoutSeconds() ]; } - /** - * @dataProvider getInvalidTimesWithoutSeconds - */ + #[DataProvider('getInvalidTimesWithoutSeconds')] public function testInvalidTimesWithoutSeconds(string $time) { $this->validator->validate($time, $constraint = new Time()); @@ -136,9 +127,7 @@ public static function getInvalidTimesWithoutSeconds() ]; } - /** - * @dataProvider getInvalidTimes - */ + #[DataProvider('getInvalidTimes')] public function testInvalidTimes($time, $code) { $constraint = new Time(message: 'myMessage'); diff --git a/Tests/Constraints/TimezoneTest.php b/Tests/Constraints/TimezoneTest.php index 41fed2386..2b4f1ad2a 100644 --- a/Tests/Constraints/TimezoneTest.php +++ b/Tests/Constraints/TimezoneTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Timezone; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -50,9 +53,7 @@ public function testExceptionForGroupedTimezonesByCountryWithoutZone() new Timezone(countryCode: 'AR'); } - /** - * @dataProvider provideInvalidZones - */ + #[DataProvider('provideInvalidZones')] public function testExceptionForInvalidGroupedTimezones(int $zone) { $this->expectException(ConstraintDefinitionException::class); @@ -71,19 +72,28 @@ public function testAttributes() $metadata = new ClassMetadata(TimezoneDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(\DateTimeZone::ALL, $aConstraint->zone); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(\DateTimeZone::PER_COUNTRY, $bConstraint->zone); self::assertSame('DE', $bConstraint->countryCode); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'TimezoneDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testDoctrineStyle() + { + $constraint = new Timezone(['zone' => \DateTimeZone::ALL]); + + $this->assertSame(\DateTimeZone::ALL, $constraint->zone); + } } class TimezoneDummy diff --git a/Tests/Constraints/TimezoneValidatorTest.php b/Tests/Constraints/TimezoneValidatorTest.php index 7ed4d79ed..879cee69f 100644 --- a/Tests/Constraints/TimezoneValidatorTest.php +++ b/Tests/Constraints/TimezoneValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Symfony\Component\Validator\Constraints\Timezone; use Symfony\Component\Validator\Constraints\TimezoneValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -47,9 +49,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Timezone()); } - /** - * @dataProvider getValidTimezones - */ + #[DataProvider('getValidTimezones')] public function testValidTimezones(string $timezone) { $this->validator->validate($timezone, new Timezone()); @@ -87,9 +87,7 @@ public static function getValidTimezones(): iterable yield ['Pacific/Noumea']; } - /** - * @dataProvider getValidGroupedTimezones - */ + #[DataProvider('getValidGroupedTimezones')] public function testValidGroupedTimezones(string $timezone, int $zone) { $constraint = new Timezone(zone: $zone); @@ -118,9 +116,7 @@ public static function getValidGroupedTimezones(): iterable yield ['Atlantic/Azores', \DateTimeZone::ATLANTIC | \DateTimeZone::ASIA]; } - /** - * @dataProvider getInvalidTimezones - */ + #[DataProvider('getInvalidTimezones')] public function testInvalidTimezoneWithoutZone(string $timezone) { $constraint = new Timezone(message: 'myMessage'); @@ -141,9 +137,7 @@ public static function getInvalidTimezones(): iterable yield ['foobar']; } - /** - * @dataProvider getInvalidGroupedTimezones - */ + #[DataProvider('getInvalidGroupedTimezones')] public function testInvalidGroupedTimezones(string $timezone, int $zone) { $constraint = new Timezone( @@ -184,9 +178,7 @@ public function testInvalidGroupedTimezoneNamed() ->assertRaised(); } - /** - * @dataProvider getValidGroupedTimezonesByCountry - */ + #[DataProvider('getValidGroupedTimezonesByCountry')] public function testValidGroupedTimezonesByCountry(string $timezone, string $country) { $constraint = new Timezone( @@ -221,9 +213,7 @@ public static function getValidGroupedTimezonesByCountry(): iterable yield ['Pacific/Kiritimati', 'KI']; } - /** - * @dataProvider getInvalidGroupedTimezonesByCountry - */ + #[DataProvider('getInvalidGroupedTimezonesByCountry')] public function testInvalidGroupedTimezonesByCountry(string $timezone, string $countryCode) { $constraint = new Timezone( @@ -265,9 +255,7 @@ public function testGroupedTimezonesWithInvalidCountry() ->assertRaised(); } - /** - * @dataProvider getDeprecatedTimezones - */ + #[DataProvider('getDeprecatedTimezones')] public function testDeprecatedTimezonesAreValidWithBC(string $timezone) { // Skip test if the timezone is not available in the current timezone database @@ -282,9 +270,7 @@ public function testDeprecatedTimezonesAreValidWithBC(string $timezone) $this->assertNoViolation(); } - /** - * @dataProvider getDeprecatedTimezones - */ + #[DataProvider('getDeprecatedTimezones')] public function testDeprecatedTimezonesAreInvalidWithoutBC(string $timezone) { $constraint = new Timezone(message: 'myMessage'); @@ -315,9 +301,7 @@ public static function getDeprecatedTimezones(): iterable yield ['US/Pacific']; } - /** - * @requires extension intl - */ + #[RequiresPhpExtension('intl')] public function testIntlCompatibility() { $reflector = new \ReflectionExtension('intl'); diff --git a/Tests/Constraints/TypeTest.php b/Tests/Constraints/TypeTest.php index 73acddd43..f9bee2df6 100644 --- a/Tests/Constraints/TypeTest.php +++ b/Tests/Constraints/TypeTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Exception\MissingOptionsException; @@ -24,15 +26,15 @@ public function testAttributes() $metadata = new ClassMetadata(TypeDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame('integer', $aConstraint->type); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame(\DateTimeImmutable::class, $bConstraint->type); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'TypeDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['string', 'array'], $cConstraint->type); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); @@ -45,6 +47,15 @@ public function testMissingType() new Type(null); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testTypeInOptionsArray() + { + $constraint = new Type(null, options: ['type' => 'digit']); + + $this->assertSame('digit', $constraint->type); + } } class TypeDummy diff --git a/Tests/Constraints/TypeValidatorTest.php b/Tests/Constraints/TypeValidatorTest.php index 8e9e1aa3b..93841bc30 100644 --- a/Tests/Constraints/TypeValidatorTest.php +++ b/Tests/Constraints/TypeValidatorTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\TypeValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -58,9 +61,7 @@ public function testEmptyIsInvalidIfNoString() ->assertRaised(); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues($value, $type) { $constraint = new Type(type: $type); @@ -118,9 +119,7 @@ public static function getValidValues() ]; } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValues($value, $type, $valueAsString) { $constraint = new Type( @@ -190,9 +189,7 @@ public static function getInvalidValues() ]; } - /** - * @dataProvider getValidValuesMultipleTypes - */ + #[DataProvider('getValidValuesMultipleTypes')] public function testValidValuesMultipleTypes($value, array $types) { $constraint = new Type(type: $types); @@ -221,9 +218,8 @@ public function testInvalidValuesMultipleTypes() ->assertRaised(); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidValuesMultipleTypesDoctrineStyle() { $this->validator->validate('12345', new Type([ diff --git a/Tests/Constraints/UlidTest.php b/Tests/Constraints/UlidTest.php index 86cc32266..25be57edc 100644 --- a/Tests/Constraints/UlidTest.php +++ b/Tests/Constraints/UlidTest.php @@ -25,12 +25,12 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->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(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/UlidValidatorTest.php b/Tests/Constraints/UlidValidatorTest.php index 172ace189..592e0a94e 100644 --- a/Tests/Constraints/UlidValidatorTest.php +++ b/Tests/Constraints/UlidValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Ulid; use Symfony\Component\Validator\Constraints\UlidValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -67,9 +68,7 @@ public function testValidUlidAsRfc4122() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidUlids - */ + #[DataProvider('getInvalidUlids')] public function testInvalidUlid(string $ulid, string $code) { $constraint = new Ulid(message: 'testMessage'); @@ -96,9 +95,7 @@ public static function getInvalidUlids(): array ]; } - /** - * @dataProvider getInvalidBase58Ulids - */ + #[DataProvider('getInvalidBase58Ulids')] public function testInvalidBase58Ulid(string $ulid, string $code) { $constraint = new Ulid(message: 'testMessage', format: Ulid::FORMAT_BASE_58); @@ -124,9 +121,7 @@ public static function getInvalidBase58Ulids(): array ]; } - /** - * @dataProvider getInvalidRfc4122Ulids - */ + #[DataProvider('getInvalidRfc4122Ulids')] public function testInvalidInvalid4122Ulid(string $ulid, string $code) { $constraint = new Ulid(message: 'testMessage', format: Ulid::FORMAT_RFC_4122); diff --git a/Tests/Constraints/UniqueTest.php b/Tests/Constraints/UniqueTest.php index 9fe2599fd..6563d463c 100644 --- a/Tests/Constraints/UniqueTest.php +++ b/Tests/Constraints/UniqueTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Unique; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -25,21 +27,20 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'UniqueDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); - [$dConstraint] = $metadata->properties['d']->getConstraints(); + [$dConstraint] = $metadata->getPropertyMetadata('d')[0]->getConstraints(); self::assertSame('intval', $dConstraint->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -47,9 +48,8 @@ public function testInvalidNormalizerThrowsException() new Unique(['normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); diff --git a/Tests/Constraints/UniqueValidatorTest.php b/Tests/Constraints/UniqueValidatorTest.php index 12efb7698..fd4641b71 100644 --- a/Tests/Constraints/UniqueValidatorTest.php +++ b/Tests/Constraints/UniqueValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Unique; use Symfony\Component\Validator\Constraints\UniqueValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -31,9 +32,7 @@ public function testExpectsUniqueConstraintCompatibleType() $this->validator->validate('', new Unique()); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testValidValues($value) { $this->validator->validate($value, new Unique()); @@ -58,9 +57,7 @@ public static function getValidValues() ]; } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValues($value, $expectedMessageParam) { $constraint = new Unique(message: 'myMessage'); @@ -97,9 +94,7 @@ public function testInvalidValueNamed() ->assertRaised(); } - /** - * @dataProvider getCallback - */ + #[DataProvider('getCallback')] public function testExpectsUniqueObjects($callback) { $object1 = new \stdClass(); @@ -121,9 +116,7 @@ public function testExpectsUniqueObjects($callback) $this->assertNoViolation(); } - /** - * @dataProvider getCallback - */ + #[DataProvider('getCallback')] public function testExpectsNonUniqueObjects($callback) { $object1 = new \stdClass(); @@ -215,9 +208,7 @@ public function testCollectionFieldsAreOptional() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidFieldNames - */ + #[DataProvider('getInvalidFieldNames')] public function testCollectionFieldNamesMustBeString(string $type, mixed $field) { $this->expectException(UnexpectedTypeException::class); @@ -235,9 +226,7 @@ public static function getInvalidFieldNames(): array ]; } - /** - * @dataProvider getInvalidCollectionValues - */ + #[DataProvider('getInvalidCollectionValues')] public function testInvalidCollectionValues(array $value, array $fields, string $expectedMessageParam) { $this->validator->validate($value, new Unique( diff --git a/Tests/Constraints/UrlTest.php b/Tests/Constraints/UrlTest.php index cbc9bc18c..cfbb5ca1a 100644 --- a/Tests/Constraints/UrlTest.php +++ b/Tests/Constraints/UrlTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Url; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -29,9 +32,8 @@ public function testNormalizerCanBeSet() $this->assertEquals('trim', $url->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -39,9 +41,8 @@ public function testInvalidNormalizerThrowsException() new Url(['normalizer' => 'Unknown Callable', 'requireTld' => true]); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -54,41 +55,49 @@ public function testAttributes() $metadata = new ClassMetadata(UrlDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(['http', 'https'], $aConstraint->protocols); self::assertFalse($aConstraint->relativeProtocol); self::assertNull($aConstraint->normalizer); self::assertFalse($aConstraint->requireTld); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->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(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->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(); + [$dConstraint] = $metadata->getPropertyMetadata('d')[0]->getConstraints(); self::assertSame(['http', 'https'], $dConstraint->protocols); self::assertFalse($dConstraint->relativeProtocol); self::assertNull($dConstraint->normalizer); self::assertTrue($dConstraint->requireTld); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testRequireTldDefaultsToFalse() { $constraint = new Url(); $this->assertFalse($constraint->requireTld); } + + #[TestWith(['*'])] + #[TestWith(['http'])] + public function testProtocolsAsString(string $protocol) + { + $constraint = new Url(protocols: $protocol, requireTld: true); + + $this->assertSame([$protocol], $constraint->protocols); + } } class UrlDummy diff --git a/Tests/Constraints/UrlValidatorTest.php b/Tests/Constraints/UrlValidatorTest.php index 535714d1e..c9c3f3e27 100644 --- a/Tests/Constraints/UrlValidatorTest.php +++ b/Tests/Constraints/UrlValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Url; use Symfony\Component\Validator\Constraints\UrlValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -50,9 +51,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Url(requireTld: true)); } - /** - * @dataProvider getValidUrls - */ + #[DataProvider('getValidUrls')] public function testValidUrls($url) { $this->validator->validate($url, new Url(requireTld: false)); @@ -60,9 +59,7 @@ public function testValidUrls($url) $this->assertNoViolation(); } - /** - * @dataProvider getValidUrls - */ + #[DataProvider('getValidUrls')] public function testValidUrlsWithNewLine($url) { $this->validator->validate($url."\n", new Url(requireTld: false)); @@ -73,9 +70,7 @@ public function testValidUrlsWithNewLine($url) ->assertRaised(); } - /** - * @dataProvider getValidUrlsWithWhitespaces - */ + #[DataProvider('getValidUrlsWithWhitespaces')] public function testValidUrlsWithWhitespaces($url) { $this->validator->validate($url, new Url( @@ -86,10 +81,8 @@ public function testValidUrlsWithWhitespaces($url) $this->assertNoViolation(); } - /** - * @dataProvider getValidRelativeUrls - * @dataProvider getValidUrls - */ + #[DataProvider('getValidRelativeUrls')] + #[DataProvider('getValidUrls')] public function testValidRelativeUrl($url) { $constraint = new Url( @@ -102,10 +95,8 @@ public function testValidRelativeUrl($url) $this->assertNoViolation(); } - /** - * @dataProvider getValidRelativeUrls - * @dataProvider getValidUrls - */ + #[DataProvider('getValidRelativeUrls')] + #[DataProvider('getValidUrls')] public function testValidRelativeUrlWithNewLine(string $url) { $constraint = new Url(relativeProtocol: true, requireTld: false); @@ -118,6 +109,100 @@ public function testValidRelativeUrlWithNewLine(string $url) ->assertRaised(); } + public function testProtocolsWildcardAllowsAnyProtocol() + { + $constraint = new Url(protocols: ['*'], requireTld: false); + + $validUrls = [ + 'http://example.com/', + 'https://example.com/', + 'ftp://example.com', + 'custom://example.com', + 'myapp://example.com/path?query=1', + 'git+ssh://git@github.com/repo.git', + 'file://path/to/file', + 'scheme123://example.com', + 'a://example.com', + ]; + + foreach ($validUrls as $url) { + $this->validator->validate($url, $constraint); + $this->assertNoViolation(); + } + } + + public function testProtocolsWildcardRejectsInvalidSchemes() + { + $constraint = new Url(protocols: ['*'], requireTld: true); + + $invalidUrls = [ + '123://example.com', + '+scheme://example.com', + '-scheme://example.com', + '.scheme://example.com', + 'example.com', + '://example.com', + ]; + + foreach ($invalidUrls as $url) { + $this->setUp(); + $this->validator->validate($url, $constraint); + + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', '"'.$url.'"') + ->setCode(Url::INVALID_URL_ERROR) + ->assertRaised(); + } + } + + public function testProtocolsWildcardWithRelativeProtocol() + { + $constraint = new Url(protocols: ['*'], relativeProtocol: true, requireTld: true); + + $this->validator->validate('custom://example.com', $constraint); + $this->assertNoViolation(); + + $this->validator->validate('//example.com', $constraint); + $this->assertNoViolation(); + } + + public function testProtocolsWildcardWithRequireTld() + { + $constraint = new Url(protocols: ['*'], requireTld: true); + + $this->validator->validate('custom://example.com', $constraint); + $this->assertNoViolation(); + + $this->validator->validate('custom://localhost', $constraint); + $this->buildViolation($constraint->tldMessage) + ->setParameter('{{ value }}', '"custom://localhost"') + ->setCode(Url::MISSING_TLD_ERROR) + ->assertRaised(); + } + + public function testProtocolsSupportsRegexPatterns() + { + $constraint = new Url(protocols: ['https?', 'custom.*'], requireTld: true); + + $validUrls = [ + 'http://example.com/', + 'https://example.com/', + 'custom://example.com', + 'customapp://example.com', + ]; + + foreach ($validUrls as $url) { + $this->validator->validate($url, $constraint); + $this->assertNoViolation(); + } + + $this->validator->validate('ftp://example.com', $constraint); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', '"ftp://example.com"') + ->setCode(Url::INVALID_URL_ERROR) + ->assertRaised(); + } + public static function getValidRelativeUrls() { return [ @@ -228,9 +313,7 @@ public static function getValidUrlsWithWhitespaces() ]; } - /** - * @dataProvider getInvalidUrls - */ + #[DataProvider('getInvalidUrls')] public function testInvalidUrls($url) { $constraint = new Url( @@ -246,10 +329,8 @@ public function testInvalidUrls($url) ->assertRaised(); } - /** - * @dataProvider getInvalidRelativeUrls - * @dataProvider getInvalidUrls - */ + #[DataProvider('getInvalidRelativeUrls')] + #[DataProvider('getInvalidUrls')] public function testInvalidRelativeUrl($url) { $constraint = new Url( @@ -330,9 +411,7 @@ public static function getInvalidUrls() ]; } - /** - * @dataProvider getValidCustomUrls - */ + #[DataProvider('getValidCustomUrls')] public function testCustomProtocolIsValid($url, $requireTld) { $constraint = new Url( @@ -354,9 +433,7 @@ public static function getValidCustomUrls() ]; } - /** - * @dataProvider getUrlsForRequiredTld - */ + #[DataProvider('getUrlsForRequiredTld')] public function testRequiredTld(string $url, bool $requireTld, bool $isValid) { $constraint = new Url(requireTld: $requireTld); diff --git a/Tests/Constraints/UuidTest.php b/Tests/Constraints/UuidTest.php index 22901a9db..60c537529 100644 --- a/Tests/Constraints/UuidTest.php +++ b/Tests/Constraints/UuidTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Uuid; use Symfony\Component\Validator\Exception\InvalidArgumentException; @@ -29,9 +31,8 @@ public function testNormalizerCanBeSet() $this->assertEquals('trim', $uuid->normalizer); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -39,9 +40,8 @@ public function testInvalidNormalizerThrowsException() new Uuid(['normalizer' => 'Unknown Callable']); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testInvalidNormalizerObjectThrowsException() { $this->expectException(InvalidArgumentException::class); @@ -54,19 +54,19 @@ public function testAttributes() $metadata = new ClassMetadata(UuidDummy::class); self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); self::assertSame(Uuid::ALL_VERSIONS, $aConstraint->versions); self::assertTrue($aConstraint->strict); self::assertNull($aConstraint->normalizer); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame([Uuid::V4_RANDOM, Uuid::V6_SORTABLE], $bConstraint->versions); self::assertFalse($bConstraint->strict); self::assertSame('trim', $bConstraint->normalizer); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'UuidDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/UuidValidatorTest.php b/Tests/Constraints/UuidValidatorTest.php index 84edc6612..ad3f1e303 100644 --- a/Tests/Constraints/UuidValidatorTest.php +++ b/Tests/Constraints/UuidValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Uuid; use Symfony\Component\Validator\Constraints\UuidValidator; @@ -57,9 +58,7 @@ public function testExpectsStringCompatibleType() $this->validator->validate(new \stdClass(), new Uuid()); } - /** - * @dataProvider getValidStrictUuids - */ + #[DataProvider('getValidStrictUuids')] public function testValidStrictUuids($uuid, $versions = null) { $constraint = new Uuid(); @@ -88,9 +87,7 @@ public static function getValidStrictUuids() ]; } - /** - * @dataProvider getValidStrictUuidsWithWhitespaces - */ + #[DataProvider('getValidStrictUuidsWithWhitespaces')] public function testValidStrictUuidsWithWhitespaces($uuid, $versions = null) { $constraint = new Uuid(normalizer: 'trim'); @@ -126,9 +123,7 @@ public function testValidStrictUuidWithWhitespacesNamed() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidStrictUuids - */ + #[DataProvider('getInvalidStrictUuids')] public function testInvalidStrictUuids($uuid, $code, $versions = null) { $constraint = new Uuid(message: 'testMessage'); @@ -188,9 +183,7 @@ public static function getInvalidStrictUuids() ]; } - /** - * @dataProvider getValidNonStrictUuids - */ + #[DataProvider('getValidNonStrictUuids')] public function testValidNonStrictUuids($uuid) { $constraint = new Uuid(strict: false); @@ -217,9 +210,7 @@ public static function getValidNonStrictUuids() ]; } - /** - * @dataProvider getInvalidNonStrictUuids - */ + #[DataProvider('getInvalidNonStrictUuids')] public function testInvalidNonStrictUuids($uuid, $code) { $constraint = new Uuid( @@ -261,9 +252,7 @@ public function testInvalidNonStrictUuidNamed() ->assertRaised(); } - /** - * @dataProvider getUuidForTimeBasedAssertions - */ + #[DataProvider('getUuidForTimeBasedAssertions')] public function testTimeBasedUuid(string $uid, bool $expectedTimeBased) { $constraint = new Uuid(versions: Uuid::TIME_BASED_VERSIONS); diff --git a/Tests/Constraints/ValidComparisonToValueTrait.php b/Tests/Constraints/ValidComparisonToValueTrait.php index c4c70d8ce..c5de9b3f8 100644 --- a/Tests/Constraints/ValidComparisonToValueTrait.php +++ b/Tests/Constraints/ValidComparisonToValueTrait.php @@ -11,11 +11,11 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; + trait ValidComparisonToValueTrait { - /** - * @dataProvider provideAllValidComparisons - */ + #[DataProvider('provideAllValidComparisons')] public function testValidComparisonToValue($dirtyValue, $comparisonValue) { $constraint = $this->createConstraint(['value' => $comparisonValue]); diff --git a/Tests/Constraints/ValidTest.php b/Tests/Constraints/ValidTest.php index a862171f1..3ae0ef76e 100644 --- a/Tests/Constraints/ValidTest.php +++ b/Tests/Constraints/ValidTest.php @@ -41,11 +41,11 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertFalse($bConstraint->traverse); self::assertSame(['traverse_group'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); } diff --git a/Tests/Constraints/VideoTest.php b/Tests/Constraints/VideoTest.php new file mode 100644 index 000000000..e84fd0ee1 --- /dev/null +++ b/Tests/Constraints/VideoTest.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\Process\ExecutableFinder; +use Symfony\Component\Validator\Constraints\Video; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; + +class VideoTest extends TestCase +{ + public static function setUpBeforeClass(): void + { + if (!(new ExecutableFinder())->find('ffprobe')) { + self::markTestSkipped('The ffprobe binary is required to run this test.'); + } + } + + public function testAttributes() + { + $metadata = new ClassMetadata(VideoDummy::class); + $loader = new AttributeLoader(); + self::assertTrue($loader->loadClassMetadata($metadata)); + + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); + self::assertNull($aConstraint->minWidth); + self::assertNull($aConstraint->maxWidth); + self::assertNull($aConstraint->minHeight); + self::assertNull($aConstraint->maxHeight); + + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); + self::assertSame(50, $bConstraint->minWidth); + self::assertSame(200, $bConstraint->maxWidth); + self::assertSame(50, $bConstraint->minHeight); + self::assertSame(200, $bConstraint->maxHeight); + self::assertSame(['Default', 'VideoDummy'], $bConstraint->groups); + + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); + self::assertSame(100000, $cConstraint->maxSize); + self::assertSame(['my_group'], $cConstraint->groups); + self::assertSame('some attached data', $cConstraint->payload); + } +} + +class VideoDummy +{ + #[Video] + private $a; + + #[Video(minWidth: 50, maxWidth: 200, minHeight: 50, maxHeight: 200)] + private $b; + + #[Video(maxSize: '100K', groups: ['my_group'], payload: 'some attached data')] + private $c; +} diff --git a/Tests/Constraints/VideoValidatorTest.php b/Tests/Constraints/VideoValidatorTest.php new file mode 100644 index 000000000..fc5177940 --- /dev/null +++ b/Tests/Constraints/VideoValidatorTest.php @@ -0,0 +1,286 @@ + + * + * 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\Attributes\RequiresPhpExtension; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Validator\Constraints\Video; +use Symfony\Component\Validator\Constraints\VideoValidator; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +#[RequiresPhpExtension('fileinfo')] +class VideoValidatorTest extends ConstraintValidatorTestCase +{ + public static function setUpBeforeClass(): void + { + if (!(new ExecutableFinder())->find('ffprobe')) { + self::markTestSkipped('The ffprobe binary is required to run this test.'); + } + } + + protected function createValidator(): VideoValidator + { + return new VideoValidator(); + } + + public function testNullIsValid() + { + $this->validator->validate(null, new Video()); + + $this->assertNoViolation(); + } + + public function testEmptyStringIsValid() + { + $this->validator->validate('', new Video()); + + $this->assertNoViolation(); + } + + public function testValidVideo() + { + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', new Video()); + + $this->assertNoViolation(); + } + + public function testFileNotFound() + { + $constraint = new Video(notFoundMessage: 'myMessage'); + $this->validator->validate('foobar', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ file }}', '"foobar"') + ->setCode(Video::NOT_FOUND_ERROR) + ->assertRaised(); + } + + public function testValidSize() + { + $constraint = new Video( + minWidth: 1, + maxWidth: 2, + minHeight: 1, + maxHeight: 2, + ); + + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->assertNoViolation(); + } + + public function testWidthTooSmall() + { + $constraint = new Video(minWidth: 3, minWidthMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', '2') + ->setParameter('{{ min_width }}', '3') + ->setCode(Video::TOO_NARROW_ERROR) + ->assertRaised(); + } + + public function testWidthTooBig() + { + $constraint = new Video(maxWidth: 1, maxWidthMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', '2') + ->setParameter('{{ max_width }}', '1') + ->setCode(Video::TOO_WIDE_ERROR) + ->assertRaised(); + } + + public function testHeightTooSmall() + { + $constraint = new Video(minHeight: 3, minHeightMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ min_height }}', '3') + ->setCode(Video::TOO_LOW_ERROR) + ->assertRaised(); + } + + public function testHeightTooBig() + { + $constraint = new Video(maxHeight: 1, maxHeightMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ max_height }}', '1') + ->setCode(Video::TOO_HIGH_ERROR) + ->assertRaised(); + } + + public function testPixelsTooFew() + { + $constraint = new Video(minPixels: 5, minPixelsMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ pixels }}', '4') + ->setParameter('{{ min_pixels }}', '5') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ width }}', '2') + ->setCode(Video::TOO_FEW_PIXEL_ERROR) + ->assertRaised(); + } + + public function testPixelsTooMany() + { + $constraint = new Video(maxPixels: 3, maxPixelsMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ pixels }}', '4') + ->setParameter('{{ max_pixels }}', '3') + ->setParameter('{{ height }}', '2') + ->setParameter('{{ width }}', '2') + ->setCode(Video::TOO_MANY_PIXEL_ERROR) + ->assertRaised(); + } + + public function testRatioTooSmall() + { + $constraint = new Video(minRatio: 2, minRatioMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ ratio }}', 1) + ->setParameter('{{ min_ratio }}', 2) + ->setCode(Video::RATIO_TOO_SMALL_ERROR) + ->assertRaised(); + } + + public function testRatioTooBig() + { + $constraint = new Video(maxRatio: 0.5, maxRatioMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ ratio }}', 1) + ->setParameter('{{ max_ratio }}', 0.5) + ->setCode(Video::RATIO_TOO_BIG_ERROR) + ->assertRaised(); + } + + public function testMaxRatioUsesTwoDecimalsOnly() + { + $constraint = new Video(maxRatio: 1.33); + + $this->validator->validate(__DIR__.'/Fixtures/test_4by3.mp4', $constraint); + + $this->assertNoViolation(); + } + + public function testMinRatioUsesInputMoreDecimals() + { + $constraint = new Video(minRatio: 4 / 3); + + $this->validator->validate(__DIR__.'/Fixtures/test_4by3.mp4', $constraint); + + $this->assertNoViolation(); + } + + public function testMaxRatioUsesInputMoreDecimals() + { + $constraint = new Video(maxRatio: 16 / 9); + + $this->validator->validate(__DIR__.'/Fixtures/test_16by9.mp4', $constraint); + + $this->assertNoViolation(); + } + + public function testSquareNotAllowed() + { + $constraint = new Video(allowSquare: false, allowSquareMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 2) + ->setParameter('{{ height }}', 2) + ->setCode(Video::SQUARE_NOT_ALLOWED_ERROR) + ->assertRaised(); + } + + public function testLandscapeNotAllowed() + { + $constraint = new Video(allowLandscape: false, allowLandscapeMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test_landscape.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 2) + ->setParameter('{{ height }}', 1) + ->setCode(Video::LANDSCAPE_NOT_ALLOWED_ERROR) + ->assertRaised(); + } + + public function testPortraitNotAllowed() + { + $constraint = new Video(allowPortrait: false, allowPortraitMessage: 'myMessage'); + $this->validator->validate(__DIR__.'/Fixtures/test_portrait.mp4', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 1) + ->setParameter('{{ height }}', 2) + ->setCode(Video::PORTRAIT_NOT_ALLOWED_ERROR) + ->assertRaised(); + } + + public function testCorrupted() + { + $constraint = new Video(maxRatio: 1); + + $this->validator->validate(__DIR__.'/Fixtures/test_corrupted.mp4', $constraint); + + $this->buildViolation('The video file is corrupted.') + ->setCode(Video::CORRUPTED_VIDEO_ERROR) + ->assertRaised(); + } + + public function testInvalidMimeType() + { + $this->validator->validate(__DIR__.'/Fixtures/ccc.txt', $constraint = new Video()); + + $this->assertSame('video/*', $constraint->mimeTypes); + + $this->buildViolation('This file is not a valid video.') + ->setParameter('{{ file }}', \sprintf('"%s/Fixtures/ccc.txt"', __DIR__)) + ->setParameter('{{ type }}', '"text/plain"') + ->setParameter('{{ types }}', '"video/*"') + ->setParameter('{{ name }}', '"ccc.txt"') + ->setCode(Video::INVALID_MIME_TYPE_ERROR) + ->assertRaised(); + } + + public function testInvalidMimeTypeWithNarrowedSet() + { + $constraint = new Video(mimeTypes: [ + 'video/mkv', + 'video/mov', + ]); + $this->validator->validate(__DIR__.'/Fixtures/test.mp4', $constraint); + + $this->buildViolation('The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.') + ->setParameter('{{ file }}', \sprintf('"%s/Fixtures/test.mp4"', __DIR__)) + ->setParameter('{{ type }}', '"video/mp4"') + ->setParameter('{{ types }}', '"video/mkv", "video/mov"') + ->setParameter('{{ name }}', '"test.mp4"') + ->setCode(Video::INVALID_MIME_TYPE_ERROR) + ->assertRaised(); + } +} diff --git a/Tests/Constraints/WeekTest.php b/Tests/Constraints/WeekTest.php index 0fc9aac62..7daeafd23 100644 --- a/Tests/Constraints/WeekTest.php +++ b/Tests/Constraints/WeekTest.php @@ -81,11 +81,11 @@ public function testAttributes() $loader = new AttributeLoader(); $this->assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); $this->assertNull($aConstraint->min); $this->assertNull($aConstraint->max); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); $this->assertSame('2010-W01', $bConstraint->min); $this->assertSame('2010-W02', $bConstraint->max); } diff --git a/Tests/Constraints/WeekValidatorTest.php b/Tests/Constraints/WeekValidatorTest.php index 0a5f0936c..e825d494d 100644 --- a/Tests/Constraints/WeekValidatorTest.php +++ b/Tests/Constraints/WeekValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Week; use Symfony\Component\Validator\Constraints\WeekValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -24,9 +25,7 @@ protected function createValidator(): WeekValidator return new WeekValidator(); } - /** - * @dataProvider provideWeekNumber - */ + #[DataProvider('provideWeekNumber')] public function testWeekIsValidWeekNumber(string|\Stringable $value, bool $expectedViolation) { $constraint = new Week(); @@ -96,9 +95,7 @@ public function testWithNewLine() ->assertRaised(); } - /** - * @dataProvider provideInvalidValues - */ + #[DataProvider('provideInvalidValues')] public function testInvalidValues(string $value) { $this->validator->validate($value, new Week()); @@ -108,9 +105,7 @@ public function testInvalidValues(string $value) ->assertRaised(); } - /** - * @dataProvider provideInvalidTypes - */ + #[DataProvider('provideInvalidTypes')] public function testNonStringValues(mixed $value) { $this->expectException(UnexpectedValueException::class); diff --git a/Tests/Constraints/WhenTest.php b/Tests/Constraints/WhenTest.php index 6f82c6429..f4dd94a0b 100644 --- a/Tests/Constraints/WhenTest.php +++ b/Tests/Constraints/WhenTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Length; @@ -26,9 +29,8 @@ final class WhenTest extends TestCase { - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testMissingOptionsExceptionIsThrown() { $this->expectException(MissingOptionsException::class); @@ -71,7 +73,7 @@ public function testAttributes() ], $classConstraint->constraints); self::assertSame([], $classConstraint->otherwise); - [$fooConstraint] = $metadata->properties['foo']->getConstraints(); + [$fooConstraint] = $metadata->getPropertyMetadata('foo')[0]->getConstraints(); self::assertInstanceOf(When::class, $fooConstraint); self::assertSame('true', $fooConstraint->expression); @@ -82,7 +84,7 @@ public function testAttributes() self::assertSame([], $fooConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithAttributes'], $fooConstraint->groups); - [$barConstraint] = $metadata->properties['bar']->getConstraints(); + [$barConstraint] = $metadata->getPropertyMetadata('bar')[0]->getConstraints(); self::assertInstanceOf(When::class, $barConstraint); self::assertSame('false', $barConstraint->expression); @@ -93,7 +95,7 @@ public function testAttributes() self::assertSame([], $barConstraint->otherwise); self::assertSame(['foo'], $barConstraint->groups); - [$quxConstraint] = $metadata->properties['qux']->getConstraints(); + [$quxConstraint] = $metadata->getPropertyMetadata('qux')[0]->getConstraints(); self::assertInstanceOf(When::class, $quxConstraint); self::assertSame('true', $quxConstraint->expression); @@ -101,7 +103,7 @@ public function testAttributes() self::assertSame([], $quxConstraint->otherwise); self::assertSame(['foo'], $quxConstraint->groups); - [$bazConstraint] = $metadata->getters['baz']->getConstraints(); + [$bazConstraint] = $metadata->getPropertyMetadata('baz')[0]->getConstraints(); self::assertInstanceOf(When::class, $bazConstraint); self::assertSame('true', $bazConstraint->expression); @@ -112,7 +114,7 @@ public function testAttributes() self::assertSame([], $bazConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithAttributes'], $bazConstraint->groups); - [$quuxConstraint] = $metadata->properties['quux']->getConstraints(); + [$quuxConstraint] = $metadata->getPropertyMetadata('quux')[0]->getConstraints(); self::assertInstanceOf(When::class, $quuxConstraint); self::assertSame('true', $quuxConstraint->expression); @@ -121,9 +123,7 @@ public function testAttributes() self::assertSame(['foo'], $quuxConstraint->groups); } - /** - * @requires PHP 8.5 - */ + #[RequiresPhp('>=8.5')] public function testAttributesWithClosure() { $loader = new AttributeLoader(); @@ -143,7 +143,7 @@ public function testAttributesWithClosure() ], $classConstraint->constraints); self::assertSame([], $classConstraint->otherwise); - [$fooConstraint] = $metadata->properties['foo']->getConstraints(); + [$fooConstraint] = $metadata->getPropertyMetadata('foo')[0]->getConstraints(); self::assertInstanceOf(When::class, $fooConstraint); self::assertInstanceOf(\Closure::class, $fooConstraint->expression); @@ -154,4 +154,17 @@ public function testAttributesWithClosure() self::assertSame([], $fooConstraint->otherwise); self::assertSame(['Default', 'WhenTestWithClosure'], $fooConstraint->groups); } + + #[IgnoreDeprecations] + #[Group('legacy')] + public function testConstraintsInOptionsArray() + { + $constraints = [ + new NotNull(), + new Length(min: 10), + ]; + $constraint = new When('true', options: ['constraints' => $constraints]); + + $this->assertSame($constraints, $constraint->constraints); + } } diff --git a/Tests/Constraints/WordCountTest.php b/Tests/Constraints/WordCountTest.php index 50ed8081d..c6fd2ea10 100644 --- a/Tests/Constraints/WordCountTest.php +++ b/Tests/Constraints/WordCountTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\WordCount; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -18,9 +19,7 @@ use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; -/** - * @requires extension intl - */ +#[RequiresPhpExtension('intl')] class WordCountTest extends TestCase { public function testLocaleIsSet() @@ -111,17 +110,17 @@ public function testAttributes() $loader = new AttributeLoader(); $this->assertTrue($loader->loadClassMetadata($metadata)); - [$aConstraint] = $metadata->properties['a']->getConstraints(); + [$aConstraint] = $metadata->getPropertyMetadata('a')[0]->getConstraints(); $this->assertSame(1, $aConstraint->min); $this->assertNull($aConstraint->max); $this->assertNull($aConstraint->locale); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); $this->assertSame(2, $bConstraint->min); $this->assertSame(5, $bConstraint->max); $this->assertNull($bConstraint->locale); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); $this->assertSame(3, $cConstraint->min); $this->assertNull($cConstraint->max); $this->assertSame('en', $cConstraint->locale); diff --git a/Tests/Constraints/WordCountValidatorTest.php b/Tests/Constraints/WordCountValidatorTest.php index ce1256f92..850f67403 100644 --- a/Tests/Constraints/WordCountValidatorTest.php +++ b/Tests/Constraints/WordCountValidatorTest.php @@ -11,15 +11,15 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; 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 - */ +#[RequiresPhpExtension('intl')] class WordCountValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): WordCountValidator @@ -27,9 +27,7 @@ protected function createValidator(): WordCountValidator return new WordCountValidator(); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function testValidWordCount(string|\Stringable|null $value, int $expectedWordCount) { $this->validator->validate($value, new WordCount(min: $expectedWordCount, max: $expectedWordCount)); @@ -63,9 +61,7 @@ public function testTooLong() ->assertRaised(); } - /** - * @dataProvider provideInvalidTypes - */ + #[DataProvider('provideInvalidTypes')] public function testNonStringValues(mixed $value) { $this->expectException(UnexpectedValueException::class); diff --git a/Tests/Constraints/YamlTest.php b/Tests/Constraints/YamlTest.php index c9529fc12..60d5d126e 100644 --- a/Tests/Constraints/YamlTest.php +++ b/Tests/Constraints/YamlTest.php @@ -28,15 +28,15 @@ public function testAttributes() $loader = new AttributeLoader(); self::assertTrue($loader->loadClassMetadata($metadata)); - [$bConstraint] = $metadata->properties['b']->getConstraints(); + [$bConstraint] = $metadata->getPropertyMetadata('b')[0]->getConstraints(); self::assertSame('myMessage', $bConstraint->message); self::assertSame(['Default', 'YamlDummy'], $bConstraint->groups); - [$cConstraint] = $metadata->properties['c']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('c')[0]->getConstraints(); self::assertSame(['my_group'], $cConstraint->groups); self::assertSame('some attached data', $cConstraint->payload); - [$cConstraint] = $metadata->properties['d']->getConstraints(); + [$cConstraint] = $metadata->getPropertyMetadata('d')[0]->getConstraints(); self::assertSame(YamlParser::PARSE_CONSTANT | YamlParser::PARSE_CUSTOM_TAGS, $cConstraint->flags); } } diff --git a/Tests/Constraints/YamlValidatorTest.php b/Tests/Constraints/YamlValidatorTest.php index 5a90ccf03..21458725e 100644 --- a/Tests/Constraints/YamlValidatorTest.php +++ b/Tests/Constraints/YamlValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Validator\Constraints\Yaml; use Symfony\Component\Validator\Constraints\YamlValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -26,9 +27,7 @@ protected function createValidator(): YamlValidator return new YamlValidator(); } - /** - * @dataProvider getValidValues - */ + #[DataProvider('getValidValues')] public function testYamlIsValid($value) { $this->validator->validate($value, new Yaml()); @@ -42,9 +41,7 @@ public function testYamlWithFlags() $this->assertNoViolation(); } - /** - * @dataProvider getInvalidValues - */ + #[DataProvider('getInvalidValues')] public function testInvalidValues($value, $message, $line) { $constraint = new Yaml( @@ -71,9 +68,7 @@ public function testInvalidFlags() ->assertRaised(); } - /** - * @dataProvider getDeprecationOnLinesData - */ + #[DataProvider('getDeprecationOnLinesData')] public function testDeprecationTriggersParseException(int $yamlLine, string $yamlValue) { $lines = explode("\n", $yamlValue); diff --git a/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php b/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php index 9a6817966..7f4b36da1 100644 --- a/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php +++ b/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\DependencyInjection; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Validator\DependencyInjection\AddAutoMappingConfigurationPass; @@ -36,9 +37,7 @@ public function testNoValidatorBuilder() $this->assertCount(1, $container->getDefinitions()); } - /** - * @dataProvider mappingProvider - */ + #[DataProvider('mappingProvider')] public function testProcess(string $namespace, array $services, string $expectedRegexp) { $container = new ContainerBuilder(); diff --git a/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php b/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php index 1b85c7e2a..69184b8fb 100644 --- a/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php +++ b/Tests/DependencyInjection/AddConstraintValidatorsPassTest.php @@ -36,7 +36,7 @@ public function testThatConstraintValidatorServicesAreProcessed() $addConstraintValidatorsPass->process($container); $locator = $container->getDefinition((string) $validatorFactory->getArgument(0)); - $this->assertTrue(!$locator->isPublic() || $locator->isPrivate()); + $this->assertTrue($locator->isPrivate()); $expected = (new Definition(ServiceLocator::class, [[ Validator1::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), 'my_constraint_validator_alias1' => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), diff --git a/Tests/DependencyInjection/AttributeMetadataPassTest.php b/Tests/DependencyInjection/AttributeMetadataPassTest.php new file mode 100644 index 000000000..611db9343 --- /dev/null +++ b/Tests/DependencyInjection/AttributeMetadataPassTest.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\Validator\DependencyInjection\AttributeMetadataPass; +use Symfony\Component\Validator\Exception\MappingException; + +class AttributeMetadataPassTest extends TestCase +{ + public function testProcessWithNoValidatorBuilder() + { + $container = new ContainerBuilder(); + + // Should not throw any exception + (new AttributeMetadataPass())->process($container); + + $this->expectNotToPerformAssertions(); + } + + public function testProcessWithValidatorBuilderButNoTaggedServices() + { + $container = new ContainerBuilder(); + $container->register('validator.builder'); + + $pass = new AttributeMetadataPass(); + $pass->process($container); + + $methodCalls = $container->getDefinition('validator.builder')->getMethodCalls(); + $this->assertCount(0, $methodCalls); + } + + public function testProcessWithTaggedServices() + { + $container = new ContainerBuilder(); + $container->setParameter('user_entity.class', 'App\Entity\User'); + $container->register('validator.builder') + ->addMethodCall('addAttributeMappings', [[]]); + + $container->register('service1', '%user_entity.class%') + ->addTag('validator.attribute_metadata') + ->addTag('container.excluded'); + $container->register('service2', 'App\Entity\Product') + ->addTag('validator.attribute_metadata') + ->addTag('container.excluded'); + $container->register('service3', 'App\Entity\Order') + ->addTag('validator.attribute_metadata') + ->addTag('container.excluded'); + // Classes should be deduplicated + $container->register('service4', 'App\Entity\Order') + ->addTag('validator.attribute_metadata') + ->addTag('container.excluded'); + + (new AttributeMetadataPass())->process($container); + + $methodCalls = $container->getDefinition('validator.builder')->getMethodCalls(); + $this->assertCount(2, $methodCalls); + $this->assertEquals('addAttributeMappings', $methodCalls[1][0]); + + // Classes should be sorted alphabetically + $expectedClasses = [ + 'App\Entity\Order' => ['App\Entity\Order'], + 'App\Entity\Product' => ['App\Entity\Product'], + 'App\Entity\User' => ['App\Entity\User'], + ]; + $this->assertEquals([$expectedClasses], $methodCalls[1][1]); + } + + public function testThrowsWhenMissingExcludedTag() + { + $container = new ContainerBuilder(); + $container->register('validator.builder'); + + $container->register('service_without_excluded', 'App\\Entity\\User') + ->addTag('validator.attribute_metadata'); + + $this->expectException(InvalidArgumentException::class); + (new AttributeMetadataPass())->process($container); + } + + public function testProcessWithForOptionAndMatchingMembers() + { + $sourceClass = _AttrMeta_Source::class; + $targetClass = _AttrMeta_Target::class; + + $container = new ContainerBuilder(); + $container->register('validator.builder'); + + $container->register('service.source', $sourceClass) + ->addTag('validator.attribute_metadata', ['for' => $targetClass]) + ->addTag('container.excluded'); + + (new AttributeMetadataPass())->process($container); + + $methodCalls = $container->getDefinition('validator.builder')->getMethodCalls(); + $this->assertNotEmpty($methodCalls); + $this->assertSame('addAttributeMappings', $methodCalls[0][0]); + $this->assertSame([$targetClass => [$sourceClass]], $methodCalls[0][1][0]); + } + + public function testProcessWithForOptionAndMissingMemberThrows() + { + $sourceClass = _AttrMeta_BadSource::class; + $targetClass = _AttrMeta_Target::class; + + $container = new ContainerBuilder(); + $container->register('validator.builder'); + + $container->register('service.source', $sourceClass) + ->addTag('validator.attribute_metadata', ['for' => $targetClass]) + ->addTag('container.excluded'); + + $this->expectException(MappingException::class); + (new AttributeMetadataPass())->process($container); + } +} + +class _AttrMeta_Source +{ + public string $name; + + public function getName() + { + } +} + +class _AttrMeta_Target +{ + public string $name; + + public function getName() + { + } +} + +class _AttrMeta_BadSource +{ + public string $extra; +} diff --git a/Tests/Fixtures/ConstraintA.php b/Tests/Fixtures/ConstraintA.php index 51e8ae6a7..6a0cc10ef 100644 --- a/Tests/Fixtures/ConstraintA.php +++ b/Tests/Fixtures/ConstraintA.php @@ -19,9 +19,11 @@ class ConstraintA extends Constraint public $property1; public $property2; - public function getDefaultOption(): ?string + public function __construct($property1 = null, $property2 = null, $groups = null) { - return 'property2'; + parent::__construct(null, $groups); + $this->property1 = $property1; + $this->property2 = $property2; } public function getTargets(): string|array diff --git a/Tests/Fixtures/ConstraintWithRequiredArgument.php b/Tests/Fixtures/ConstraintWithRequiredArgument.php index f8abc8a56..93123677a 100644 --- a/Tests/Fixtures/ConstraintWithRequiredArgument.php +++ b/Tests/Fixtures/ConstraintWithRequiredArgument.php @@ -22,7 +22,7 @@ final class ConstraintWithRequiredArgument extends Constraint #[HasNamedArguments] public function __construct(string $requiredArg, ?array $groups = null, mixed $payload = null) { - parent::__construct([], $groups, $payload); + parent::__construct(null, $groups, $payload); $this->requiredArg = $requiredArg; } diff --git a/Tests/Fixtures/LegacyConstraintA.php b/Tests/Fixtures/LegacyConstraintA.php new file mode 100644 index 000000000..b115608de --- /dev/null +++ b/Tests/Fixtures/LegacyConstraintA.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\Fixtures; + +use Symfony\Component\Validator\Constraint; + +#[\Attribute] +class LegacyConstraintA extends Constraint +{ + public $property1; + public $property2; + + public function getDefaultOption(): ?string + { + return 'property2'; + } + + public function getTargets(): string|array + { + return [self::PROPERTY_CONSTRAINT, self::CLASS_CONSTRAINT]; + } +} diff --git a/Tests/Mapping/ClassMetadataTest.php b/Tests/Mapping/ClassMetadataTest.php index edfacae16..07da48c8d 100644 --- a/Tests/Mapping/ClassMetadataTest.php +++ b/Tests/Mapping/ClassMetadataTest.php @@ -88,8 +88,8 @@ public function testAddMultiplePropertyConstraints() $this->metadata->addPropertyConstraints('lastName', [new ConstraintA(), new ConstraintB()]); $constraints = [ - new ConstraintA(['groups' => ['Default', 'Entity']]), - new ConstraintB(['groups' => ['Default', 'Entity']]), + new ConstraintA(null, null, ['Default', 'Entity']), + new ConstraintB(null, ['Default', 'Entity']), ]; $properties = $this->metadata->getPropertyMetadata('lastName'); @@ -105,8 +105,8 @@ public function testAddGetterConstraints() $this->metadata->addGetterConstraint('lastName', new ConstraintB()); $constraints = [ - new ConstraintA(['groups' => ['Default', 'Entity']]), - new ConstraintB(['groups' => ['Default', 'Entity']]), + new ConstraintA(null, null, ['Default', 'Entity']), + new ConstraintB(null, ['Default', 'Entity']), ]; $properties = $this->metadata->getPropertyMetadata('lastName'); @@ -121,8 +121,8 @@ public function testAddMultipleGetterConstraints() $this->metadata->addGetterConstraints('lastName', [new ConstraintA(), new ConstraintB()]); $constraints = [ - new ConstraintA(['groups' => ['Default', 'Entity']]), - new ConstraintB(['groups' => ['Default', 'Entity']]), + new ConstraintA(null, null, ['Default', 'Entity']), + new ConstraintB(null, ['Default', 'Entity']), ]; $properties = $this->metadata->getPropertyMetadata('lastName'); @@ -141,15 +141,15 @@ public function testMergeConstraintsMergesClassConstraints() $this->metadata->addConstraint(new ConstraintA()); $constraints = [ - new ConstraintA(['groups' => [ + new ConstraintA(null, null, [ 'Default', 'EntityParent', 'Entity', - ]]), - new ConstraintA(['groups' => [ + ]), + new ConstraintA(null, null, [ 'Default', 'Entity', - ]]), + ]), ]; $this->assertEquals($constraints, $this->metadata->getConstraints()); @@ -159,47 +159,36 @@ public function testMergeConstraintsMergesMemberConstraints() { $parent = new ClassMetadata(self::PARENTCLASS); $parent->addPropertyConstraint('firstName', new ConstraintA()); - $parent->addPropertyConstraint('firstName', new ConstraintB(['groups' => 'foo'])); + $parent->addPropertyConstraint('firstName', new ConstraintB(null, ['foo'])); $this->metadata->addPropertyConstraint('firstName', new ConstraintA()); $this->metadata->mergeConstraints($parent); - $constraintA1 = new ConstraintA(['groups' => [ + $constraintA1 = new ConstraintA(null, null, [ 'Default', 'EntityParent', 'Entity', - ]]); - $constraintA2 = new ConstraintA(['groups' => [ + ]); + $constraintA2 = new ConstraintA(null, null, [ 'Default', 'Entity', - ]]); - $constraintB = new ConstraintB([ - 'groups' => ['foo'], ]); + $constraintB = new ConstraintB(null, ['foo']); $members = $this->metadata->getPropertyMetadata('firstName'); $this->assertCount(2, $members); $this->assertEquals(self::CLASSNAME, $members[0]->getClassName()); $this->assertEquals([$constraintA2], $members[0]->getConstraints()); - $this->assertEquals( - [ - 'Default' => [$constraintA2], - 'Entity' => [$constraintA2], - ], - $members[0]->constraintsByGroup - ); + $this->assertEquals([$constraintA2], $members[0]->findConstraints('Default')); + $this->assertEquals([$constraintA2], $members[0]->findConstraints('Entity')); + $this->assertEquals(self::PARENTCLASS, $members[1]->getClassName()); $this->assertEquals([$constraintA1, $constraintB], $members[1]->getConstraints()); - $this->assertEquals( - [ - 'Default' => [$constraintA1], - 'Entity' => [$constraintA1], - 'EntityParent' => [$constraintA1], - 'foo' => [$constraintB], - ], - $members[1]->constraintsByGroup - ); + $this->assertEquals([$constraintA1], $members[1]->findConstraints('Default')); + $this->assertEquals([$constraintA1], $members[1]->findConstraints('Entity')); + $this->assertEquals([$constraintA1], $members[1]->findConstraints('EntityParent')); + $this->assertEquals([$constraintB], $members[1]->findConstraints('foo')); } public function testMemberMetadatas() @@ -219,17 +208,17 @@ public function testMergeConstraintsKeepsPrivateMembersSeparate() $this->metadata->addPropertyConstraint('internal', new ConstraintA()); $parentConstraints = [ - new ConstraintA(['groups' => [ + new ConstraintA(null, null, [ 'Default', 'EntityParent', 'Entity', - ]]), + ]), ]; $constraints = [ - new ConstraintA(['groups' => [ + new ConstraintA(null, null, [ 'Default', 'Entity', - ]]), + ]), ]; $members = $this->metadata->getPropertyMetadata('internal'); @@ -250,8 +239,8 @@ public function testGetReflectionClass() public function testSerialize() { - $this->metadata->addConstraint(new ConstraintA(['property1' => 'A'])); - $this->metadata->addConstraint(new ConstraintB(['groups' => 'TestGroup'])); + $this->metadata->addConstraint(new ConstraintA('A')); + $this->metadata->addConstraint(new ConstraintB(null, ['TestGroup'])); $this->metadata->addPropertyConstraint('firstName', new ConstraintA()); $this->metadata->addGetterConstraint('lastName', new ConstraintB()); @@ -339,7 +328,6 @@ public function testCascadeConstraint() $metadata->addConstraint(new Cascade()); $this->assertSame(CascadingStrategy::CASCADE, $metadata->getCascadingStrategy()); - $this->assertCount(4, $metadata->properties); $this->assertSame([ 'requiredChild', 'optionalChild', @@ -354,7 +342,6 @@ public function testCascadeConstraintWithUnionTypeProperties() $metadata->addConstraint(new Cascade()); $this->assertSame(CascadingStrategy::CASCADE, $metadata->getCascadingStrategy()); - $this->assertCount(5, $metadata->properties); $this->assertSame([ 'classes', 'classAndArray', @@ -370,7 +357,6 @@ public function testCascadeConstraintWithIntersectionTypeProperties() $metadata->addConstraint(new Cascade()); $this->assertSame(CascadingStrategy::CASCADE, $metadata->getCascadingStrategy()); - $this->assertCount(1, $metadata->properties); $this->assertSame([ 'classes', ], $metadata->getConstrainedProperties()); @@ -383,7 +369,6 @@ public function testCascadeConstraintWithExcludedProperties() $metadata->addConstraint(new Cascade(exclude: ['requiredChild', 'optionalChild'])); $this->assertSame(CascadingStrategy::CASCADE, $metadata->getCascadingStrategy()); - $this->assertCount(2, $metadata->properties); $this->assertSame([ 'staticChild', 'children', @@ -395,9 +380,11 @@ class ClassCompositeConstraint extends Composite { public $nested; - public function getDefaultOption(): ?string + public function __construct(array $nested) { - return $this->getCompositeOption(); + $this->nested = $nested; + + parent::__construct(); } protected function getCompositeOption(): string diff --git a/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index 68f279ecf..afd650581 100644 --- a/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -20,6 +20,7 @@ use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Loader\LoaderInterface; +use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\EntityParent; @@ -140,7 +141,7 @@ public function testMetadataCacheWithRuntimeConstraint() public function testGroupsFromParent() { - $reader = new \Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader(); + $reader = new StaticMethodLoader(); $factory = new LazyLoadingMetadataFactory($reader); $metadata = $factory->getMetadataFor('Symfony\Component\Validator\Tests\Fixtures\EntityStaticCarTurbo'); $groups = []; diff --git a/Tests/Mapping/Loader/AttributeLoaderTest.php b/Tests/Mapping/Loader/AttributeLoaderTest.php index 9285117f9..7113fc743 100644 --- a/Tests/Mapping/Loader/AttributeLoaderTest.php +++ b/Tests/Mapping/Loader/AttributeLoaderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Attribute\ExtendsValidationFor; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\AtLeastOneOf; use Symfony\Component\Validator\Constraints\Callback; @@ -30,21 +31,27 @@ use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; +use Symfony\Component\Validator\Tests\Dummy\DummyGroupProvider; +use Symfony\Component\Validator\Tests\Fixtures\Attribute\GroupProviderDto; +use Symfony\Component\Validator\Tests\Fixtures\CallbackClass; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; +use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity; +use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\EntityParent; +use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\GroupSequenceProviderEntity; class AttributeLoaderTest extends TestCase { public function testLoadClassMetadataReturnsTrueIfSuccessful() { - $loader = $this->createAttributeLoader(); - $metadata = new ClassMetadata($this->getFixtureNamespace().'\Entity'); + $loader = new AttributeLoader(); + $metadata = new ClassMetadata(Entity::class); $this->assertTrue($loader->loadClassMetadata($metadata)); } public function testLoadClassMetadataReturnsFalseIfNotSuccessful() { - $loader = $this->createAttributeLoader(); + $loader = new AttributeLoader(); $metadata = new ClassMetadata('\stdClass'); $this->assertFalse($loader->loadClassMetadata($metadata)); @@ -52,17 +59,16 @@ public function testLoadClassMetadataReturnsFalseIfNotSuccessful() public function testLoadClassMetadata() { - $loader = $this->createAttributeLoader(); - $namespace = $this->getFixtureNamespace(); + $loader = new AttributeLoader(); - $metadata = new ClassMetadata($namespace.'\Entity'); + $metadata = new ClassMetadata(Entity::class); $loader->loadClassMetadata($metadata); - $expected = new ClassMetadata($namespace.'\Entity'); + $expected = new ClassMetadata(Entity::class); $expected->setGroupSequence(['Foo', 'Entity']); $expected->addConstraint(new ConstraintA()); - $expected->addConstraint(new Callback(['Symfony\Component\Validator\Tests\Fixtures\CallbackClass', 'callback'])); + $expected->addConstraint(new Callback([CallbackClass::class, 'callback'])); $expected->addConstraint(new Sequentially([ new Expression('this.getFirstName() != null'), ])); @@ -108,14 +114,13 @@ public function testLoadClassMetadata() */ public function testLoadParentClassMetadata() { - $loader = $this->createAttributeLoader(); - $namespace = $this->getFixtureNamespace(); + $loader = new AttributeLoader(); // Load Parent MetaData - $parent_metadata = new ClassMetadata($namespace.'\EntityParent'); + $parent_metadata = new ClassMetadata(EntityParent::class); $loader->loadClassMetadata($parent_metadata); - $expected_parent = new ClassMetadata($namespace.'\EntityParent'); + $expected_parent = new ClassMetadata(EntityParent::class); $expected_parent->addPropertyConstraint('other', new NotNull()); $expected_parent->getReflectionClass(); @@ -127,28 +132,27 @@ public function testLoadParentClassMetadata() */ public function testLoadClassMetadataAndMerge() { - $loader = $this->createAttributeLoader(); - $namespace = $this->getFixtureNamespace(); + $loader = new AttributeLoader(); // Load Parent MetaData - $parent_metadata = new ClassMetadata($namespace.'\EntityParent'); + $parent_metadata = new ClassMetadata(EntityParent::class); $loader->loadClassMetadata($parent_metadata); - $metadata = new ClassMetadata($namespace.'\Entity'); + $metadata = new ClassMetadata(Entity::class); $loader->loadClassMetadata($metadata); // Merge parent metaData. $metadata->mergeConstraints($parent_metadata); - $expected_parent = new ClassMetadata($namespace.'\EntityParent'); + $expected_parent = new ClassMetadata(EntityParent::class); $expected_parent->addPropertyConstraint('other', new NotNull()); $expected_parent->getReflectionClass(); - $expected = new ClassMetadata($namespace.'\Entity'); + $expected = new ClassMetadata(Entity::class); $expected->setGroupSequence(['Foo', 'Entity']); $expected->addConstraint(new ConstraintA()); - $expected->addConstraint(new Callback(['Symfony\Component\Validator\Tests\Fixtures\CallbackClass', 'callback'])); + $expected->addConstraint(new Callback([CallbackClass::class, 'callback'])); $expected->addConstraint(new Sequentially([ new Expression('this.getFirstName() != null'), ])); @@ -197,13 +201,12 @@ public function testLoadClassMetadataAndMerge() public function testLoadGroupSequenceProviderAttribute() { - $loader = $this->createAttributeLoader(); - $namespace = $this->getFixtureNamespace(); + $loader = new AttributeLoader(); - $metadata = new ClassMetadata($namespace.'\GroupSequenceProviderEntity'); + $metadata = new ClassMetadata(GroupSequenceProviderEntity::class); $loader->loadClassMetadata($metadata); - $expected = new ClassMetadata($namespace.'\GroupSequenceProviderEntity'); + $expected = new ClassMetadata(GroupSequenceProviderEntity::class); $expected->setGroupSequenceProvider(true); $expected->getReflectionClass(); @@ -212,32 +215,137 @@ public function testLoadGroupSequenceProviderAttribute() public function testLoadExternalGroupSequenceProvider() { - $loader = $this->createAttributeLoader(); - $namespace = $this->getFixtureAttributeNamespace(); + $loader = new AttributeLoader(); - $metadata = new ClassMetadata($namespace.'\GroupProviderDto'); + $metadata = new ClassMetadata(GroupProviderDto::class); $loader->loadClassMetadata($metadata); - $expected = new ClassMetadata($namespace.'\GroupProviderDto'); - $expected->setGroupProvider('Symfony\Component\Validator\Tests\Dummy\DummyGroupProvider'); + $expected = new ClassMetadata(GroupProviderDto::class); + $expected->setGroupProvider(DummyGroupProvider::class); $expected->setGroupSequenceProvider(true); $expected->getReflectionClass(); $this->assertEquals($expected, $metadata); } - protected function createAttributeLoader(): AttributeLoader + public function testGetMappedClasses() { - return new AttributeLoader(); + $classes = [ + 'App\Entity\User' => ['App\Entity\User'], + 'App\Entity\Product' => ['App\Entity\Product'], + 'App\Entity\Order' => ['App\Entity\Order'], + ]; + $loader = new AttributeLoader(false, $classes); + + $this->assertSame(array_keys($classes), $loader->getMappedClasses()); } - protected function getFixtureNamespace(): string + public function testLoadClassMetadataReturnsFalseForUnmappedClass() { - return 'Symfony\Component\Validator\Tests\Fixtures\NestedAttribute'; + $loader = new AttributeLoader(false, ['App\Entity\User' => ['App\Entity\User']]); + $metadata = new ClassMetadata('App\Entity\Product'); + + $this->assertFalse($loader->loadClassMetadata($metadata)); + } + + public function testLoadClassMetadataReturnsFalseForClassWithoutAttributes() + { + $loader = new AttributeLoader(false, ['stdClass' => ['stdClass']]); + $metadata = new ClassMetadata('stdClass'); + + $this->assertFalse($loader->loadClassMetadata($metadata)); + } + + public function testLoadClassMetadataForMappedClassWithAttributes() + { + $loader = new AttributeLoader(false, [Entity::class => [Entity::class]]); + $metadata = new ClassMetadata(Entity::class); + + $this->assertTrue($loader->loadClassMetadata($metadata)); + + $this->assertNotEmpty($metadata->getConstraints()); + } + + public function testLoadClassMetadataFromExplicitAttributeMappings() + { + $targetClass = _AttrMap_Target::class; + $sourceClass = _AttrMap_Source::class; + + $loader = new AttributeLoader(false, [$targetClass => [$sourceClass]]); + $metadata = new ClassMetadata($targetClass); + + $this->assertTrue($loader->loadClassMetadata($metadata)); + $this->assertInstanceOf(NotBlank::class, $metadata->getPropertyMetadata('name', $sourceClass)[0]->getConstraints()[0]); } - protected function getFixtureAttributeNamespace(): string + public function testLoadClassMetadataWithClassLevelConstraints() { - return 'Symfony\Component\Validator\Tests\Fixtures\Attribute'; + $targetClass = _AttrMap_Target::class; + $sourceClass = _AttrMap_ClassLevelSource::class; + + $loader = new AttributeLoader(false, [$targetClass => [$sourceClass]]); + $metadata = new ClassMetadata($targetClass); + + $this->assertTrue($loader->loadClassMetadata($metadata)); + + // Check that class-level constraints are added to the target + $constraints = $metadata->getConstraints(); + $this->assertCount(2, $constraints); + + // Check for Callback constraint + $callbackConstraint = null; + foreach ($constraints as $constraint) { + if ($constraint instanceof Callback) { + $callbackConstraint = $constraint; + break; + } + } + $this->assertInstanceOf(Callback::class, $callbackConstraint); + $this->assertEquals('validateClass', $callbackConstraint->callback); + + // Check for Expression constraint + $expressionConstraint = null; + foreach ($constraints as $constraint) { + if ($constraint instanceof Expression) { + $expressionConstraint = $constraint; + break; + } + } + $this->assertInstanceOf(Expression::class, $expressionConstraint); + $this->assertEquals('this.name != null', $expressionConstraint->expression); + + // Check that property constraints are also added + $this->assertInstanceOf(NotBlank::class, $metadata->getPropertyMetadata('name', $sourceClass)[0]->getConstraints()[0]); + } +} + +class _AttrMap_Target +{ + public string $name; + + public function getName() + { + return $this->name; } + + public function validateClass() + { + // This method will be called by the Callback constraint + return true; + } +} + +#[ExtendsValidationFor(_AttrMap_Target::class)] +class _AttrMap_Source +{ + #[NotBlank] public string $name; +} + +#[ExtendsValidationFor(_AttrMap_Target::class)] +#[Callback('validateClass')] +#[Expression('this.name != null')] +class _AttrMap_ClassLevelSource +{ + #[NotBlank] + public string $name = ''; } diff --git a/Tests/Mapping/Loader/Fixtures/ConstraintWithNamedArguments.php b/Tests/Mapping/Loader/Fixtures/ConstraintWithNamedArguments.php index 70579011c..8dfc6dd1b 100644 --- a/Tests/Mapping/Loader/Fixtures/ConstraintWithNamedArguments.php +++ b/Tests/Mapping/Loader/Fixtures/ConstraintWithNamedArguments.php @@ -21,7 +21,7 @@ class ConstraintWithNamedArguments extends Constraint #[HasNamedArguments] public function __construct(array|string|null $choices = [], ?array $groups = null) { - parent::__construct([], $groups); + parent::__construct(null, $groups); $this->choices = $choices; } diff --git a/Tests/Mapping/Loader/Fixtures/ConstraintWithoutValueWithNamedArguments.php b/Tests/Mapping/Loader/Fixtures/ConstraintWithoutValueWithNamedArguments.php index af950fc13..48b67362c 100644 --- a/Tests/Mapping/Loader/Fixtures/ConstraintWithoutValueWithNamedArguments.php +++ b/Tests/Mapping/Loader/Fixtures/ConstraintWithoutValueWithNamedArguments.php @@ -19,7 +19,7 @@ class ConstraintWithoutValueWithNamedArguments extends Constraint #[HasNamedArguments] public function __construct(?array $groups = null) { - parent::__construct([], $groups); + parent::__construct(null, $groups); } public function getTargets(): string diff --git a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php index ae5253a3f..19c904544 100644 --- a/Tests/Mapping/Loader/PropertyInfoLoaderTest.php +++ b/Tests/Mapping/Loader/PropertyInfoLoaderTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; @@ -224,9 +225,7 @@ public function getTypes(string $class, string $property, array $context = []): $this->assertCount(0, $noAutoMappingConstraints, 'DisableAutoMapping constraint is not added in the list'); } - /** - * @dataProvider regexpProvider - */ + #[DataProvider('regexpProvider')] public function testClassValidator(bool $expected, ?string $classValidatorRegexp = null) { $propertyListExtractor = $this->createMock(PropertyListExtractorInterface::class); @@ -299,7 +298,7 @@ public function getTypes(string $class, string $property, array $context = []): /** @var ClassMetadata $classMetadata */ $classMetadata = $validator->getMetadataFor(new PropertyInfoLoaderNoAutoMappingEntity()); $this->assertSame([], $classMetadata->getPropertyMetadata('string')); - $this->assertCount(2, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->constraints); + $this->assertCount(2, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->getConstraints()); $this->assertSame(AutoMappingStrategy::ENABLED, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->getAutoMappingStrategy()); } } diff --git a/Tests/Mapping/Loader/XmlFileLoaderTest.php b/Tests/Mapping/Loader/XmlFileLoaderTest.php index 03757720c..6490a8b13 100644 --- a/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -11,8 +11,9 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; 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; @@ -23,11 +24,13 @@ use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Constraints\Regex; use Symfony\Component\Validator\Constraints\Traverse; +use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Exception\MappingException; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader; use Symfony\Component\Validator\Tests\Dummy\DummyGroupProvider; use Symfony\Component\Validator\Tests\Fixtures\Attribute\GroupProviderDto; +use Symfony\Component\Validator\Tests\Fixtures\CallbackClass; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; use Symfony\Component\Validator\Tests\Fixtures\ConstraintB; use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithRequiredArgument; @@ -40,8 +43,6 @@ class XmlFileLoaderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testLoadClassMetadataReturnsTrueIfSuccessful() { $loader = new XmlFileLoader(__DIR__.'/constraint-mapping.xml'); @@ -77,8 +78,6 @@ 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 Choice(['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: [ @@ -96,6 +95,23 @@ public function testLoadClassMetadata() $this->assertEquals($expected, $metadata); } + #[IgnoreDeprecations] + #[Group('legacy')] + public function testLoadClassMetadataValueOption() + { + $loader = new XmlFileLoader(__DIR__.'/constraint-mapping-value-option.xml'); + $metadata = new ClassMetadata(Entity::class); + + $loader->loadClassMetadata($metadata); + + $expected = new ClassMetadata(Entity::class); + $expected->addConstraint(new Callback([CallbackClass::class, 'callback'])); + $expected->addPropertyConstraint('firstName', new Type(type: 'string')); + $expected->addPropertyConstraint('firstName', new Choice(choices: ['A', 'B'])); + + $this->assertEquals($expected, $metadata); + } + public function testLoadClassMetadataWithNonStrings() { $loader = new XmlFileLoader(__DIR__.'/constraint-mapping-non-strings.xml'); @@ -177,9 +193,8 @@ public function testDoNotModifyStateIfExceptionIsThrown() } } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testLoadConstraintWithoutNamedArgumentsSupport() { $loader = new XmlFileLoader(__DIR__.'/constraint-without-named-arguments-support.xml'); @@ -190,9 +205,8 @@ public function testLoadConstraintWithoutNamedArgumentsSupport() $loader->loadClassMetadata($metadata); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testLengthConstraintValueOptionTriggersDeprecation() { $loader = new XmlFileLoader(__DIR__.'/constraint-mapping-exactly-value.xml'); @@ -201,7 +215,7 @@ public function testLengthConstraintValueOptionTriggersDeprecation() $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; + $constraints = $metadata->getPropertyMetadata('title')[0]->getConstraints(); self::assertCount(1, $constraints); self::assertInstanceOf(Length::class, $constraints[0]); diff --git a/Tests/Mapping/Loader/YamlFileLoaderTest.php b/Tests/Mapping/Loader/YamlFileLoaderTest.php index c3bbcb18e..8b2077931 100644 --- a/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -11,8 +11,10 @@ namespace Symfony\Component\Validator\Tests\Mapping\Loader; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; 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; @@ -20,10 +22,12 @@ use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader; use Symfony\Component\Validator\Tests\Dummy\DummyGroupProvider; use Symfony\Component\Validator\Tests\Fixtures\Attribute\GroupProviderDto; +use Symfony\Component\Validator\Tests\Fixtures\CallbackClass; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; use Symfony\Component\Validator\Tests\Fixtures\ConstraintB; use Symfony\Component\Validator\Tests\Fixtures\ConstraintWithRequiredArgument; @@ -36,8 +40,6 @@ class YamlFileLoaderTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testLoadClassMetadataReturnsFalseIfEmpty() { $loader = new YamlFileLoader(__DIR__.'/empty-mapping.yml'); @@ -49,9 +51,7 @@ public function testLoadClassMetadataReturnsFalseIfEmpty() $this->assertSame([], $r->getValue($loader)); } - /** - * @dataProvider provideInvalidYamlFiles - */ + #[DataProvider('provideInvalidYamlFiles')] public function testInvalidYamlFiles($path) { $loader = new YamlFileLoader(__DIR__.'/'.$path); @@ -120,8 +120,6 @@ 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 Choice(['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: [ @@ -139,6 +137,24 @@ public function testLoadClassMetadata() $this->assertEquals($expected, $metadata); } + #[IgnoreDeprecations] + #[Group('legacy')] + public function testLoadClassMetadataValueOption() + { + $loader = new YamlFileLoader(__DIR__.'/constraint-mapping-value-option.yml'); + $metadata = new ClassMetadata(Entity::class); + + $loader->loadClassMetadata($metadata); + + $expected = new ClassMetadata(Entity::class); + $expected->addConstraint(new Callback('validateMeStatic')); + $expected->addConstraint(new Callback([CallbackClass::class, 'callback'])); + $expected->addPropertyConstraint('firstName', new Type(type: 'string')); + $expected->addPropertyConstraint('firstName', new Choice(choices: ['A', 'B'])); + + $this->assertEquals($expected, $metadata); + } + public function testLoadClassMetadataWithConstants() { $loader = new YamlFileLoader(__DIR__.'/mapping-with-constants.yml'); @@ -192,9 +208,8 @@ public function testLoadGroupProvider() $this->assertEquals($expected, $metadata); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testLoadConstraintWithoutNamedArgumentsSupport() { $loader = new YamlFileLoader(__DIR__.'/constraint-without-named-arguments-support.yml'); diff --git a/Tests/Mapping/Loader/constraint-mapping-value-option.xml b/Tests/Mapping/Loader/constraint-mapping-value-option.xml new file mode 100644 index 000000000..f7f5bdb55 --- /dev/null +++ b/Tests/Mapping/Loader/constraint-mapping-value-option.xml @@ -0,0 +1,31 @@ + + + + + Symfony\Component\Validator\Tests\Fixtures\ + + + + Symfony\Component\Validator\Tests\Fixtures\CallbackClass + callback + + + + + + + + string + + + + A + B + + + + + + diff --git a/Tests/Mapping/Loader/constraint-mapping-value-option.yml b/Tests/Mapping/Loader/constraint-mapping-value-option.yml new file mode 100644 index 000000000..093696c8f --- /dev/null +++ b/Tests/Mapping/Loader/constraint-mapping-value-option.yml @@ -0,0 +1,13 @@ +namespaces: + custom: Symfony\Component\Validator\Tests\Fixtures\ + +Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity: + constraints: + - Callback: validateMeStatic + - Callback: [Symfony\Component\Validator\Tests\Fixtures\CallbackClass, callback] + properties: + firstName: + # Constraint with single value + - Type: string + # Constraint with multiple values + - Choice: [A, B] diff --git a/Tests/Mapping/Loader/constraint-mapping.xml b/Tests/Mapping/Loader/constraint-mapping.xml index 8a7975f11..588833208 100644 --- a/Tests/Mapping/Loader/constraint-mapping.xml +++ b/Tests/Mapping/Loader/constraint-mapping.xml @@ -27,8 +27,10 @@ validateMeStatic - Symfony\Component\Validator\Tests\Fixtures\CallbackClass - callback + @@ -43,8 +45,10 @@ - foo - bar + @@ -59,24 +63,14 @@ - - - - - - - - A - B - - - - - - - + diff --git a/Tests/Mapping/Loader/constraint-mapping.yml b/Tests/Mapping/Loader/constraint-mapping.yml index af091a89f..e31a64189 100644 --- a/Tests/Mapping/Loader/constraint-mapping.yml +++ b/Tests/Mapping/Loader/constraint-mapping.yml @@ -12,30 +12,31 @@ Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity: # Custom constraint with namespaces prefix - "custom:ConstraintB": ~ # Callbacks - - Callback: validateMe - - Callback: validateMeStatic - - Callback: [Symfony\Component\Validator\Tests\Fixtures\CallbackClass, callback] + - Callback: + callback: validateMe + - Callback: + callback: validateMeStatic + - Callback: + callback: [Symfony\Component\Validator\Tests\Fixtures\CallbackClass, callback] # Constraint with named arguments support without value - Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithoutValueWithNamedArguments: ~ # Constraint with named arguments support with scalar value - - Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithNamedArguments: foo + - Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithNamedArguments: + choices: foo # Constraint with named arguments support with array value - - Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithNamedArguments: [foo, bar] + - Symfony\Component\Validator\Tests\Mapping\Loader\Fixtures\ConstraintWithNamedArguments: + choices: [foo, bar] properties: firstName: # Constraint without value - NotNull: ~ - # Constraint with single value - - Range: - min: 3 - # Constraint with multiple values - - Choice: [A, B] # Constraint with child constraints - All: - - NotNull: ~ - - Range: - min: 3 + constraints: + - NotNull: ~ + - Range: + min: 3 # Option with child constraints - All: constraints: diff --git a/Tests/Mapping/MemberMetadataTest.php b/Tests/Mapping/MemberMetadataTest.php index 84d047f10..d5dfb9ec0 100644 --- a/Tests/Mapping/MemberMetadataTest.php +++ b/Tests/Mapping/MemberMetadataTest.php @@ -74,8 +74,8 @@ public function testAddCompositeConstraintAcceptsDeepNestedPropertyConstraints() public function testSerialize() { - $this->metadata->addConstraint(new ConstraintA(['property1' => 'A'])); - $this->metadata->addConstraint(new ConstraintB(['groups' => 'TestGroup'])); + $this->metadata->addConstraint(new ConstraintA('A')); + $this->metadata->addConstraint(new ConstraintB(null, ['TestGroup'])); $metadata = unserialize(serialize($this->metadata)); @@ -116,9 +116,11 @@ class PropertyCompositeConstraint extends Composite { public $nested; - public function getDefaultOption(): ?string + public function __construct(array $nested) { - return $this->getCompositeOption(); + $this->nested = $nested; + + parent::__construct(); } protected function getCompositeOption(): string diff --git a/Tests/Mapping/PropertyMetadataTest.php b/Tests/Mapping/PropertyMetadataTest.php index bacd572c9..dbcdb0897 100644 --- a/Tests/Mapping/PropertyMetadataTest.php +++ b/Tests/Mapping/PropertyMetadataTest.php @@ -53,8 +53,10 @@ public function testGetPropertyValueFromOverriddenPrivateProperty() public function testGetPropertyValueFromRemovedProperty() { $entity = new Entity('foobar'); - $metadata = new PropertyMetadata(self::CLASSNAME, 'internal'); - $metadata->name = 'test'; + + // simulate out-of-sync metadata + $metadata = 'O:52:"Symfony\Component\Validator\Mapping\PropertyMetadata":3:{s:5:"class";s:65:"Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity";s:4:"name";s:7:"missing";s:8:"property";s:7:"missing";}'; + $metadata = unserialize($metadata); $this->expectException(ValidatorException::class); $metadata->getPropertyValue($entity); diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index 326ed4fc4..acb102993 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -11,14 +11,13 @@ namespace Symfony\Component\Validator\Tests\Resources; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Translation\Util\XliffUtils; class TranslationFilesTest extends TestCase { - /** - * @dataProvider provideTranslationFiles - */ + #[DataProvider('provideTranslationFiles')] public function testTranslationFileIsValid($filePath) { $document = new \DOMDocument(); @@ -29,9 +28,7 @@ public function testTranslationFileIsValid($filePath) $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } - /** - * @dataProvider provideTranslationFiles - */ + #[DataProvider('provideTranslationFiles')] public function testTranslationFileIsValidWithoutEntityLoader($filePath) { $document = new \DOMDocument(); diff --git a/Tests/Util/PropertyPathTest.php b/Tests/Util/PropertyPathTest.php index f7f33d476..9ba408c0f 100644 --- a/Tests/Util/PropertyPathTest.php +++ b/Tests/Util/PropertyPathTest.php @@ -11,14 +11,13 @@ namespace Symfony\Component\Validator\Tests\Util; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Util\PropertyPath; class PropertyPathTest extends TestCase { - /** - * @dataProvider provideAppendPaths - */ + #[DataProvider('provideAppendPaths')] public function testAppend($basePath, $subPath, $expectedPath, $message) { $this->assertSame($expectedPath, PropertyPath::append($basePath, $subPath), $message); diff --git a/Tests/Validator/RecursiveValidatorTest.php b/Tests/Validator/RecursiveValidatorTest.php index 1ae14ba30..328a67c40 100644 --- a/Tests/Validator/RecursiveValidatorTest.php +++ b/Tests/Validator/RecursiveValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests\Validator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Symfony\Component\Translation\IdentityTranslator; @@ -120,7 +121,7 @@ public function testValidate() $violations = $this->validate('Bernhard', $constraint, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -156,7 +157,7 @@ public function testClassConstraint() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -195,7 +196,7 @@ public function testPropertyConstraint() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -234,7 +235,7 @@ public function testGetterConstraint() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -271,7 +272,7 @@ public function testArray() $violations = $this->validate($array, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -308,7 +309,7 @@ public function testRecursiveArray() $violations = $this->validate($array, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -345,7 +346,7 @@ public function testTraversable() $violations = $this->validate($traversable, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -384,7 +385,7 @@ public function testRecursiveTraversable() $violations = $this->validate($traversable, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -422,7 +423,7 @@ public function testReferenceClassConstraint() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -463,7 +464,7 @@ public function testReferencePropertyConstraint() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -504,7 +505,7 @@ public function testReferenceGetterConstraint() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -525,7 +526,7 @@ public function testsIgnoreNullReference() $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -541,9 +542,7 @@ public function testFailOnScalarReferences() $this->validate($entity); } - /** - * @dataProvider getConstraintMethods - */ + #[DataProvider('getConstraintMethods')] public function testArrayReference($constraintMethod) { $entity = new Entity(); @@ -570,7 +569,7 @@ public function testArrayReference($constraintMethod) $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -582,9 +581,7 @@ public function testArrayReference($constraintMethod) $this->assertNull($violations[0]->getCode()); } - /** - * @dataProvider getConstraintMethods - */ + #[DataProvider('getConstraintMethods')] public function testRecursiveArrayReference($constraintMethod) { $entity = new Entity(); @@ -611,7 +608,7 @@ public function testRecursiveArrayReference($constraintMethod) $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -643,13 +640,11 @@ public function testOnlyCascadedArraysAreTraversed() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } - /** - * @dataProvider getConstraintMethods - */ + #[DataProvider('getConstraintMethods')] public function testArrayTraversalCannotBeDisabled($constraintMethod) { $entity = new Entity(); @@ -666,13 +661,11 @@ public function testArrayTraversalCannotBeDisabled($constraintMethod) $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } - /** - * @dataProvider getConstraintMethods - */ + #[DataProvider('getConstraintMethods')] public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod) { $entity = new Entity(); @@ -690,13 +683,11 @@ public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod) $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } - /** - * @dataProvider getConstraintMethods - */ + #[DataProvider('getConstraintMethods')] public function testIgnoreScalarsDuringArrayTraversal($constraintMethod) { $entity = new Entity(); @@ -706,13 +697,11 @@ public function testIgnoreScalarsDuringArrayTraversal($constraintMethod) $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } - /** - * @dataProvider getConstraintMethods - */ + #[DataProvider('getConstraintMethods')] public function testIgnoreNullDuringArrayTraversal($constraintMethod) { $entity = new Entity(); @@ -722,7 +711,7 @@ public function testIgnoreNullDuringArrayTraversal($constraintMethod) $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -752,7 +741,7 @@ public function testTraversableReference() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -781,7 +770,7 @@ public function testDisableTraversableTraversal() $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -829,7 +818,7 @@ public function testEnableRecursiveTraversableTraversal() $violations = $this->validate($entity, null, 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -877,7 +866,7 @@ public function testValidateProperty() $violations = $this->validateProperty($entity, 'firstName', 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -940,7 +929,7 @@ public function testValidatePropertyValue() 'Group' ); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -989,7 +978,7 @@ public function testValidatePropertyValueWithClassName() 'Group' ); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -1028,7 +1017,7 @@ public function testValidateObjectOnlyOncePerGroup() $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } @@ -1048,7 +1037,7 @@ public function testValidateDifferentObjectsSeparately() $violations = $this->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(2, $violations); } @@ -1071,7 +1060,7 @@ public function testValidateSingleGroup() $violations = $this->validate($entity, null, 'Group 2'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } @@ -1094,7 +1083,7 @@ public function testValidateMultipleGroups() $violations = $this->validate($entity, null, ['Group 1', 'Group 2']); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(2, $violations); } @@ -1127,7 +1116,7 @@ public function testReplaceDefaultGroupByGroupSequenceObject() $violations = $this->validate($entity, null, 'Default'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); } @@ -1161,7 +1150,7 @@ public function testReplaceDefaultGroupByGroupSequenceArray() $violations = $this->validate($entity, null, 'Default'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); } @@ -1193,7 +1182,7 @@ public function testPropagateDefaultGroupToReferenceWhenReplacingDefaultGroup() $violations = $this->validate($entity, null, 'Default'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Violation in Default group', $violations[0]->getMessage()); } @@ -1223,14 +1212,12 @@ public function testValidateCustomGroupWhenDefaultGroupWasReplaced() $violations = $this->validate($entity, null, 'Other Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Violation in other group', $violations[0]->getMessage()); } - /** - * @dataProvider getTestReplaceDefaultGroup - */ + #[DataProvider('getTestReplaceDefaultGroup')] public function testReplaceDefaultGroup($sequence, array $assertViolations) { $entity = new GroupSequenceProviderEntity($sequence); @@ -1261,7 +1248,7 @@ public function testReplaceDefaultGroup($sequence, array $assertViolations) $violations = $this->validate($entity, null, 'Default'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(\count($assertViolations), $violations); foreach ($assertViolations as $key => $message) { $this->assertSame($message, $violations[$key]->getMessage()); @@ -1364,7 +1351,7 @@ public function testGroupSequenceAbortsAfterFailedGroup() $sequence = new GroupSequence(['Group 1', 'Group 2', 'Group 3']); $violations = $this->validator->validate($entity, new Valid(), $sequence); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message 1', $violations[0]->getMessage()); } @@ -1394,7 +1381,7 @@ public function testGroupSequenceIncludesReferences() $sequence = new GroupSequence(['Group 1', 'Entity']); $violations = $this->validator->validate($entity, new Valid(), $sequence); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Reference violation 1', $violations[0]->getMessage()); } @@ -1412,7 +1399,7 @@ public function testValidateInSeparateContext() ->validate($value->reference, new Valid(), 'Group') ; - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -1453,7 +1440,7 @@ public function testValidateInSeparateContext() $violations = $this->validator->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Separate violation', $violations[0]->getMessage()); } @@ -1509,7 +1496,7 @@ public function testValidateInContext() $violations = $this->validator->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -1572,7 +1559,7 @@ public function testValidateArrayInContext() $violations = $this->validator->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -1610,7 +1597,7 @@ public function testTraverseTraversableByDefault() $violations = $this->validate($traversable, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -1642,7 +1629,7 @@ public function testTraversalEnabledOnClass() $violations = $this->validate($traversable, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } @@ -1666,7 +1653,7 @@ public function testTraversalDisabledOnClass() $violations = $this->validate($traversable, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -1700,7 +1687,7 @@ public function testReferenceTraversalDisabledOnClass() $violations = $this->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -1727,7 +1714,7 @@ public function testReferenceTraversalEnabledOnReferenceDisabledOnClass() $violations = $this->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -1754,7 +1741,7 @@ public function testReferenceTraversalDisabledOnReferenceEnabledOnClass() $violations = $this->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -1774,7 +1761,7 @@ public function testReferenceCascadeDisabledByDefault() $violations = $this->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -1796,7 +1783,7 @@ public function testReferenceCascadeEnabledIgnoresUntyped() $violations = $this->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(0, $violations); } @@ -1826,7 +1813,7 @@ public function testTypedReferenceCascadeEnabled() $violations = $this->validate($entity, new Valid(), 'Group'); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertInstanceOf(Callback::class, $violations->get(0)->getConstraint()); } @@ -1848,7 +1835,7 @@ public function testAddCustomizedViolation() $violations = $this->validator->validate($entity); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); $this->assertSame('Message value', $violations[0]->getMessage()); $this->assertSame('Message %param%', $violations[0]->getMessageTemplate()); @@ -1875,7 +1862,7 @@ public function testNoDuplicateValidationIfClassConstraintInMultipleGroups() $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } @@ -1894,7 +1881,7 @@ public function testNoDuplicateValidationIfPropertyConstraintInMultipleGroups() $violations = $this->validator->validate($entity, new Valid(), ['Group 1', 'Group 2']); - /** @var ConstraintViolationInterface[] $violations */ + /* @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); } diff --git a/Validator/RecursiveContextualValidator.php b/Validator/RecursiveContextualValidator.php index 9805bdcd4..55af3fc90 100644 --- a/Validator/RecursiveContextualValidator.php +++ b/Validator/RecursiveContextualValidator.php @@ -27,6 +27,7 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Exception\UnsupportedMetadataException; use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Component\Validator\GroupSequenceProviderInterface; use Symfony\Component\Validator\Mapping\CascadingStrategy; use Symfony\Component\Validator\Mapping\ClassMetadataInterface; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; @@ -443,7 +444,7 @@ private function validateClassNode(object $object, ?string $cacheKey, ClassMetad } else { // The group sequence is dynamically obtained from the validated // object - /** @var \Symfony\Component\Validator\GroupSequenceProviderInterface $object */ + /** @var GroupSequenceProviderInterface $object */ $group = $object->getGroupSequence(); } $defaultOverridden = true; diff --git a/ValidatorBuilder.php b/ValidatorBuilder.php index 917f1c57a..b51299016 100644 --- a/ValidatorBuilder.php +++ b/ValidatorBuilder.php @@ -44,6 +44,7 @@ class ValidatorBuilder private array $xmlMappings = []; private array $yamlMappings = []; private array $methodMappings = []; + private array $attributeMappings = []; private bool $enableAttributeMapping = false; private ?MetadataFactoryInterface $metadataFactory = null; private ConstraintValidatorFactoryInterface $validatorFactory; @@ -148,6 +149,27 @@ public function addYamlMappings(array $paths): static return $this; } + /** + * Adds classes with mapping constraints as attributes. + * + * The keys are the classes on which the constraints should be added. + * The values are the classes that contain the constraints to add as attributes. + * + * @param array> $classes + * + * @return $this + */ + public function addAttributeMappings(array $classes): static + { + if (null !== $this->metadataFactory) { + throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.'); + } + + $this->attributeMappings = array_merge_recursive($this->attributeMappings, $classes); + + return $this; + } + /** * Enables constraint mapping using the given static method. * @@ -217,7 +239,7 @@ public function disableAttributeMapping(): static */ public function setMetadataFactory(MetadataFactoryInterface $metadataFactory): static { - if (\count($this->xmlMappings) > 0 || \count($this->yamlMappings) > 0 || \count($this->methodMappings) > 0 || $this->enableAttributeMapping) { + if ($this->xmlMappings || $this->yamlMappings || $this->methodMappings || $this->attributeMappings || $this->enableAttributeMapping) { throw new ValidatorException('You cannot set a custom metadata factory after adding custom mappings. You should do either of both.'); } @@ -331,8 +353,8 @@ public function getLoaders(): array $loaders[] = new StaticMethodLoader($methodName); } - if ($this->enableAttributeMapping) { - $loaders[] = new AttributeLoader(); + if ($this->enableAttributeMapping || $this->attributeMappings) { + $loaders[] = new AttributeLoader($this->enableAttributeMapping, $this->attributeMappings); } return array_merge($loaders, $this->loaders); diff --git a/Violation/ConstraintViolationBuilderInterface.php b/Violation/ConstraintViolationBuilderInterface.php index 195dec924..4c0de8913 100644 --- a/Violation/ConstraintViolationBuilderInterface.php +++ b/Violation/ConstraintViolationBuilderInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Violation; +use Symfony\Contracts\Translation\TranslatorInterface; + /** * Builds {@link \Symfony\Component\Validator\ConstraintViolationInterface} * objects. @@ -69,7 +71,7 @@ public function disableTranslation(): static; * * @return $this * - * @see \Symfony\Contracts\Translation\TranslatorInterface + * @see TranslatorInterface */ public function setTranslationDomain(string $translationDomain): static; diff --git a/composer.json b/composer.json index 368373f53..0af683361 100644 --- a/composer.json +++ b/composer.json @@ -24,22 +24,23 @@ "symfony/translation-contracts": "^2.5|^3" }, "require-dev": { - "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/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4.3|^7.0.3|^8.0", "symfony/type-info": "^7.1.8", "egulias/email-validator": "^2.1.10|^3|^4" }, @@ -52,6 +53,7 @@ "symfony/intl": "<6.4", "symfony/property-info": "<6.4", "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/var-exporter": "<6.4.25|>=7.0,<7.3.3", "symfony/yaml": "<6.4" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 8288431b6..cdd1c74a9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,11 @@ @@ -18,7 +19,7 @@ - + ./ @@ -27,5 +28,9 @@ ./Tests ./vendor - + + + + +