From 536e2f56b668d19a226b5c80f3b9e150fcdaae28 Mon Sep 17 00:00:00 2001 From: Ihor Khokhlov Date: Tue, 2 Aug 2022 15:35:16 +0300 Subject: [PATCH 1/6] Add is_valid function to Expression constraint --- .../Component/ExpressionLanguage/CHANGELOG.md | 5 + .../ExpressionLanguage/ExpressionLanguage.php | 10 ++ .../Tests/ExpressionLanguageTest.php | 24 +++++ src/Symfony/Component/Validator/CHANGELOG.md | 1 + .../ExpressionLanguageProvider.php | 49 +++++++++ .../Constraints/ExpressionValidator.php | 4 + .../ExpressionLanguageProviderTest.php | 99 +++++++++++++++++++ .../Constraints/ExpressionValidatorTest.php | 39 ++++++++ 8 files changed, 231 insertions(+) create mode 100644 src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php create mode 100644 src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php diff --git a/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md b/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md index b39c4cb1cda47..8bfa42c66d088 100644 --- a/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md +++ b/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.2 +--- + +* Add `ExpressionLanguage::hasFunction()` and `ExpressionLanguage::hasFunctionByName()` methods. + 6.1 --- diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index e076eb9bc56e0..5f4d20ac6f0f7 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -128,6 +128,16 @@ public function addFunction(ExpressionFunction $function) $this->register($function->getName(), $function->getCompiler(), $function->getEvaluator()); } + public function hasFunction(ExpressionFunction $function): bool + { + return $this->hasFunctionByName($function->getName()); + } + + public function hasFunctionByName(string $functionName): bool + { + return isset($this->functions[$functionName]); + } + public function registerProvider(ExpressionFunctionProviderInterface $provider) { foreach ($provider->getFunctions() as $function) { diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 2efa7a3be4722..251130a9a0f4f 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -384,4 +384,28 @@ function (ExpressionLanguage $el) { ], ]; } + + public function testHasFunction(): void + { + $expressionLanguage = new ExpressionLanguage(); + + $function = new ExpressionFunction('foo', function () {}, function () {}); + + $this->assertFalse($expressionLanguage->hasFunction($function)); + + $expressionLanguage->addFunction($function); + + $this->assertTrue($expressionLanguage->hasFunction($function)); + } + + public function testHasFunctionByName(): void + { + $expressionLanguage = new ExpressionLanguage(); + + $this->assertFalse($expressionLanguage->hasFunctionByName('foo')); + + $expressionLanguage->register('foo', function () {}, function () {}); + + $this->assertTrue($expressionLanguage->hasFunctionByName('foo')); + } } diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 5d74eba99afb0..c19dc3d921f5e 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Deprecate the "loose" e-mail validation mode, use "html5" instead * Add the `negate` option to the `Expression` constraint, to inverse the logic of the violation's creation + * Add `is_valid` function to the `Expression` constraint, api the same as `ValidatorInterface::validate` 6.1 --- diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php new file mode 100644 index 0000000000000..14186b63a274f --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use LogicException; +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; +use Symfony\Component\Validator\Context\ExecutionContextInterface; + +/** + * Define some ExpressionLanguage functions. + * + * @author Ihor Khokhlov + */ +class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface +{ + private ExecutionContextInterface $context; + + public function __construct(ExecutionContextInterface $context) + { + $this->context = $context; + } + + public function getFunctions(): array + { + return [ + new ExpressionFunction('is_valid', function () { + throw new LogicException('The "is_valid" function cannot be compiled.'); + }, function (array $variables, ...$arguments): bool { + $context = $this->context; + + $validator = $context->getValidator()->inContext($context); + + $violations = $validator->validate(...$arguments)->getViolations(); + + return 0 === $violations->count(); + }), + ]; + } +} diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index 0e8ecac6b0093..2eabecda91797 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -56,6 +56,10 @@ private function getExpressionLanguage(): ExpressionLanguage $this->expressionLanguage = new ExpressionLanguage(); } + if (false === $this->expressionLanguage->hasFunctionByName('is_valid')) { + $this->expressionLanguage->registerProvider(new ExpressionLanguageProvider($this->context)); + } + return $this->expressionLanguage; } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php new file mode 100644 index 0000000000000..45e34678b62b1 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Constraints; + +use LogicException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\ConstraintViolationListInterface; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Validator\ContextualValidatorInterface; +use Symfony\Component\Validator\Validator\ValidatorInterface; + +class ExpressionLanguageProviderTest extends TestCase +{ + public function testCompile(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The "is_valid" function cannot be compiled.'); + + $context = $this->createMock(ExecutionContextInterface::class); + + $provider = new ExpressionLanguageProvider($context); + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->registerProvider($provider); + + $expressionLanguage->compile('is_valid()'); + } + + /** + * @dataProvider dataProviderEvaluate + */ + public function testEvaluate(bool $expected, int $errorsCount): void + { + $constraints = [new NotNull(), new Range(['min' => 2])]; + + $violationList = $this->getMockBuilder(ConstraintViolationListInterface::class) + ->onlyMethods(['count']) + ->getMockForAbstractClass(); + $violationList->expects($this->once()) + ->method('count') + ->willReturn($errorsCount); + + $contextualValidator = $this->getMockBuilder(ContextualValidatorInterface::class) + ->onlyMethods(['getViolations', 'validate']) + ->getMockForAbstractClass(); + $contextualValidator->expects($this->once()) + ->method('validate') + ->with('foo', $constraints) + ->willReturnSelf(); + $contextualValidator->expects($this->once()) + ->method('getViolations') + ->willReturn($violationList); + + $validator = $this->getMockBuilder(ValidatorInterface::class) + ->onlyMethods(['inContext']) + ->getMockForAbstractClass(); + + $context = $this->getMockBuilder(ExecutionContextInterface::class) + ->onlyMethods(['getValidator']) + ->getMockForAbstractClass(); + $context->expects($this->once()) + ->method('getValidator') + ->willReturn($validator); + + $validator->expects($this->once()) + ->method('inContext') + ->with($context) + ->willReturn($contextualValidator); + + $provider = new ExpressionLanguageProvider($context); + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->registerProvider($provider); + + $this->assertSame($expected, $expressionLanguage->evaluate('is_valid("foo", a)', ['a' => $constraints])); + } + + public function dataProviderEvaluate(): array + { + return [ + [true, 0], + [false, 1], + [false, 12], + ]; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 9447648f35a9e..45472dae966b7 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -14,6 +14,8 @@ use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\ExpressionValidator; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; use Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity; use Symfony\Component\Validator\Tests\Fixtures\ToString; @@ -304,4 +306,41 @@ public function testViolationOnPass() ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } + + public function testIsValidExpression(): void + { + $constraints = [new NotNull(), new Range(['min' => 2])]; + + $constraint = new Expression( + ['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]] + ); + + $object = new Entity(); + $object->data = '7'; + + $this->setObject($object); + + $this->expectValidateValue(0, $object->data, $constraints); + + $this->validator->validate($object, $constraint); + + $this->assertNoViolation(); + } + + public function testExistingIsValidFunctionIsNotOverridden(): void + { + $used = false; + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->register('is_valid', function () {}, function () use (&$used) { + $used = true; + }); + + $validator = new ExpressionValidator($expressionLanguage); + $validator->initialize($this->context); + + $validator->validate('foo', new Expression('is_valid()')); + + $this->assertTrue($used); + } } From 4f48c40a1fdb966bc1633bde4aaaf3b7a9bb9d54 Mon Sep 17 00:00:00 2001 From: Ihor Khokhlov Date: Thu, 4 Aug 2022 14:50:58 +0300 Subject: [PATCH 2/6] rework compile, evaluate --- .../ExpressionLanguageProvider.php | 24 +++-------- .../Constraints/ExpressionValidator.php | 3 +- .../ExpressionLanguageProviderTest.php | 40 ++++++++++++++----- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php index 14186b63a274f..ecf433a6e3032 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php @@ -11,10 +11,8 @@ namespace Symfony\Component\Validator\Constraints; -use LogicException; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; -use Symfony\Component\Validator\Context\ExecutionContextInterface; /** * Define some ExpressionLanguage functions. @@ -23,26 +21,16 @@ */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface { - private ExecutionContextInterface $context; - - public function __construct(ExecutionContextInterface $context) - { - $this->context = $context; - } - public function getFunctions(): array { return [ - new ExpressionFunction('is_valid', function () { - throw new LogicException('The "is_valid" function cannot be compiled.'); + new ExpressionFunction('is_valid', function (...$arguments) { + return sprintf( + '0 === $context->getValidator()->inContext($context)->validate(%s)->getViolations()->count()', + implode(', ', $arguments) + ); }, function (array $variables, ...$arguments): bool { - $context = $this->context; - - $validator = $context->getValidator()->inContext($context); - - $violations = $validator->validate(...$arguments)->getViolations(); - - return 0 === $violations->count(); + return 0 === $variables['context']->getValidator()->inContext($variables['context'])->validate(...$arguments)->getViolations()->count(); }), ]; } diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index 2eabecda91797..293bcb0413bca 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -41,6 +41,7 @@ public function validate(mixed $value, Constraint $constraint) $variables = $constraint->values; $variables['value'] = $value; $variables['this'] = $this->context->getObject(); + $variables['context'] = $this->context; if ($constraint->negate xor $this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { $this->context->buildViolation($constraint->message) @@ -57,7 +58,7 @@ private function getExpressionLanguage(): ExpressionLanguage } if (false === $this->expressionLanguage->hasFunctionByName('is_valid')) { - $this->expressionLanguage->registerProvider(new ExpressionLanguageProvider($this->context)); + $this->expressionLanguage->registerProvider(new ExpressionLanguageProvider()); } return $this->expressionLanguage; diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php index 45e34678b62b1..2df1f8a26f0c5 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php @@ -11,7 +11,6 @@ namespace Constraints; -use LogicException; use PHPUnit\Framework\TestCase; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; @@ -24,19 +23,35 @@ class ExpressionLanguageProviderTest extends TestCase { - public function testCompile(): void + /** + * @dataProvider dataProviderCompile + */ + public function testCompile(string $expression, array $names, string $expected): void { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "is_valid" function cannot be compiled.'); - - $context = $this->createMock(ExecutionContextInterface::class); - - $provider = new ExpressionLanguageProvider($context); + $provider = new ExpressionLanguageProvider(); $expressionLanguage = new ExpressionLanguage(); $expressionLanguage->registerProvider($provider); - $expressionLanguage->compile('is_valid()'); + $result = $expressionLanguage->compile($expression, $names); + + $this->assertSame($expected, $result); + } + + public function dataProviderCompile(): array + { + return [ + [ + 'is_valid("foo", constraints)', + ['constraints'], + '0 === $context->getValidator()->inContext($context)->validate("foo", $constraints)->getViolations()->count()', + ], + [ + 'is_valid(this.data, constraints, groups)', + ['this', 'constraints', 'groups'], + '0 === $context->getValidator()->inContext($context)->validate($this->data, $constraints, $groups)->getViolations()->count()', + ] + ]; } /** @@ -80,12 +95,15 @@ public function testEvaluate(bool $expected, int $errorsCount): void ->with($context) ->willReturn($contextualValidator); - $provider = new ExpressionLanguageProvider($context); + $provider = new ExpressionLanguageProvider(); $expressionLanguage = new ExpressionLanguage(); $expressionLanguage->registerProvider($provider); - $this->assertSame($expected, $expressionLanguage->evaluate('is_valid("foo", a)', ['a' => $constraints])); + $this->assertSame( + $expected, + $expressionLanguage->evaluate('is_valid("foo", a)', ['a' => $constraints, 'context' => $context]) + ); } public function dataProviderEvaluate(): array From 461b03bf37fc15c0d6adc77e9156a00a1b578f71 Mon Sep 17 00:00:00 2001 From: Ihor Khokhlov Date: Mon, 8 Aug 2022 14:01:14 +0300 Subject: [PATCH 3/6] remove hasFuncton method from ExprLanguage --- .../Component/ExpressionLanguage/CHANGELOG.md | 5 ---- .../ExpressionLanguage/ExpressionLanguage.php | 10 -------- .../Tests/ExpressionLanguageTest.php | 24 ------------------- .../Constraints/ExpressionValidator.php | 5 ++-- .../Constraints/ExpressionValidatorTest.php | 17 ------------- 5 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md b/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md index 8bfa42c66d088..b39c4cb1cda47 100644 --- a/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md +++ b/src/Symfony/Component/ExpressionLanguage/CHANGELOG.md @@ -1,11 +1,6 @@ CHANGELOG ========= -6.2 ---- - -* Add `ExpressionLanguage::hasFunction()` and `ExpressionLanguage::hasFunctionByName()` methods. - 6.1 --- diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index 5f4d20ac6f0f7..e076eb9bc56e0 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -128,16 +128,6 @@ public function addFunction(ExpressionFunction $function) $this->register($function->getName(), $function->getCompiler(), $function->getEvaluator()); } - public function hasFunction(ExpressionFunction $function): bool - { - return $this->hasFunctionByName($function->getName()); - } - - public function hasFunctionByName(string $functionName): bool - { - return isset($this->functions[$functionName]); - } - public function registerProvider(ExpressionFunctionProviderInterface $provider) { foreach ($provider->getFunctions() as $function) { diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 251130a9a0f4f..2efa7a3be4722 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -384,28 +384,4 @@ function (ExpressionLanguage $el) { ], ]; } - - public function testHasFunction(): void - { - $expressionLanguage = new ExpressionLanguage(); - - $function = new ExpressionFunction('foo', function () {}, function () {}); - - $this->assertFalse($expressionLanguage->hasFunction($function)); - - $expressionLanguage->addFunction($function); - - $this->assertTrue($expressionLanguage->hasFunction($function)); - } - - public function testHasFunctionByName(): void - { - $expressionLanguage = new ExpressionLanguage(); - - $this->assertFalse($expressionLanguage->hasFunctionByName('foo')); - - $expressionLanguage->register('foo', function () {}, function () {}); - - $this->assertTrue($expressionLanguage->hasFunctionByName('foo')); - } } diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index 293bcb0413bca..b9aaf2645be55 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -27,6 +27,8 @@ class ExpressionValidator extends ConstraintValidator public function __construct(ExpressionLanguage $expressionLanguage = null) { $this->expressionLanguage = $expressionLanguage; + + $this->expressionLanguage?->registerProvider(new ExpressionLanguageProvider()); } /** @@ -55,9 +57,6 @@ private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { $this->expressionLanguage = new ExpressionLanguage(); - } - - if (false === $this->expressionLanguage->hasFunctionByName('is_valid')) { $this->expressionLanguage->registerProvider(new ExpressionLanguageProvider()); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 45472dae966b7..fc69e4b16a1da 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -326,21 +326,4 @@ public function testIsValidExpression(): void $this->assertNoViolation(); } - - public function testExistingIsValidFunctionIsNotOverridden(): void - { - $used = false; - - $expressionLanguage = new ExpressionLanguage(); - $expressionLanguage->register('is_valid', function () {}, function () use (&$used) { - $used = true; - }); - - $validator = new ExpressionValidator($expressionLanguage); - $validator->initialize($this->context); - - $validator->validate('foo', new Expression('is_valid()')); - - $this->assertTrue($used); - } } From e3d85ec0d0b944c9ed2b1e204e0bbb108ce05dca Mon Sep 17 00:00:00 2001 From: Ihor Khokhlov Date: Tue, 9 Aug 2022 18:00:18 +0300 Subject: [PATCH 4/6] fix class comment, add invalid expectation test --- .../ExpressionLanguageProvider.php | 2 - .../Constraints/ExpressionValidatorTest.php | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php index ecf433a6e3032..9048e299f8fce 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionLanguageProvider.php @@ -15,8 +15,6 @@ use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; /** - * Define some ExpressionLanguage functions. - * * @author Ihor Khokhlov */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index fc69e4b16a1da..e878ddcb75369 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Validator\Constraints\ExpressionValidator; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; use Symfony\Component\Validator\Tests\Fixtures\Annotation\Entity; use Symfony\Component\Validator\Tests\Fixtures\ToString; @@ -316,7 +317,7 @@ public function testIsValidExpression(): void ); $object = new Entity(); - $object->data = '7'; + $object->data = 7; $this->setObject($object); @@ -326,4 +327,38 @@ public function testIsValidExpression(): void $this->assertNoViolation(); } + + public function testIsValidExpressionInvalid(): void + { + $constraints = [new Range(['min' => 2, 'max' => 5])]; + + $constraint = new Expression( + ['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]] + ); + + $object = new Entity(); + $object->data = 7; + + $this->setObject($object); + + $this->expectFailingValueValidation( + 0, + 7, + $constraints, + null, + new ConstraintViolation( + 'error_range', + null, [], + null, + '', + null, + null, + 'range' + ) + ); + + $this->validator->validate($object, $constraint); + + $this->assertCount(2, $this->context->getViolations()); + } } From 91dafc8bad98fcb9e921b5b37c451815ab8e7b2f Mon Sep 17 00:00:00 2001 From: Ihor Khokhlov Date: Thu, 11 Aug 2022 14:04:42 +0300 Subject: [PATCH 5/6] fix remarks --- src/Symfony/Component/Validator/CHANGELOG.md | 2 +- .../Constraints/ExpressionValidator.php | 1 - .../ExpressionLanguageProviderTest.php | 22 ++++++------------- .../Constraints/ExpressionValidatorTest.php | 14 +++--------- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index c19dc3d921f5e..ef4ced3890932 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -6,7 +6,7 @@ CHANGELOG * Deprecate the "loose" e-mail validation mode, use "html5" instead * Add the `negate` option to the `Expression` constraint, to inverse the logic of the violation's creation - * Add `is_valid` function to the `Expression` constraint, api the same as `ValidatorInterface::validate` + * Add `is_valid` function to the `Expression` constraint, its behavior is the same as `ValidatorInterface::validate` 6.1 --- diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index b9aaf2645be55..80a924e9debe6 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -27,7 +27,6 @@ class ExpressionValidator extends ConstraintValidator public function __construct(ExpressionLanguage $expressionLanguage = null) { $this->expressionLanguage = $expressionLanguage; - $this->expressionLanguage?->registerProvider(new ExpressionLanguageProvider()); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php index 2df1f8a26f0c5..7c6243e68ffce 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php @@ -26,7 +26,7 @@ class ExpressionLanguageProviderTest extends TestCase /** * @dataProvider dataProviderCompile */ - public function testCompile(string $expression, array $names, string $expected): void + public function testCompile(string $expression, array $names, string $expected) { $provider = new ExpressionLanguageProvider(); @@ -50,27 +50,23 @@ public function dataProviderCompile(): array 'is_valid(this.data, constraints, groups)', ['this', 'constraints', 'groups'], '0 === $context->getValidator()->inContext($context)->validate($this->data, $constraints, $groups)->getViolations()->count()', - ] + ], ]; } /** * @dataProvider dataProviderEvaluate */ - public function testEvaluate(bool $expected, int $errorsCount): void + public function testEvaluate(bool $expected, int $errorsCount) { $constraints = [new NotNull(), new Range(['min' => 2])]; - $violationList = $this->getMockBuilder(ConstraintViolationListInterface::class) - ->onlyMethods(['count']) - ->getMockForAbstractClass(); + $violationList = $this->createMock(ConstraintViolationListInterface::class); $violationList->expects($this->once()) ->method('count') ->willReturn($errorsCount); - $contextualValidator = $this->getMockBuilder(ContextualValidatorInterface::class) - ->onlyMethods(['getViolations', 'validate']) - ->getMockForAbstractClass(); + $contextualValidator = $this->createMock(ContextualValidatorInterface::class); $contextualValidator->expects($this->once()) ->method('validate') ->with('foo', $constraints) @@ -79,13 +75,9 @@ public function testEvaluate(bool $expected, int $errorsCount): void ->method('getViolations') ->willReturn($violationList); - $validator = $this->getMockBuilder(ValidatorInterface::class) - ->onlyMethods(['inContext']) - ->getMockForAbstractClass(); + $validator = $this->createMock(ValidatorInterface::class); - $context = $this->getMockBuilder(ExecutionContextInterface::class) - ->onlyMethods(['getValidator']) - ->getMockForAbstractClass(); + $context = $this->createMock(ExecutionContextInterface::class); $context->expects($this->once()) ->method('getValidator') ->willReturn($validator); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index e878ddcb75369..6df36c05315b6 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -308,7 +308,7 @@ public function testViolationOnPass() ->assertRaised(); } - public function testIsValidExpression(): void + public function testIsValidExpression() { $constraints = [new NotNull(), new Range(['min' => 2])]; @@ -328,7 +328,7 @@ public function testIsValidExpression(): void $this->assertNoViolation(); } - public function testIsValidExpressionInvalid(): void + public function testIsValidExpressionInvalid() { $constraints = [new Range(['min' => 2, 'max' => 5])]; @@ -346,15 +346,7 @@ public function testIsValidExpressionInvalid(): void 7, $constraints, null, - new ConstraintViolation( - 'error_range', - null, [], - null, - '', - null, - null, - 'range' - ) + new ConstraintViolation('error_range', '', [], '', '', 7, null, 'range') ); $this->validator->validate($object, $constraint); From a45c2af36f18b9f58b916c8d0a34247ca18251f4 Mon Sep 17 00:00:00 2001 From: Ihor Khokhlov Date: Thu, 11 Aug 2022 14:17:53 +0300 Subject: [PATCH 6/6] rework tests --- .../ExpressionLanguageProviderTest.php | 83 +++++++------------ 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php index 7c6243e68ffce..add2051e97942 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageProviderTest.php @@ -11,18 +11,20 @@ namespace Constraints; -use PHPUnit\Framework\TestCase; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; -use Symfony\Component\Validator\Constraints\NotNull; -use Symfony\Component\Validator\Constraints\Range; -use Symfony\Component\Validator\ConstraintViolationListInterface; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Validator\ContextualValidatorInterface; -use Symfony\Component\Validator\Validator\ValidatorInterface; - -class ExpressionLanguageProviderTest extends TestCase +use Symfony\Component\Validator\Constraints\ExpressionValidator; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class ExpressionLanguageProviderTest extends ConstraintValidatorTestCase { + protected function createValidator() + { + return new ExpressionValidator(); + } + /** * @dataProvider dataProviderCompile */ @@ -54,56 +56,33 @@ public function dataProviderCompile(): array ]; } - /** - * @dataProvider dataProviderEvaluate - */ - public function testEvaluate(bool $expected, int $errorsCount) + public function testEvaluateValid() { - $constraints = [new NotNull(), new Range(['min' => 2])]; - - $violationList = $this->createMock(ConstraintViolationListInterface::class); - $violationList->expects($this->once()) - ->method('count') - ->willReturn($errorsCount); - - $contextualValidator = $this->createMock(ContextualValidatorInterface::class); - $contextualValidator->expects($this->once()) - ->method('validate') - ->with('foo', $constraints) - ->willReturnSelf(); - $contextualValidator->expects($this->once()) - ->method('getViolations') - ->willReturn($violationList); - - $validator = $this->createMock(ValidatorInterface::class); - - $context = $this->createMock(ExecutionContextInterface::class); - $context->expects($this->once()) - ->method('getValidator') - ->willReturn($validator); - - $validator->expects($this->once()) - ->method('inContext') - ->with($context) - ->willReturn($contextualValidator); + $constraints = [new Length(['min' => 2, 'max' => 12])]; - $provider = new ExpressionLanguageProvider(); + $this->expectValidateValue(0, 'foo', $constraints); $expressionLanguage = new ExpressionLanguage(); - $expressionLanguage->registerProvider($provider); + $expressionLanguage->registerProvider(new ExpressionLanguageProvider()); - $this->assertSame( - $expected, - $expressionLanguage->evaluate('is_valid("foo", a)', ['a' => $constraints, 'context' => $context]) - ); + $this->assertTrue($expressionLanguage->evaluate('is_valid("foo", a)', ['a' => $constraints, 'context' => $this->context])); } - public function dataProviderEvaluate(): array + public function testEvaluateInvalid() { - return [ - [true, 0], - [false, 1], - [false, 12], - ]; + $constraints = [new Length(['min' => 7, 'max' => 12])]; + + $this->expectFailingValueValidation( + 0, + 'foo', + $constraints, + null, + new ConstraintViolation('error_length', '', [], '', '', 'foo', null, 'range') + ); + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->registerProvider(new ExpressionLanguageProvider()); + + $this->assertFalse($expressionLanguage->evaluate('is_valid("foo", a)', ['a' => $constraints, 'context' => $this->context])); } }