Skip to content

Commit 2cbde2e

Browse files
Andrej-in-uafabpot
authored andcommitted
[ExpressionLanguage] Added expression language syntax validator
1 parent e28c06a commit 2cbde2e

File tree

5 files changed

+189
-2
lines changed

5 files changed

+189
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* allow to define a reusable set of constraints by extending the `Compound` constraint
1010
* added `Sequentially` constraint, to sequentially validate a set of constraints (any violation raised will prevent further validation of the nested constraints)
1111
* added the `divisibleBy` option to the `Count` constraint
12+
* added the `ExpressionLanguageSyntax` constraint
1213

1314
5.0.0
1415
-----
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
class ExpressionLanguageSyntax extends Constraint
23+
{
24+
const EXPRESSION_LANGUAGE_SYNTAX_ERROR = '1766a3f3-ff03-40eb-b053-ab7aa23d988a';
25+
26+
protected static $errorNames = [
27+
self::EXPRESSION_LANGUAGE_SYNTAX_ERROR => 'EXPRESSION_LANGUAGE_SYNTAX_ERROR',
28+
];
29+
30+
public $message = 'This value should be a valid expression.';
31+
public $service;
32+
public $validateNames = true;
33+
public $names = [];
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function validatedBy()
39+
{
40+
return $this->service;
41+
}
42+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
20+
/**
21+
* @author Andrey Sevastianov <[email protected]>
22+
*/
23+
class ExpressionLanguageSyntaxValidator extends ConstraintValidator
24+
{
25+
private $expressionLanguage;
26+
27+
public function __construct(ExpressionLanguage $expressionLanguage)
28+
{
29+
$this->expressionLanguage = $expressionLanguage;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function validate($expression, Constraint $constraint): void
36+
{
37+
if (!$constraint instanceof ExpressionLanguageSyntax) {
38+
throw new UnexpectedTypeException($constraint, ExpressionLanguageSyntax::class);
39+
}
40+
41+
if (!\is_string($expression)) {
42+
throw new UnexpectedTypeException($expression, 'string');
43+
}
44+
45+
try {
46+
$this->expressionLanguage->lint($expression, ($constraint->validateNames ? ($constraint->names ?? []) : null));
47+
} catch (SyntaxError $exception) {
48+
$this->context->buildViolation($constraint->message)
49+
->setParameter('{{ syntax_error }}', $this->formatValue($exception->getMessage()))
50+
->setInvalidValue((string) $expression)
51+
->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR)
52+
->addViolation();
53+
}
54+
}
55+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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\MockObject\MockObject;
15+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
16+
use Symfony\Component\ExpressionLanguage\SyntaxError;
17+
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntax;
18+
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator;
19+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
20+
21+
class ExpressionLanguageSyntaxTest extends ConstraintValidatorTestCase
22+
{
23+
/**
24+
* @var \PHPUnit\Framework\MockObject\MockObject|ExpressionLanguage
25+
*/
26+
protected $expressionLanguage;
27+
28+
protected function createValidator()
29+
{
30+
return new ExpressionLanguageSyntaxValidator($this->expressionLanguage);
31+
}
32+
33+
protected function setUp(): void
34+
{
35+
$this->expressionLanguage = $this->createExpressionLanguage();
36+
37+
parent::setUp();
38+
}
39+
40+
public function testExpressionValid(): void
41+
{
42+
$this->expressionLanguage->expects($this->once())
43+
->method('lint')
44+
->with($this->value, []);
45+
46+
$this->validator->validate($this->value, new ExpressionLanguageSyntax([
47+
'message' => 'myMessage',
48+
]));
49+
50+
$this->assertNoViolation();
51+
}
52+
53+
public function testExpressionWithoutNames(): void
54+
{
55+
$this->expressionLanguage->expects($this->once())
56+
->method('lint')
57+
->with($this->value, null);
58+
59+
$this->validator->validate($this->value, new ExpressionLanguageSyntax([
60+
'message' => 'myMessage',
61+
'validateNames' => false,
62+
]));
63+
64+
$this->assertNoViolation();
65+
}
66+
67+
public function testExpressionIsNotValid(): void
68+
{
69+
$this->expressionLanguage->expects($this->once())
70+
->method('lint')
71+
->with($this->value, [])
72+
->willThrowException(new SyntaxError('Test exception', 42));
73+
74+
$this->validator->validate($this->value, new ExpressionLanguageSyntax([
75+
'message' => 'myMessage',
76+
]));
77+
78+
$this->buildViolation('myMessage')
79+
->setParameter('{{ syntax_error }}', '"Test exception around position 42."')
80+
->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR)
81+
->assertRaised();
82+
}
83+
84+
protected function createExpressionLanguage(): MockObject
85+
{
86+
return $this->getMockBuilder('\Symfony\Component\ExpressionLanguage\ExpressionLanguage')->getMock();
87+
}
88+
}

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"symfony/yaml": "^4.4|^5.0",
3131
"symfony/config": "^4.4|^5.0",
3232
"symfony/dependency-injection": "^4.4|^5.0",
33-
"symfony/expression-language": "^4.4|^5.0",
33+
"symfony/expression-language": "^5.1",
3434
"symfony/cache": "^4.4|^5.0",
3535
"symfony/mime": "^4.4|^5.0",
3636
"symfony/property-access": "^4.4|^5.0",
@@ -44,6 +44,7 @@
4444
"doctrine/lexer": "<1.0.2",
4545
"phpunit/phpunit": "<5.4.3",
4646
"symfony/dependency-injection": "<4.4",
47+
"symfony/expression-language": "<5.1",
4748
"symfony/http-kernel": "<4.4",
4849
"symfony/intl": "<4.4",
4950
"symfony/translation": "<4.4",
@@ -61,7 +62,7 @@
6162
"egulias/email-validator": "Strict (RFC compliant) email validation",
6263
"symfony/property-access": "For accessing properties within comparison constraints",
6364
"symfony/property-info": "To automatically add NotNull and Type constraints",
64-
"symfony/expression-language": "For using the Expression validator"
65+
"symfony/expression-language": "For using the Expression validator and the ExpressionLanguageSyntax constraints"
6566
},
6667
"autoload": {
6768
"psr-4": { "Symfony\\Component\\Validator\\": "" },

0 commit comments

Comments
 (0)