diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index cf1358e94de9..1a0a50b427bb 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Add support for `Stringable` values when using the `Cidr`, `CssColor`, `ExpressionSyntax` and `PasswordStrength` constraints * Add `MacAddress` constraint * Add `list` and `associative_array` types to `Type` constraint * Add the `Charset` constraint diff --git a/src/Symfony/Component/Validator/Constraints/CidrValidator.php b/src/Symfony/Component/Validator/Constraints/CidrValidator.php index c90ebcfae35f..38168ebb4747 100644 --- a/src/Symfony/Component/Validator/Constraints/CidrValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CidrValidator.php @@ -28,7 +28,7 @@ public function validate($value, Constraint $constraint): void return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } diff --git a/src/Symfony/Component/Validator/Constraints/CssColorValidator.php b/src/Symfony/Component/Validator/Constraints/CssColorValidator.php index 9e8b1b55c194..d55191b412b1 100644 --- a/src/Symfony/Component/Validator/Constraints/CssColorValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CssColorValidator.php @@ -62,7 +62,7 @@ public function validate($value, Constraint $constraint): void return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } @@ -76,7 +76,7 @@ public function validate($value, Constraint $constraint): void } $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue((string) $value)) ->setCode(CssColor::INVALID_FORMAT_ERROR) ->addViolation(); } diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionSyntaxValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionSyntaxValidator.php index 4b20de302ad8..c0c758d6a52b 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionSyntaxValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionSyntaxValidator.php @@ -40,7 +40,7 @@ public function validate(mixed $expression, Constraint $constraint): void return; } - if (!\is_string($expression)) { + if (!\is_string($expression) && !$expression instanceof \Stringable) { throw new UnexpectedValueException($expression, 'string'); } diff --git a/src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php b/src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php index c3d2b7d76a2b..72227b85af59 100644 --- a/src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php +++ b/src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php @@ -36,11 +36,11 @@ public function validate(#[\SensitiveParameter] mixed $value, Constraint $constr return; } - if (!\is_string($value)) { + if (!\is_string($value) && !$value instanceof \Stringable) { throw new UnexpectedValueException($value, 'string'); } $passwordStrengthEstimator = $this->passwordStrengthEstimator ?? self::estimateStrength(...); - $strength = $passwordStrengthEstimator($value); + $strength = $passwordStrengthEstimator((string) $value); if ($strength < $constraint->minScore) { $this->context->buildViolation($constraint->message) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CharsetValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CharsetValidatorTest.php index 9044e1c125f8..4d33080d081b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CharsetValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CharsetValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\CharsetValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class CharsetValidatorTest extends ConstraintValidatorTestCase { @@ -66,12 +67,7 @@ public static function provideValidValues() yield ['my ûtf 8', ['ASCII', 'UTF-8']]; yield ['my ûtf 8', ['UTF-8']]; yield ['string', ['ISO-8859-1']]; - yield [new class() implements \Stringable { - public function __toString(): string - { - return 'my ûtf 8'; - } - }, ['UTF-8']]; + yield [new StringableValue('my ûtf 8'), ['UTF-8']]; } public static function provideInvalidValues() diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CidrValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CidrValidatorTest.php index 7c5745ee6942..59caf3872c79 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CidrValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CidrValidatorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class CidrValidatorTest extends ConstraintValidatorTestCase { @@ -83,7 +84,7 @@ public function testInvalidIpValue(string $cidr) /** * @dataProvider getValid */ - public function testValidCidr(string $cidr, string $version) + public function testValidCidr(string|\Stringable $cidr, string $version) { $this->validator->validate($cidr, new Cidr(['version' => $version])); @@ -93,7 +94,7 @@ public function testValidCidr(string $cidr, string $version) /** * @dataProvider getWithInvalidMasksAndIps */ - public function testInvalidIpAddressAndNetmask(string $cidr) + public function testInvalidIpAddressAndNetmask(string|\Stringable $cidr) { $this->validator->validate($cidr, new Cidr()); $this @@ -195,6 +196,7 @@ public static function getValid(): array ['::255.255.255.255/32', Ip::V6], ['::123.45.67.178/120', Ip::V6], ['::123.45.67.178/120', Ip::ALL], + [new StringableValue('::123.45.67.178/120'), Ip::ALL], ]; } @@ -233,6 +235,7 @@ public static function getWithInvalidMasksAndIps(): array ['::0.0.0/a/'], ['::256.0.0.0/-1aa'], ['::0.256.0.0/1b'], + [new StringableValue('::0.256.0.0/1b')], ]; } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php index 081d41a57c70..1f509130eb01 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\CssColorValidator; use Symfony\Component\Validator\Exception\UnexpectedValueException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; final class CssColorValidatorTest extends ConstraintValidatorTestCase { @@ -67,6 +68,7 @@ public static function getValidAnyColor(): array ['rgba(255, 255, 255, 0.3)'], ['hsl(0, 0%, 20%)'], ['hsla(0, 0%, 20%, 0.4)'], + [new StringableValue('hsla(0, 0%, 20%, 0.4)')], ]; } @@ -323,7 +325,7 @@ public function testInvalidNamedColors($cssColor) public static function getInvalidNamedColors(): array { - return [['fabpot'], ['ngrekas'], ['symfony'], ['FABPOT'], ['NGREKAS'], ['SYMFONY']]; + return [['fabpot'], ['ngrekas'], ['symfony'], ['FABPOT'], ['NGREKAS'], ['SYMFONY'], [new StringableValue('SYMFONY')]]; } /** diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionSyntaxValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionSyntaxValidatorTest.php index de316f47e283..d7e62cbd27ab 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionSyntaxValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionSyntaxValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\ExpressionSyntax; use Symfony\Component\Validator\Constraints\ExpressionSyntaxValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class ExpressionSyntaxValidatorTest extends ConstraintValidatorTestCase { @@ -47,6 +48,16 @@ public function testExpressionValid() $this->assertNoViolation(); } + public function testStringableExpressionValid() + { + $this->validator->validate(new StringableValue('1 + 1'), new ExpressionSyntax([ + 'message' => 'myMessage', + 'allowedVariables' => [], + ])); + + $this->assertNoViolation(); + } + public function testExpressionWithoutNames() { $this->validator->validate('1 + 1', new ExpressionSyntax([ @@ -79,4 +90,18 @@ public function testExpressionIsNotValid() ->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR) ->assertRaised(); } + + public function testStringableExpressionIsNotValid() + { + $this->validator->validate(new StringableValue('a + 1'), new ExpressionSyntax([ + 'message' => 'myMessage', + 'allowedVariables' => [], + ])); + + $this->buildViolation('myMessage') + ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."') + ->setInvalidValue('a + 1') + ->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR) + ->assertRaised(); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/StringableValue.php b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/StringableValue.php new file mode 100644 index 000000000000..0cd2f4ddb63a --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/StringableValue.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; + +class StringableValue implements \Stringable +{ + public function __construct(private string $value) + { + } + + public function __toString(): string + { + return $this->value; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/PasswordStrengthValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/PasswordStrengthValidatorTest.php index 21dabcad738a..e279843f30a0 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/PasswordStrengthValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/PasswordStrengthValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraints\PasswordStrength; use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue; class PasswordStrengthValidatorTest extends ConstraintValidatorTestCase { @@ -25,7 +26,7 @@ protected function createValidator(): PasswordStrengthValidator /** * @dataProvider getValidValues */ - public function testValidValues(string $value, int $expectedStrength) + public function testValidValues(string|\Stringable $value, int $expectedStrength) { $this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength)); @@ -48,6 +49,7 @@ public static function getValidValues(): iterable yield ['Reasonable-pwd', PasswordStrength::STRENGTH_MEDIUM]; yield ['This 1s a very g00d Pa55word! ;-)', PasswordStrength::STRENGTH_VERY_STRONG]; yield ['pudding-smack-👌🏼-fox-😎', PasswordStrength::STRENGTH_VERY_STRONG]; + yield [new StringableValue('How-is-this'), PasswordStrength::STRENGTH_WEAK]; } /**