Skip to content

Commit 7eebfcc

Browse files
committed
Merge branch '6.4' into 7.0
* 6.4: [Scheduler] Fix changelog [FrameworkBundle][Test]: add token attributes in `KernelBrowser::loginUser()` Fix stateful scheduler [Scheduler] Speed up tests [HttpClient] Enable using EventSourceHttpClient::connect() for both GET and POST [Serializer] Allow Context to target classes [Validator] Add is_valid function to Expression constraint Fix Form profiler toggles [FrameworkBundle] Fix missing PhraseProviderFactory import [Security] Fixing deprecation message
2 parents cbc6d13 + 50b0062 commit 7eebfcc

File tree

3 files changed

+110
-4
lines changed

3 files changed

+110
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ CHANGELOG
2222
6.4
2323
---
2424

25+
* Add `is_valid` function to the `Expression` constraint, its behavior is the same as `ValidatorInterface::validate`
2526
* Allow single integer for the `versions` option of the `Uuid` constraint
2627
* Allow single constraint to be passed to the `constraints` option of the `When` constraint
2728
* Deprecate Doctrine annotations support in favor of native attributes

Constraints/ExpressionValidator.php

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
15+
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
1416
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
1517
use Symfony\Component\Validator\Constraint;
1618
use Symfony\Component\Validator\ConstraintValidator;
@@ -20,13 +22,16 @@
2022
* @author Fabien Potencier <[email protected]>
2123
* @author Bernhard Schussek <[email protected]>
2224
*/
23-
class ExpressionValidator extends ConstraintValidator
25+
class ExpressionValidator extends ConstraintValidator implements ExpressionFunctionProviderInterface
2426
{
25-
private ?ExpressionLanguage $expressionLanguage;
27+
private ExpressionLanguage $expressionLanguage;
2628

2729
public function __construct(ExpressionLanguage $expressionLanguage = null)
2830
{
29-
$this->expressionLanguage = $expressionLanguage;
31+
if ($expressionLanguage) {
32+
$this->expressionLanguage = clone $expressionLanguage;
33+
$this->expressionLanguage->registerProvider($this);
34+
}
3035
}
3136

3237
public function validate(mixed $value, Constraint $constraint): void
@@ -38,6 +43,7 @@ public function validate(mixed $value, Constraint $constraint): void
3843
$variables = $constraint->values;
3944
$variables['value'] = $value;
4045
$variables['this'] = $this->context->getObject();
46+
$variables['context'] = $this->context;
4147

4248
if ($constraint->negate xor $this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) {
4349
$this->context->buildViolation($constraint->message)
@@ -47,8 +53,27 @@ public function validate(mixed $value, Constraint $constraint): void
4753
}
4854
}
4955

56+
public function getFunctions(): array
57+
{
58+
return [
59+
new ExpressionFunction('is_valid', function (...$arguments) {
60+
return sprintf(
61+
'0 === $context->getValidator()->inContext($context)->validate(%s)->getViolations()->count()',
62+
implode(', ', $arguments)
63+
);
64+
}, function (array $variables, ...$arguments): bool {
65+
return 0 === $variables['context']->getValidator()->inContext($variables['context'])->validate(...$arguments)->getViolations()->count();
66+
}),
67+
];
68+
}
69+
5070
private function getExpressionLanguage(): ExpressionLanguage
5171
{
52-
return $this->expressionLanguage ??= new ExpressionLanguage();
72+
if (!isset($this->expressionLanguage)) {
73+
$this->expressionLanguage = new ExpressionLanguage();
74+
$this->expressionLanguage->registerProvider($this);
75+
}
76+
77+
return $this->expressionLanguage;
5378
}
5479
}

Tests/Constraints/ExpressionValidatorTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
1515
use Symfony\Component\Validator\Constraints\Expression;
1616
use Symfony\Component\Validator\Constraints\ExpressionValidator;
17+
use Symfony\Component\Validator\Constraints\NotNull;
18+
use Symfony\Component\Validator\Constraints\Range;
19+
use Symfony\Component\Validator\ConstraintViolation;
1720
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
1821
use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity;
1922
use Symfony\Component\Validator\Tests\Fixtures\ToString;
@@ -304,4 +307,81 @@ public function testViolationOnPass()
304307
->setCode(Expression::EXPRESSION_FAILED_ERROR)
305308
->assertRaised();
306309
}
310+
311+
public function testIsValidExpression()
312+
{
313+
$constraints = [new NotNull(), new Range(['min' => 2])];
314+
315+
$constraint = new Expression(
316+
['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]]
317+
);
318+
319+
$object = new Entity();
320+
$object->data = 7;
321+
322+
$this->setObject($object);
323+
324+
$this->expectValidateValue(0, $object->data, $constraints);
325+
326+
$this->validator->validate($object, $constraint);
327+
328+
$this->assertNoViolation();
329+
}
330+
331+
public function testIsValidExpressionInvalid()
332+
{
333+
$constraints = [new Range(['min' => 2, 'max' => 5])];
334+
335+
$constraint = new Expression(
336+
['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]]
337+
);
338+
339+
$object = new Entity();
340+
$object->data = 7;
341+
342+
$this->setObject($object);
343+
344+
$this->expectFailingValueValidation(
345+
0,
346+
7,
347+
$constraints,
348+
null,
349+
new ConstraintViolation('error_range', '', [], '', '', 7, null, 'range')
350+
);
351+
352+
$this->validator->validate($object, $constraint);
353+
354+
$this->assertCount(2, $this->context->getViolations());
355+
}
356+
357+
/**
358+
* @dataProvider provideCompileIsValid
359+
*/
360+
public function testCompileIsValid(string $expression, array $names, string $expected)
361+
{
362+
$provider = new ExpressionValidator();
363+
364+
$expressionLanguage = new ExpressionLanguage();
365+
$expressionLanguage->registerProvider($provider);
366+
367+
$result = $expressionLanguage->compile($expression, $names);
368+
369+
$this->assertSame($expected, $result);
370+
}
371+
372+
public static function provideCompileIsValid(): array
373+
{
374+
return [
375+
[
376+
'is_valid("foo", constraints)',
377+
['constraints'],
378+
'0 === $context->getValidator()->inContext($context)->validate("foo", $constraints)->getViolations()->count()',
379+
],
380+
[
381+
'is_valid(this.data, constraints, groups)',
382+
['this', 'constraints', 'groups'],
383+
'0 === $context->getValidator()->inContext($context)->validate($this->data, $constraints, $groups)->getViolations()->count()',
384+
],
385+
];
386+
}
307387
}

0 commit comments

Comments
 (0)