Skip to content

Commit 632219a

Browse files
mpiotnicolas-grekas
authored andcommitted
[Validator] Deprecate constraint "ExpressionLanguageSyntax", use "ExpressionSyntax" instead
1 parent e70c416 commit 632219a

9 files changed

+291
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Deprecate `Constraint::$errorNames`, use `Constraint::ERROR_NAMES` instead
8+
* Deprecate constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead
89

910
6.0
1011
---

Constraints/ExpressionLanguageSyntax.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313

1414
use Symfony\Component\Validator\Constraint;
1515

16+
trigger_deprecation('symfony/validator', '6.1', 'The "%s" constraint is deprecated since symfony 6.1, use "ExpressionSyntax" instead.', ExpressionLanguageSyntax::class);
17+
1618
/**
1719
* @Annotation
1820
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
1921
*
2022
* @author Andrey Sevastianov <[email protected]>
23+
*
24+
* @deprecated since symfony 6.1, use ExpressionSyntax instead
2125
*/
2226
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2327
class ExpressionLanguageSyntax extends Constraint

Constraints/ExpressionLanguageSyntaxValidator.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
1919
use Symfony\Component\Validator\Exception\UnexpectedValueException;
2020

21+
trigger_deprecation('symfony/validator', '6.1', 'The "%s" constraint is deprecated since symfony 6.1, use "ExpressionSyntaxValidator" instead.', ExpressionLanguageSyntaxValidator::class);
22+
2123
/**
2224
* @author Andrey Sevastianov <[email protected]>
25+
*
26+
* @deprecated since symfony 6.1, use ExpressionSyntaxValidator instead
2327
*/
2428
class ExpressionLanguageSyntaxValidator extends ConstraintValidator
2529
{

Constraints/ExpressionSyntax.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
16+
/**
17+
* @Annotation
18+
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
19+
*
20+
* @author Andrey Sevastianov <[email protected]>
21+
*/
22+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
23+
class ExpressionSyntax extends Constraint
24+
{
25+
public const EXPRESSION_SYNTAX_ERROR = 'e219aa22-8b11-48ec-81a0-fc07cdb0e13f';
26+
27+
protected const ERROR_NAMES = [
28+
self::EXPRESSION_SYNTAX_ERROR => 'EXPRESSION_SYNTAX_ERROR',
29+
];
30+
31+
public $message = 'This value should be a valid expression.';
32+
public $service;
33+
public $allowedVariables;
34+
35+
public function __construct(array $options = null, string $message = null, string $service = null, array $allowedVariables = null, array $groups = null, mixed $payload = null)
36+
{
37+
parent::__construct($options, $groups, $payload);
38+
39+
$this->message = $message ?? $this->message;
40+
$this->service = $service ?? $this->service;
41+
$this->allowedVariables = $allowedVariables ?? $this->allowedVariables;
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function validatedBy(): string
48+
{
49+
return $this->service ?? static::class.'Validator';
50+
}
51+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Constraints;
13+
14+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
15+
use Symfony\Component\ExpressionLanguage\SyntaxError;
16+
use Symfony\Component\Validator\Constraint;
17+
use Symfony\Component\Validator\ConstraintValidator;
18+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
19+
use Symfony\Component\Validator\Exception\UnexpectedValueException;
20+
21+
/**
22+
* @author Andrey Sevastianov <[email protected]>
23+
*/
24+
class ExpressionSyntaxValidator extends ConstraintValidator
25+
{
26+
private ?ExpressionLanguage $expressionLanguage;
27+
28+
public function __construct(ExpressionLanguage $expressionLanguage = null)
29+
{
30+
$this->expressionLanguage = $expressionLanguage;
31+
}
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public function validate(mixed $expression, Constraint $constraint): void
37+
{
38+
if (!$constraint instanceof ExpressionSyntax) {
39+
throw new UnexpectedTypeException($constraint, ExpressionSyntax::class);
40+
}
41+
42+
if (null === $expression || '' === $expression) {
43+
return;
44+
}
45+
46+
if (!\is_string($expression)) {
47+
throw new UnexpectedValueException($expression, 'string');
48+
}
49+
50+
if (null === $this->expressionLanguage) {
51+
$this->expressionLanguage = new ExpressionLanguage();
52+
}
53+
54+
try {
55+
$this->expressionLanguage->lint($expression, $constraint->allowedVariables);
56+
} catch (SyntaxError $exception) {
57+
$this->context->buildViolation($constraint->message)
58+
->setParameter('{{ syntax_error }}', $this->formatValue($exception->getMessage()))
59+
->setInvalidValue((string) $expression)
60+
->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR)
61+
->addViolation();
62+
}
63+
}
64+
}

Tests/Constraints/ExpressionLanguageSyntaxTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
use Symfony\Component\Validator\Mapping\ClassMetadata;
1818
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
1919

20+
/**
21+
* @group legacy
22+
*/
2023
class ExpressionLanguageSyntaxTest extends TestCase
2124
{
2225
public function testValidatedByStandardValidator()

Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator;
1717
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
1818

19+
/**
20+
* @group legacy
21+
*/
1922
class ExpressionLanguageSyntaxValidatorTest extends ConstraintValidatorTestCase
2023
{
2124
protected function createValidator()
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests\Constraints;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Validator\Constraints\ExpressionSyntax;
16+
use Symfony\Component\Validator\Constraints\ExpressionSyntaxValidator;
17+
use Symfony\Component\Validator\Mapping\ClassMetadata;
18+
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
19+
20+
class ExpressionSyntaxTest extends TestCase
21+
{
22+
public function testValidatedByStandardValidator()
23+
{
24+
$constraint = new ExpressionSyntax();
25+
26+
self::assertSame(ExpressionSyntaxValidator::class, $constraint->validatedBy());
27+
}
28+
29+
/**
30+
* @dataProvider provideServiceValidatedConstraints
31+
*/
32+
public function testValidatedByService(ExpressionSyntax $constraint)
33+
{
34+
self::assertSame('my_service', $constraint->validatedBy());
35+
}
36+
37+
public function provideServiceValidatedConstraints(): iterable
38+
{
39+
yield 'Doctrine style' => [new ExpressionSyntax(['service' => 'my_service'])];
40+
41+
yield 'named arguments' => [new ExpressionSyntax(service: 'my_service')];
42+
43+
$metadata = new ClassMetadata(ExpressionSyntaxDummy::class);
44+
self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata));
45+
46+
yield 'attribute' => [$metadata->properties['b']->constraints[0]];
47+
}
48+
49+
public function testAttributes()
50+
{
51+
$metadata = new ClassMetadata(ExpressionSyntaxDummy::class);
52+
self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata));
53+
54+
[$aConstraint] = $metadata->properties['a']->getConstraints();
55+
self::assertNull($aConstraint->service);
56+
self::assertNull($aConstraint->allowedVariables);
57+
58+
[$bConstraint] = $metadata->properties['b']->getConstraints();
59+
self::assertSame('my_service', $bConstraint->service);
60+
self::assertSame('myMessage', $bConstraint->message);
61+
self::assertSame(['Default', 'ExpressionSyntaxDummy'], $bConstraint->groups);
62+
63+
[$cConstraint] = $metadata->properties['c']->getConstraints();
64+
self::assertSame(['foo', 'bar'], $cConstraint->allowedVariables);
65+
self::assertSame(['my_group'], $cConstraint->groups);
66+
}
67+
}
68+
69+
class ExpressionSyntaxDummy
70+
{
71+
#[ExpressionSyntax]
72+
private $a;
73+
74+
#[ExpressionSyntax(service: 'my_service', message: 'myMessage')]
75+
private $b;
76+
77+
#[ExpressionSyntax(allowedVariables: ['foo', 'bar'], groups: ['my_group'])]
78+
private $c;
79+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests\Constraints;
13+
14+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
15+
use Symfony\Component\Validator\Constraints\ExpressionSyntax;
16+
use Symfony\Component\Validator\Constraints\ExpressionSyntaxValidator;
17+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
18+
19+
class ExpressionSyntaxValidatorTest extends ConstraintValidatorTestCase
20+
{
21+
protected function createValidator()
22+
{
23+
return new ExpressionSyntaxValidator(new ExpressionLanguage());
24+
}
25+
26+
public function testNullIsValid()
27+
{
28+
$this->validator->validate(null, new ExpressionSyntax());
29+
30+
$this->assertNoViolation();
31+
}
32+
33+
public function testEmptyStringIsValid()
34+
{
35+
$this->validator->validate('', new ExpressionSyntax());
36+
37+
$this->assertNoViolation();
38+
}
39+
40+
public function testExpressionValid()
41+
{
42+
$this->validator->validate('1 + 1', new ExpressionSyntax([
43+
'message' => 'myMessage',
44+
'allowedVariables' => [],
45+
]));
46+
47+
$this->assertNoViolation();
48+
}
49+
50+
public function testExpressionWithoutNames()
51+
{
52+
$this->validator->validate('1 + 1', new ExpressionSyntax([
53+
'message' => 'myMessage',
54+
]));
55+
56+
$this->assertNoViolation();
57+
}
58+
59+
public function testExpressionWithAllowedVariableName()
60+
{
61+
$this->validator->validate('a + 1', new ExpressionSyntax([
62+
'message' => 'myMessage',
63+
'allowedVariables' => ['a'],
64+
]));
65+
66+
$this->assertNoViolation();
67+
}
68+
69+
public function testExpressionIsNotValid()
70+
{
71+
$this->validator->validate('a + 1', new ExpressionSyntax([
72+
'message' => 'myMessage',
73+
'allowedVariables' => [],
74+
]));
75+
76+
$this->buildViolation('myMessage')
77+
->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."')
78+
->setInvalidValue('a + 1')
79+
->setCode(ExpressionSyntax::EXPRESSION_SYNTAX_ERROR)
80+
->assertRaised();
81+
}
82+
}

0 commit comments

Comments
 (0)