From 91bf2774a2b8d5006c4ed110e9c2c99605531932 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 17 Jul 2014 17:54:00 +0200 Subject: [PATCH 1/3] [Validator] Made sure that context changes don't leak out of (Contextual)ValidatorInterface --- .../Validator/Context/ExecutionContext.php | 2 +- .../Context/ExecutionContextInterface.php | 2 +- .../Tests/Validator/Abstract2Dot5ApiTest.php | 30 +++++++++++++++-- .../Tests/Validator/AbstractLegacyApiTest.php | 26 +++++++++++++-- .../RecursiveContextualValidator.php | 33 +++++++++++++++++++ 5 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 542ea336585f..4096a345bd9b 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -147,7 +147,7 @@ public function __construct(ValidatorInterface $validator, $root, TranslatorInte /** * {@inheritdoc} */ - public function setNode($value, $object, MetadataInterface $metadata, $propertyPath) + public function setNode($value, $object, MetadataInterface $metadata = null, $propertyPath) { $this->value = $value; $this->object = $object; diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index 37518fdf7a68..ee78cce183e2 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -124,7 +124,7 @@ public function getObject(); * @internal Used by the validator engine. Should not be called by user * code. */ - public function setNode($value, $object, MetadataInterface $metadata, $propertyPath); + public function setNode($value, $object, MetadataInterface $metadata = null, $propertyPath); /** * Sets the currently validated group. diff --git a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php index 02fefda5f042..e920da9a7219 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php @@ -193,13 +193,26 @@ public function testValidateInContext() $entity = new Entity(); $entity->reference = new Reference(); - $callback1 = function ($value, ExecutionContextInterface $context) { + $callback1 = function ($value, ExecutionContextInterface $context) use ($test) { + $previousValue = $context->getValue(); + $previousObject = $context->getObject(); + $previousMetadata = $context->getMetadata(); + $previousPath = $context->getPropertyPath(); + $previousGroup = $context->getGroup(); + $context ->getValidator() ->inContext($context) ->atPath('subpath') ->validate($value->reference) ; + + // context changes shouldn't leak out of the validate() call + $test->assertSame($previousValue, $context->getValue()); + $test->assertSame($previousObject, $context->getObject()); + $test->assertSame($previousMetadata, $context->getMetadata()); + $test->assertSame($previousPath, $context->getPropertyPath()); + $test->assertSame($previousGroup, $context->getGroup()); }; $callback2 = function ($value, ExecutionContextInterface $context) use ($test, $entity) { @@ -244,13 +257,26 @@ public function testValidateArrayInContext() $entity = new Entity(); $entity->reference = new Reference(); - $callback1 = function ($value, ExecutionContextInterface $context) { + $callback1 = function ($value, ExecutionContextInterface $context) use ($test) { + $previousValue = $context->getValue(); + $previousObject = $context->getObject(); + $previousMetadata = $context->getMetadata(); + $previousPath = $context->getPropertyPath(); + $previousGroup = $context->getGroup(); + $context ->getValidator() ->inContext($context) ->atPath('subpath') ->validate(array('key' => $value->reference)) ; + + // context changes shouldn't leak out of the validate() call + $test->assertSame($previousValue, $context->getValue()); + $test->assertSame($previousObject, $context->getObject()); + $test->assertSame($previousMetadata, $context->getMetadata()); + $test->assertSame($previousPath, $context->getPropertyPath()); + $test->assertSame($previousGroup, $context->getGroup()); }; $callback2 = function ($value, ExecutionContextInterface $context) use ($test, $entity) { diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractLegacyApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractLegacyApiTest.php index 56e94313bed3..53b935f5c373 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractLegacyApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/AbstractLegacyApiTest.php @@ -120,8 +120,19 @@ public function testValidateInContext() $entity = new Entity(); $entity->reference = new Reference(); - $callback1 = function ($value, ExecutionContextInterface $context) { + $callback1 = function ($value, ExecutionContextInterface $context) use ($test) { + $previousValue = $context->getValue(); + $previousMetadata = $context->getMetadata(); + $previousPath = $context->getPropertyPath(); + $previousGroup = $context->getGroup(); + $context->validate($value->reference, 'subpath'); + + // context changes shouldn't leak out of the validate() call + $test->assertSame($previousValue, $context->getValue()); + $test->assertSame($previousMetadata, $context->getMetadata()); + $test->assertSame($previousPath, $context->getPropertyPath()); + $test->assertSame($previousGroup, $context->getGroup()); }; $callback2 = function ($value, ExecutionContextInterface $context) use ($test, $entity) { @@ -167,8 +178,19 @@ public function testValidateArrayInContext() $entity = new Entity(); $entity->reference = new Reference(); - $callback1 = function ($value, ExecutionContextInterface $context) { + $callback1 = function ($value, ExecutionContextInterface $context) use ($test) { + $previousValue = $context->getValue(); + $previousMetadata = $context->getMetadata(); + $previousPath = $context->getPropertyPath(); + $previousGroup = $context->getGroup(); + $context->validate(array('key' => $value->reference), 'subpath'); + + // context changes shouldn't leak out of the validate() call + $test->assertSame($previousValue, $context->getValue()); + $test->assertSame($previousMetadata, $context->getMetadata()); + $test->assertSame($previousPath, $context->getPropertyPath()); + $test->assertSame($previousGroup, $context->getGroup()); }; $callback2 = function ($value, ExecutionContextInterface $context) use ($test, $entity) { diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 3738dab069a0..581b2f17f50f 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -96,6 +96,12 @@ public function validate($value, $constraints = null, $groups = null) { $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; + $previousValue = $this->context->getValue(); + $previousObject = $this->context->getObject(); + $previousMetadata = $this->context->getMetadata(); + $previousPath = $this->context->getPropertyPath(); + $previousGroup = $this->context->getGroup(); + // If explicit constraints are passed, validate the value against // those constraints if (null !== $constraints) { @@ -120,6 +126,9 @@ public function validate($value, $constraints = null, $groups = null) $this->context ); + $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath); + $this->context->setGroup($previousGroup); + return $this; } @@ -134,6 +143,9 @@ public function validate($value, $constraints = null, $groups = null) $this->context ); + $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath); + $this->context->setGroup($previousGroup); + return $this; } @@ -148,6 +160,9 @@ public function validate($value, $constraints = null, $groups = null) $this->context ); + $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath); + $this->context->setGroup($previousGroup); + return $this; } @@ -180,6 +195,12 @@ public function validateProperty($object, $propertyName, $groups = null) $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; $cacheKey = spl_object_hash($object); + $previousValue = $this->context->getValue(); + $previousObject = $this->context->getObject(); + $previousMetadata = $this->context->getMetadata(); + $previousPath = $this->context->getPropertyPath(); + $previousGroup = $this->context->getGroup(); + foreach ($propertyMetadatas as $propertyMetadata) { $propertyValue = $propertyMetadata->getPropertyValue($object); @@ -196,6 +217,9 @@ public function validateProperty($object, $propertyName, $groups = null) ); } + $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath); + $this->context->setGroup($previousGroup); + return $this; } @@ -221,6 +245,12 @@ public function validatePropertyValue($object, $propertyName, $value, $groups = $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; $cacheKey = spl_object_hash($object); + $previousValue = $this->context->getValue(); + $previousObject = $this->context->getObject(); + $previousMetadata = $this->context->getMetadata(); + $previousPath = $this->context->getPropertyPath(); + $previousGroup = $this->context->getGroup(); + foreach ($propertyMetadatas as $propertyMetadata) { $this->validateGenericNode( $value, @@ -235,6 +265,9 @@ public function validatePropertyValue($object, $propertyName, $value, $groups = ); } + $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath); + $this->context->setGroup($previousGroup); + return $this; } From 14b60c8c8fbab97f026eaaf3b835dbbe8001eed1 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sat, 26 Jul 2014 14:41:49 +0200 Subject: [PATCH 2/3] [Validator] Fixed doc block --- .../Validator/Context/ExecutionContextInterface.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index ee78cce183e2..d91c5304fbae 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -116,10 +116,10 @@ public function getObject(); /** * Sets the currently validated value. * - * @param mixed $value The validated value - * @param object|null $object The currently validated object - * @param MetadataInterface $metadata The validation metadata - * @param string $propertyPath The property path to the current value + * @param mixed $value The validated value + * @param object|null $object The currently validated object + * @param MetadataInterface|null $metadata The validation metadata + * @param string $propertyPath The property path to the current value * * @internal Used by the validator engine. Should not be called by user * code. From ff489390c6910d1499253f8d5a76cec71d3bf906 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sat, 26 Jul 2014 14:43:06 +0200 Subject: [PATCH 3/3] [Validator] Added markObjectAsInitialized() and isObjectInitialized() to ExecutionContextInterface --- .../Validator/Context/ExecutionContext.php | 21 ++------------- .../Context/ExecutionContextInterface.php | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 4096a345bd9b..3bd423fb6590 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -18,7 +18,6 @@ use Symfony\Component\Validator\Exception\BadMethodCallException; use Symfony\Component\Validator\Mapping\MetadataInterface; use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; -use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\Validator\Util\PropertyPath; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Violation\ConstraintViolationBuilder; @@ -370,14 +369,7 @@ public function isConstraintValidated($cacheKey, $constraintHash) } /** - * Marks that an object was initialized. - * - * @param string $cacheKey The hash of the object - * - * @internal Used by the validator engine. Should not be called by user - * code. - * - * @see ObjectInitializerInterface + * {@inheritdoc} */ public function markObjectAsInitialized($cacheKey) { @@ -385,16 +377,7 @@ public function markObjectAsInitialized($cacheKey) } /** - * Returns whether an object was initialized. - * - * @param string $cacheKey The hash of the object - * - * @return bool Whether the object was already initialized - * - * @internal Used by the validator engine. Should not be called by user - * code. - * - * @see ObjectInitializerInterface + * {@inheritdoc} */ public function isObjectInitialized($cacheKey) { diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index d91c5304fbae..d2527f4694ee 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -186,4 +186,30 @@ public function markConstraintAsValidated($cacheKey, $constraintHash); * code. */ public function isConstraintValidated($cacheKey, $constraintHash); + + /** + * Marks that an object was initialized. + * + * @param string $cacheKey The hash of the object + * + * @internal Used by the validator engine. Should not be called by user + * code. + * + * @see ObjectInitializerInterface + */ + public function markObjectAsInitialized($cacheKey); + + /** + * Returns whether an object was initialized. + * + * @param string $cacheKey The hash of the object + * + * @return bool Whether the object was already initialized + * + * @internal Used by the validator engine. Should not be called by user + * code. + * + * @see ObjectInitializerInterface + */ + public function isObjectInitialized($cacheKey); }