Skip to content

Commit

Permalink
feature #53374 [Validator] support \Stringable instances in all con…
Browse files Browse the repository at this point in the history
…straints (xabbuh)

This PR was merged into the 7.1 branch.

Discussion
----------

[Validator] support `\Stringable` instances in all constraints

| Q             | A
| ------------- | ---
| Branch?       | 7.1
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Issues        |
| License       | MIT

Commits
-------

05f9f45 support Stringable values in all constraints
  • Loading branch information
nicolas-grekas committed Jan 4, 2024
2 parents 894ef83 + 05f9f45 commit 4266345
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Expand Up @@ -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
Expand Down
Expand Up @@ -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');
}

Expand Down
Expand Up @@ -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');
}

Expand All @@ -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();
}
Expand Down
Expand Up @@ -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');
}

Expand Down
Expand Up @@ -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)
Expand Down
Expand Up @@ -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
{
Expand Down Expand Up @@ -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()
Expand Down
Expand Up @@ -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
{
Expand Down Expand Up @@ -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]));

Expand All @@ -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
Expand Down Expand Up @@ -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],
];
}

Expand Down Expand Up @@ -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')],
];
}

Expand Down
Expand Up @@ -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
{
Expand Down Expand Up @@ -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)')],
];
}

Expand Down Expand Up @@ -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')]];
}

/**
Expand Down
Expand Up @@ -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
{
Expand Down Expand Up @@ -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([
Expand Down Expand Up @@ -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();
}
}
@@ -0,0 +1,24 @@
<?php

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

namespace Symfony\Component\Validator\Tests\Constraints\Fixtures;

class StringableValue implements \Stringable
{
public function __construct(private string $value)
{
}

public function __toString(): string
{
return $this->value;
}
}
Expand Up @@ -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
{
Expand All @@ -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));

Expand All @@ -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];
}

/**
Expand Down

0 comments on commit 4266345

Please sign in to comment.