diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntax.php b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntax.php index d5c1f6f9fc24d..8f8eb92254782 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntax.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntax.php @@ -31,14 +31,20 @@ class ExpressionLanguageSyntax extends Constraint public $message = 'This value should be a valid expression.'; public $service; public $allowedVariables; + public $allowNullAndEmptyString = false; - public function __construct(array $options = null, string $message = null, string $service = null, array $allowedVariables = null, array $groups = null, $payload = null) + public function __construct(array $options = null, string $message = null, string $service = null, array $allowedVariables = null, bool $allowNullAndEmptyString = null, array $groups = null, $payload = null) { parent::__construct($options, $groups, $payload); $this->message = $message ?? $this->message; $this->service = $service ?? $this->service; $this->allowedVariables = $allowedVariables ?? $this->allowedVariables; + $this->allowNullAndEmptyString = $allowNullAndEmptyString ?? $this->allowNullAndEmptyString; + + if (!$this->allowNullAndEmptyString) { + trigger_deprecation('symfony/validator', '5.4', 'Validating empty expressions with "%s" constraint is deprecated. Set "allowNullAndEmptyString" option to "true" instead and add explicit constraint like NotNull or NotBlank.', __CLASS__); + } } /** diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntaxValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntaxValidator.php index 4b67da2c2be9c..74bdd42f93d4b 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntaxValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageSyntaxValidator.php @@ -39,6 +39,10 @@ public function validate($expression, Constraint $constraint): void throw new UnexpectedTypeException($constraint, ExpressionLanguageSyntax::class); } + if (true === $constraint->allowNullAndEmptyString && (null === $expression || '' === $expression)) { + return; + } + if (!\is_string($expression)) { throw new UnexpectedValueException($expression, 'string'); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxTest.php index 1c57d56f0e6a6..45767470d21c3 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxTest.php @@ -61,6 +61,7 @@ public function testAttributes() [$aConstraint] = $metadata->properties['a']->getConstraints(); self::assertNull($aConstraint->service); self::assertNull($aConstraint->allowedVariables); + self::assertFalse($aConstraint->allowNullAndEmptyString); [$bConstraint] = $metadata->properties['b']->getConstraints(); self::assertSame('my_service', $bConstraint->service); @@ -70,6 +71,9 @@ public function testAttributes() [$cConstraint] = $metadata->properties['c']->getConstraints(); self::assertSame(['foo', 'bar'], $cConstraint->allowedVariables); self::assertSame(['my_group'], $cConstraint->groups); + + [$dConstraint] = $metadata->properties['d']->getConstraints(); + self::assertTrue($dConstraint->allowNullAndEmptyString); } } @@ -83,4 +87,7 @@ class ExpressionLanguageSyntaxDummy #[ExpressionLanguageSyntax(allowedVariables: ['foo', 'bar'], groups: ['my_group'])] private $c; + + #[ExpressionLanguageSyntax(allowNullAndEmptyString: true)] + private $d; } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php index 8cca54ed8a730..26dfdd4cba3ec 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php @@ -65,4 +65,46 @@ public function testExpressionIsNotValid() ->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR) ->assertRaised(); } + + public function testNullIsValid() + { + $this->validator->validate(null, new ExpressionLanguageSyntax([ + 'allowNullAndEmptyString' => true, + ])); + + $this->assertNoViolation(); + } + + /** + * @group legacy + */ + public function testNullWithoutAllowOptionIsNotValid() + { + $this->expectExceptionMessage('Expected argument of type "string", "null" given'); + + $this->validator->validate(null, new ExpressionLanguageSyntax()); + } + + public function testEmptyStringIsValid() + { + $this->validator->validate('', new ExpressionLanguageSyntax([ + 'allowNullAndEmptyString' => true, + ])); + + $this->assertNoViolation(); + } + + /** + * @group legacy + */ + public function testEmptyStringWithoutAllowOptionIsNotValid() + { + $this->validator->validate('', new ExpressionLanguageSyntax()); + + $this->buildViolation('This value should be a valid expression.') + ->setParameter('{{ syntax_error }}', '"Unexpected token "end of expression" of value "" around position 1."') + ->setInvalidValue('') + ->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR) + ->assertRaised(); + } }