diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md index a7082b195a..09d82ce62e 100644 --- a/CHANGELOG-2.1.md +++ b/CHANGELOG-2.1.md @@ -299,6 +299,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c individual fields * changed default value for `extraFieldsMessage` and `missingFieldsMessage` in Collection constraint + * made ExecutionContext immutable ### Yaml diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 5b70d5927d..2dd1989440 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -102,10 +102,7 @@ class UniqueEntityValidator extends ConstraintValidator return true; } - $oldPath = $this->context->getPropertyPath(); - $this->context->setPropertyPath( empty($oldPath) ? $fields[0] : $oldPath.'.'.$fields[0]); - $this->context->addViolation($constraint->message, array(), $criteria[$fields[0]]); - $this->context->setPropertyPath($oldPath); + $this->context->addNestedViolationAt($fields[0], $constraint->message, array(), $criteria[$fields[0]]); return true; // all true, we added the violation already! } diff --git a/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php b/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php index 431205efc9..8717eb20b3 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php @@ -111,10 +111,6 @@ class DelegatingValidator implements FormValidatorInterface $propertyPath = $context->getPropertyPath(); $graphWalker = $context->getGraphWalker(); - // The Execute constraint is called on class level, so we need to - // set the property manually - $context->setCurrentProperty('data'); - // Adjust the property path accordingly if (!empty($propertyPath)) { $propertyPath .= '.'; @@ -134,10 +130,6 @@ class DelegatingValidator implements FormValidatorInterface $propertyPath = $context->getPropertyPath(); $graphWalker = $context->getGraphWalker(); - // The Execute constraint is called on class level, so we need to - // set the property manually - $context->setCurrentProperty('children'); - // Adjust the property path accordingly if (!empty($propertyPath)) { $propertyPath .= '.'; diff --git a/src/Symfony/Component/Validator/ConstraintViolationList.php b/src/Symfony/Component/Validator/ConstraintViolationList.php index 6a0825390a..96efce4e81 100644 --- a/src/Symfony/Component/Validator/ConstraintViolationList.php +++ b/src/Symfony/Component/Validator/ConstraintViolationList.php @@ -78,13 +78,13 @@ EOF; /** * Merge an existing ConstraintViolationList into this list. * - * @param ConstraintViolationList $violations + * @param ConstraintViolationList $otherList * * @api */ - public function addAll(ConstraintViolationList $violations) + public function addAll(ConstraintViolationList $otherList) { - foreach ($violations->violations as $violation) { + foreach ($otherList->violations as $violation) { $this->violations[] = $violation; } } diff --git a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php index a5df3d2e05..34104c0eac 100644 --- a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php @@ -48,13 +48,6 @@ class CallbackValidator extends ConstraintValidator } $methods = $constraint->methods; - $context = $this->context; - - // save context state - $currentClass = $context->getCurrentClass(); - $currentProperty = $context->getCurrentProperty(); - $group = $context->getGroup(); - $propertyPath = $context->getPropertyPath(); foreach ($methods as $method) { if (is_array($method) || $method instanceof \Closure) { @@ -62,20 +55,14 @@ class CallbackValidator extends ConstraintValidator throw new ConstraintDefinitionException(sprintf('"%s::%s" targeted by Callback constraint is not a valid callable', $method[0], $method[1])); } - call_user_func($method, $object, $context); + call_user_func($method, $object, $this->context); } else { if (!method_exists($object, $method)) { throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist', $method)); } - $object->$method($context); + $object->$method($this->context); } - - // restore context state - $context->setCurrentClass($currentClass); - $context->setCurrentProperty($currentProperty); - $context->setGroup($group); - $context->setPropertyPath($propertyPath); } return true; diff --git a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php index 2395fd23a7..c380041ee6 100644 --- a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php @@ -68,7 +68,7 @@ class CollectionValidator extends ConstraintValidator $walker->walkConstraint($constr, $value[$field], $group, $propertyPath.'['.$field.']'); } } elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) { - $this->context->addViolationAt($propertyPath.'['.$field.']', $constraint->missingFieldsMessage, array( + $this->context->addNestedViolationAt('['.$field.']', $constraint->missingFieldsMessage, array( '{{ field }}' => '"'.$field.'"' ), null); $valid = false; @@ -78,7 +78,7 @@ class CollectionValidator extends ConstraintValidator if (!$constraint->allowExtraFields) { foreach ($value as $field => $fieldValue) { if (!isset($constraint->fields[$field])) { - $this->context->addViolationAt($propertyPath.'['.$field.']', $constraint->extraFieldsMessage, array( + $this->context->addNestedViolationAt('['.$field.']', $constraint->extraFieldsMessage, array( '{{ field }}' => '"'.$field.'"' ), $fieldValue); $valid = false; diff --git a/src/Symfony/Component/Validator/ExecutionContext.php b/src/Symfony/Component/Validator/ExecutionContext.php index 87fb1c7f99..1118d9d90a 100644 --- a/src/Symfony/Component/Validator/ExecutionContext.php +++ b/src/Symfony/Component/Validator/ExecutionContext.php @@ -16,8 +16,10 @@ use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface; /** * Stores the state of the current node in the validation graph. * - * This object is used by the GraphWalker to initialize validation of different - * items and keep track of the violations. + * This class is immutable by design. + * + * It is used by the GraphWalker to initialize validation of different items + * and keep track of the violations. * * @author Fabien Potencier * @author Bernhard Schussek @@ -26,60 +28,97 @@ use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface; */ class ExecutionContext { - protected $root; - protected $propertyPath; - protected $class; - protected $property; - protected $value; - protected $group; - protected $violations; - protected $graphWalker; - protected $metadataFactory; + private $globalContext; + private $propertyPath; + private $value; + private $group; + private $class; + private $property; - public function __construct( - $root, - GraphWalker $graphWalker, - ClassMetadataFactoryInterface $metadataFactory - ) + public function __construct(GlobalExecutionContext $globalContext, $value, $propertyPath, $group, $class = null, $property = null) { - $this->root = $root; - $this->graphWalker = $graphWalker; - $this->metadataFactory = $metadataFactory; - $this->violations = new ConstraintViolationList(); + $this->globalContext = $globalContext; + $this->value = $value; + $this->propertyPath = $propertyPath; + $this->group = $group; + $this->class = $class; + $this->property = $property; } public function __clone() { - $this->violations = clone $this->violations; + $this->globalContext = clone $this->globalContext; } /** + * Adds a violation at the current node of the validation graph. + * + * @param string $message The error message. + * @param array $params The parameters parsed into the error message. + * @param mixed $invalidValue The invalid, validated value. + * * @api */ public function addViolation($message, array $params = array(), $invalidValue = null) { - $this->violations->add(new ConstraintViolation( + $this->globalContext->addViolation(new ConstraintViolation( $message, $params, - $this->root, + $this->globalContext->getRoot(), $this->propertyPath, // check using func_num_args() to allow passing null values func_num_args() === 3 ? $invalidValue : $this->value )); } + /** + * Adds a violation at the validation graph node with the given property + * path. + * + * @param string $propertyPath The property path for the violation. + * @param string $message The error message. + * @param array $params The parameters parsed into the error message. + * @param mixed $invalidValue The invalid, validated value. + */ public function addViolationAt($propertyPath, $message, array $params = array(), $invalidValue = null) { - $this->violations->add(new ConstraintViolation( + $this->globalContext->addViolation(new ConstraintViolation( $message, $params, - $this->root, + $this->globalContext->getRoot(), $propertyPath, // check using func_num_args() to allow passing null values func_num_args() === 4 ? $invalidValue : $this->value )); } + /** + * Adds a violation at the child of the current validation graph node with + * the given property path. + * + * @param string $childPropertyPath The property path of the child node. + * @param string $message The error message. + * @param array $params The parameters parsed into the error message. + * @param mixed $invalidValue The invalid, validated value. + */ + public function addNestedViolationAt($childPropertyPath, $message, array $params = array(), $invalidValue = null) + { + $propertyPath = $this->propertyPath; + + if ('' !== $propertyPath && '' !== $childPropertyPath && '[' !== $childPropertyPath[0]) { + $propertyPath .= '.'; + } + + $this->globalContext->addViolation(new ConstraintViolation( + $message, + $params, + $this->globalContext->getRoot(), + $propertyPath . $childPropertyPath, + // check using func_num_args() to allow passing null values + func_num_args() === 4 ? $invalidValue : $this->value + )); + } + /** * @return ConstraintViolationList * @@ -87,17 +126,12 @@ class ExecutionContext */ public function getViolations() { - return $this->violations; + return $this->globalContext->getViolations(); } public function getRoot() { - return $this->root; - } - - public function setPropertyPath($propertyPath) - { - $this->propertyPath = $propertyPath; + return $this->globalContext->getRoot(); } public function getPropertyPath() @@ -105,41 +139,21 @@ class ExecutionContext return $this->propertyPath; } - public function setCurrentClass($class) - { - $this->class = $class; - } - public function getCurrentClass() { return $this->class; } - public function setCurrentProperty($property) - { - $this->property = $property; - } - public function getCurrentProperty() { return $this->property; } - public function setCurrentValue($value) - { - $this->value = $value; - } - public function getCurrentValue() { return $this->value; } - public function setGroup($group) - { - $this->group = $group; - } - public function getGroup() { return $this->group; @@ -150,7 +164,7 @@ class ExecutionContext */ public function getGraphWalker() { - return $this->graphWalker; + return $this->globalContext->getGraphWalker(); } /** @@ -158,6 +172,6 @@ class ExecutionContext */ public function getMetadataFactory() { - return $this->metadataFactory; + return $this->globalContext->getMetadataFactory(); } } diff --git a/src/Symfony/Component/Validator/GlobalExecutionContext.php b/src/Symfony/Component/Validator/GlobalExecutionContext.php new file mode 100644 index 0000000000..36f71fdf7f --- /dev/null +++ b/src/Symfony/Component/Validator/GlobalExecutionContext.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator; + +use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface; + +/** + * Stores the node-independent information of a validation run. + * + * This class is immutable by design, except for violation tracking. + * + * @author Bernhard Schussek + */ +class GlobalExecutionContext +{ + private $root; + private $graphWalker; + private $metadataFactory; + private $violations; + + public function __construct( + $root, + GraphWalker $graphWalker, + ClassMetadataFactoryInterface $metadataFactory + ) + { + $this->root = $root; + $this->graphWalker = $graphWalker; + $this->metadataFactory = $metadataFactory; + $this->violations = new ConstraintViolationList(); + } + + public function __clone() + { + $this->violations = clone $this->violations; + } + + public function addViolation(ConstraintViolation $violation) + { + $this->violations->add($violation); + } + + /** + * @return ConstraintViolationList + */ + public function getViolations() + { + return $this->violations; + } + + public function getRoot() + { + return $this->root; + } + + /** + * @return GraphWalker + */ + public function getGraphWalker() + { + return $this->graphWalker; + } + + /** + * @return ClassMetadataFactoryInterface + */ + public function getMetadataFactory() + { + return $this->metadataFactory; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/Validator/GraphWalker.php b/src/Symfony/Component/Validator/GraphWalker.php index 7e9047e195..da6e5a0aea 100644 --- a/src/Symfony/Component/Validator/GraphWalker.php +++ b/src/Symfony/Component/Validator/GraphWalker.php @@ -27,15 +27,15 @@ use Symfony\Component\Validator\Mapping\MemberMetadata; */ class GraphWalker { - protected $context; - protected $validatorFactory; - protected $metadataFactory; - protected $validatorInitializers = array(); - protected $validatedObjects = array(); + private $globalContext; + private $validatorFactory; + private $metadataFactory; + private $validatorInitializers = array(); + private $validatedObjects = array(); public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = array()) { - $this->context = new ExecutionContext($root, $this, $metadataFactory); + $this->globalContext = new GlobalExecutionContext($root, $this, $metadataFactory); $this->validatorFactory = $factory; $this->metadataFactory = $metadataFactory; $this->validatorInitializers = $validatorInitializers; @@ -46,7 +46,7 @@ class GraphWalker */ public function getViolations() { - return $this->context->getViolations(); + return $this->globalContext->getViolations(); } /** @@ -67,8 +67,6 @@ class GraphWalker $initializer->initialize($object); } - $this->context->setCurrentClass($metadata->getClassName()); - if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) { $groups = $metadata->getGroupSequence(); foreach ($groups as $group) { @@ -100,8 +98,10 @@ class GraphWalker // traversing the object graph $this->validatedObjects[$hash][$group] = true; + $currentClass = $metadata->getClassName(); + foreach ($metadata->findConstraints($group) as $constraint) { - $this->walkConstraint($constraint, $object, $group, $propertyPath); + $this->walkConstraint($constraint, $object, $group, $propertyPath, $currentClass); } if (null !== $object) { @@ -129,11 +129,11 @@ class GraphWalker protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null) { - $this->context->setCurrentClass($metadata->getClassName()); - $this->context->setCurrentProperty($metadata->getPropertyName()); + $currentClass = $metadata->getClassName(); + $currentProperty = $metadata->getPropertyName(); foreach ($metadata->findConstraints($group) as $constraint) { - $this->walkConstraint($constraint, $value, $group, $propertyPath); + $this->walkConstraint($constraint, $value, $group, $propertyPath, $currentClass, $currentProperty); } if ($metadata->isCascaded()) { @@ -164,15 +164,20 @@ class GraphWalker } } - public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath) + public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath, $currentClass = null, $currentProperty = null) { $validator = $this->validatorFactory->getInstance($constraint); - $this->context->setCurrentValue($value); - $this->context->setPropertyPath($propertyPath); - $this->context->setGroup($group); + $localContext = new ExecutionContext( + $this->globalContext, + $value, + $propertyPath, + $group, + $currentClass, + $currentProperty + ); - $validator->initialize($this->context); + $validator->initialize($localContext); if (!$validator->isValid($value, $constraint)) { $messageTemplate = $validator->getMessageTemplate(); @@ -180,15 +185,9 @@ class GraphWalker // Somewhat ugly hack: Don't add a violation if no message is set. // This is required if the validator added its violations directly - // to the context already + // to the globalContext already if (!empty($messageTemplate)) { - // Resetting the property path. This is needed because some - // validators, like CollectionValidator, use the walker internally - // and so change the context. - $this->context->setCurrentValue($value); - $this->context->setPropertyPath($propertyPath); - - $this->context->addViolation($messageTemplate, $messageParams); + $localContext->addViolation($messageTemplate, $messageParams); } } } diff --git a/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php b/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php index e1081a2863..27fc1cb27d 100644 --- a/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php +++ b/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Tests\Component\Form\Extension\Validator\Validator; +use Symfony\Component\Validator\GlobalExecutionContext; + use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormError; @@ -63,6 +65,15 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase return $this->getMock('Symfony\Component\Form\DataTransformerInterface', array(), array(), '', false, false); } + protected function getExecutionContext($propertyPath = null) + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $globalContext = new GlobalExecutionContext('Root', $graphWalker, $metadataFactory); + + return new ExecutionContext($globalContext, null, $propertyPath, null, null, null); + } + protected function getConstraintViolation($propertyPath) { return new ConstraintViolation($this->message, $this->params, null, $propertyPath, null); @@ -589,9 +600,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormData() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context = $this->getExecutionContext(); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $form = $this->getBuilder() ->setAttribute('validation_groups', array('group1', 'group2')) @@ -611,9 +621,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormDataCanHandleCallbackValidationGroups() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context = $this->getExecutionContext(); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $form = $this->getBuilder() ->setAttribute('validation_groups', array($this, 'getValidationGroups')) @@ -633,9 +642,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormDataCanHandleClosureValidationGroups() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context = $this->getExecutionContext(); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $form = $this->getBuilder() ->setAttribute('validation_groups', function(FormInterface $form){ @@ -657,10 +665,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormDataUsesInheritedValidationGroup() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $context->setPropertyPath('path'); + $context = $this->getExecutionContext('foo.bar'); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $parent = $this->getBuilder() @@ -675,17 +681,15 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker->expects($this->once()) ->method('walkReference') - ->with($object, 'group', 'path.data', true); + ->with($object, 'group', 'foo.bar.data', true); DelegatingValidator::validateFormData($child, $context); } public function testValidateFormDataUsesInheritedCallbackValidationGroup() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $context->setPropertyPath('path'); + $context = $this->getExecutionContext('foo.bar'); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $parent = $this->getBuilder() @@ -700,20 +704,18 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker->expects($this->at(0)) ->method('walkReference') - ->with($object, 'group1', 'path.data', true); + ->with($object, 'group1', 'foo.bar.data', true); $graphWalker->expects($this->at(1)) ->method('walkReference') - ->with($object, 'group2', 'path.data', true); + ->with($object, 'group2', 'foo.bar.data', true); DelegatingValidator::validateFormData($child, $context); } public function testValidateFormDataUsesInheritedClosureValidationGroup() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $context->setPropertyPath('path'); + $context = $this->getExecutionContext('foo.bar'); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $parent = $this->getBuilder() @@ -730,46 +732,24 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker->expects($this->at(0)) ->method('walkReference') - ->with($object, 'group1', 'path.data', true); + ->with($object, 'group1', 'foo.bar.data', true); $graphWalker->expects($this->at(1)) ->method('walkReference') - ->with($object, 'group2', 'path.data', true); + ->with($object, 'group2', 'foo.bar.data', true); DelegatingValidator::validateFormData($child, $context); } public function testValidateFormDataAppendsPropertyPath() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $context->setPropertyPath('path'); + $context = $this->getExecutionContext('foo.bar'); + $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); $form = $this->getForm(); $graphWalker->expects($this->once()) ->method('walkReference') - ->with($object, 'Default', 'path.data', true); - - $form->setData($object); - - DelegatingValidator::validateFormData($form, $context); - } - - public function testValidateFormDataSetsCurrentPropertyToData() - { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $object = $this->getMock('\stdClass'); - $form = $this->getForm(); - $test = $this; - - $graphWalker->expects($this->once()) - ->method('walkReference') - ->will($this->returnCallback(function () use ($context, $test) { - $test->assertEquals('data', $context->getCurrentProperty()); - })); + ->with($object, 'Default', 'foo.bar.data', true); $form->setData($object); @@ -778,9 +758,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormDataDoesNotWalkScalars() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context = $this->getExecutionContext(); + $graphWalker = $context->getGraphWalker(); $clientTransformer = $this->getMockTransformer(); $form = $this->getBuilder() @@ -801,9 +780,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormChildren() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context = $this->getExecutionContext(); + $graphWalker = $context->getGraphWalker(); $form = $this->getBuilder() ->setAttribute('cascade_validation', true) ->setAttribute('validation_groups', array('group1', 'group2')) @@ -821,10 +799,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase public function testValidateFormChildrenAppendsPropertyPath() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $context->setPropertyPath('path'); + $context = $this->getExecutionContext('foo.bar'); + $graphWalker = $context->getGraphWalker(); $form = $this->getBuilder() ->setAttribute('cascade_validation', true) ->getForm(); @@ -832,36 +808,15 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker->expects($this->once()) ->method('walkReference') - ->with($form->getChildren(), 'Default', 'path.children', true); - - DelegatingValidator::validateFormChildren($form, $context); - } - - public function testValidateFormChildrenSetsCurrentPropertyToData() - { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); - $form = $this->getBuilder() - ->setAttribute('cascade_validation', true) - ->getForm(); - $form->add($this->getForm('firstName')); - $test = $this; - - $graphWalker->expects($this->once()) - ->method('walkReference') - ->will($this->returnCallback(function () use ($context, $test) { - $test->assertEquals('children', $context->getCurrentProperty()); - })); + ->with($form->getChildren(), 'Default', 'foo.bar.children', true); DelegatingValidator::validateFormChildren($form, $context); } public function testValidateFormChildrenDoesNothingIfDisabled() { - $graphWalker = $this->getMockGraphWalker(); - $metadataFactory = $this->getMockMetadataFactory(); - $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context = $this->getExecutionContext(); + $graphWalker = $context->getGraphWalker(); $form = $this->getBuilder() ->setAttribute('cascade_validation', false) ->getForm(); diff --git a/tests/Symfony/Tests/Component/Validator/ConstraintViolationListTest.php b/tests/Symfony/Tests/Component/Validator/ConstraintViolationListTest.php new file mode 100644 index 0000000000..37c376fb83 --- /dev/null +++ b/tests/Symfony/Tests/Component/Validator/ConstraintViolationListTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Validator; + +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationList; + +class ConstraintViolationListTest extends \PHPUnit_Framework_TestCase +{ + protected $list; + + protected function setUp() + { + $this->list = new ConstraintViolationList(); + } + + protected function tearDown() + { + $this->list = null; + } + + public function testInit() + { + $this->assertCount(0, $this->list); + } + + public function testInitWithViolations() + { + $violation = $this->getViolation('Error'); + $this->list = new ConstraintViolationList(array($violation)); + + $this->assertCount(1, $this->list); + $this->assertSame($violation, $this->list[0]); + } + + public function testAdd() + { + $violation = $this->getViolation('Error'); + $this->list->add($violation); + + $this->assertCount(1, $this->list); + $this->assertSame($violation, $this->list[0]); + } + + public function testAddAll() + { + $violations = array( + 10 => $this->getViolation('Error 1'), + 20 => $this->getViolation('Error 2'), + 30 => $this->getViolation('Error 3'), + ); + $otherList = new ConstraintViolationList($violations); + $this->list->addAll($otherList); + + $this->assertCount(3, $this->list); + + $this->assertSame($violations[10], $this->list[0]); + $this->assertSame($violations[20], $this->list[1]); + $this->assertSame($violations[30], $this->list[2]); + } + + public function testIterator() + { + $violations = array( + 10 => $this->getViolation('Error 1'), + 20 => $this->getViolation('Error 2'), + 30 => $this->getViolation('Error 3'), + ); + + $this->list = new ConstraintViolationList($violations); + + // indices are reset upon adding -> array_values() + $this->assertSame(array_values($violations), iterator_to_array($this->list)); + } + + public function testArrayAccess() + { + $violation = $this->getViolation('Error'); + $this->list[] = $violation; + + $this->assertSame($violation, $this->list[0]); + $this->assertTrue(isset($this->list[0])); + + unset($this->list[0]); + + $this->assertFalse(isset($this->list[0])); + + $this->list[10] = $violation; + + $this->assertSame($violation, $this->list[10]); + $this->assertTrue(isset($this->list[10])); + } + + public function testToString() + { + $this->list = new ConstraintViolationList(array( + $this->getViolation('Error 1', 'Root'), + $this->getViolation('Error 2', 'Root', 'foo.bar'), + $this->getViolation('Error 3', 'Root', '[baz]'), + $this->getViolation('Error 4', '', 'foo.bar'), + $this->getViolation('Error 5', '', '[baz]'), + )); + + $expected = <<assertEquals($expected, (string) $this->list); + } + + protected function getViolation($message, $root = null, $propertyPath = null) + { + return new ConstraintViolation($message, array(), $root, $propertyPath, null); + } +} diff --git a/tests/Symfony/Tests/Component/Validator/Constraints/AllValidatorTest.php b/tests/Symfony/Tests/Component/Validator/Constraints/AllValidatorTest.php index 2842f298a5..0649833574 100644 --- a/tests/Symfony/Tests/Component/Validator/Constraints/AllValidatorTest.php +++ b/tests/Symfony/Tests/Component/Validator/Constraints/AllValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Tests\Component\Validator\Constraints; +use Symfony\Component\Validator\GlobalExecutionContext; + use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\Constraints\Min; use Symfony\Component\Validator\Constraints\All; @@ -26,8 +28,9 @@ class AllValidatorTest extends \PHPUnit_Framework_TestCase { $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); $metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); + $globalContext = new GlobalExecutionContext('Root', $this->walker, $metadataFactory); - $this->context = new ExecutionContext('Root', $this->walker, $metadataFactory); + $this->context = new ExecutionContext($globalContext, null, 'foo', 'MyGroup', null, null); $this->validator = new AllValidator(); $this->validator->initialize($this->context); @@ -57,9 +60,6 @@ class AllValidatorTest extends \PHPUnit_Framework_TestCase */ public function testWalkSingleConstraint($array) { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('foo'); - $constraint = new Min(4); foreach ($array as $key => $value) { @@ -76,9 +76,6 @@ class AllValidatorTest extends \PHPUnit_Framework_TestCase */ public function testWalkMultipleConstraints($array) { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('foo'); - $constraint = new Min(4); // can only test for twice the same constraint because PHPUnits mocking // can't test method calls with different arguments diff --git a/tests/Symfony/Tests/Component/Validator/Constraints/CallbackValidatorTest.php b/tests/Symfony/Tests/Component/Validator/Constraints/CallbackValidatorTest.php index 496fb84b11..d8822e5dc5 100644 --- a/tests/Symfony/Tests/Component/Validator/Constraints/CallbackValidatorTest.php +++ b/tests/Symfony/Tests/Component/Validator/Constraints/CallbackValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Tests\Component\Validator\Constraints; +use Symfony\Component\Validator\GlobalExecutionContext; use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; @@ -21,11 +22,6 @@ class CallbackValidatorTest_Class { public static function validateStatic($object, ExecutionContext $context) { - $context->setCurrentClass('Foo'); - $context->setCurrentProperty('bar'); - $context->setGroup('mygroup'); - $context->setPropertyPath('foo.bar'); - $context->addViolation('Static message', array('parameter'), 'invalidValue'); } } @@ -34,11 +30,6 @@ class CallbackValidatorTest_Object { public function validateOne(ExecutionContext $context) { - $context->setCurrentClass('Foo'); - $context->setCurrentProperty('bar'); - $context->setGroup('mygroup'); - $context->setPropertyPath('foo.bar'); - $context->addViolation('My message', array('parameter'), 'invalidValue'); } @@ -58,13 +49,9 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase { $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); $metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); + $globalContext = new GlobalExecutionContext('Root', $this->walker, $metadataFactory); - $this->context = new ExecutionContext('Root', $this->walker, $metadataFactory); - $this->context->setCurrentClass('InitialClass'); - $this->context->setCurrentProperty('initialProperty'); - $this->context->setGroup('InitialGroup'); - $this->context->setPropertyPath('initial.property.path'); - + $this->context = new ExecutionContext($globalContext, 'value', 'foo.bar', 'Group', 'ClassName', 'propertyName'); $this->validator = new CallbackValidator(); $this->validator->initialize($this->context); } @@ -97,10 +84,6 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase )); $this->assertEquals($violations, $this->context->getViolations()); - $this->assertEquals('InitialClass', $this->context->getCurrentClass()); - $this->assertEquals('initialProperty', $this->context->getCurrentProperty()); - $this->assertEquals('InitialGroup', $this->context->getGroup()); - $this->assertEquals('initial.property.path', $this->context->getPropertyPath()); } public function testCallbackSingleStaticMethod() @@ -121,10 +104,6 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase )); $this->assertEquals($violations, $this->context->getViolations()); - $this->assertEquals('InitialClass', $this->context->getCurrentClass()); - $this->assertEquals('initialProperty', $this->context->getCurrentProperty()); - $this->assertEquals('InitialGroup', $this->context->getGroup()); - $this->assertEquals('initial.property.path', $this->context->getPropertyPath()); } public function testCallbackMultipleMethods() @@ -143,13 +122,11 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase 'foo.bar', 'invalidValue' )); - - // context was reset $violations->add(new ConstraintViolation( 'Other message', array('other parameter'), 'Root', - 'initial.property.path', + 'foo.bar', 'otherInvalidValue' )); diff --git a/tests/Symfony/Tests/Component/Validator/Constraints/ChoiceValidatorTest.php b/tests/Symfony/Tests/Component/Validator/Constraints/ChoiceValidatorTest.php index d2b2659661..54fc6305ef 100644 --- a/tests/Symfony/Tests/Component/Validator/Constraints/ChoiceValidatorTest.php +++ b/tests/Symfony/Tests/Component/Validator/Constraints/ChoiceValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Tests\Component\Validator\Constraints; +use Symfony\Component\Validator\GlobalExecutionContext; + use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\Constraints\Choice; use Symfony\Component\Validator\Constraints\ChoiceValidator; @@ -33,8 +35,8 @@ class ChoiceValidatorTest extends \PHPUnit_Framework_TestCase { $walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); $factory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); - $context = new ExecutionContext('root', $walker, $factory); - $context->setCurrentClass(__CLASS__); + $globalContext = new GlobalExecutionContext('root', $walker, $factory); + $context = new ExecutionContext($globalContext, null, null, null, __CLASS__, null); $this->validator = new ChoiceValidator(); $this->validator->initialize($context); } diff --git a/tests/Symfony/Tests/Component/Validator/Constraints/CollectionValidatorTest.php b/tests/Symfony/Tests/Component/Validator/Constraints/CollectionValidatorTest.php index 9898425b2f..d52b0242a2 100644 --- a/tests/Symfony/Tests/Component/Validator/Constraints/CollectionValidatorTest.php +++ b/tests/Symfony/Tests/Component/Validator/Constraints/CollectionValidatorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Tests\Component\Validator\Constraints; +use Symfony\Component\Validator\GlobalExecutionContext; + use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ExecutionContext; @@ -25,6 +27,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase { protected $validator; protected $walker; + protected $globalContext; protected $context; protected function setUp() @@ -32,7 +35,8 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); $metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); - $this->context = new ExecutionContext('Root', $this->walker, $metadataFactory); + $this->globalContext = new GlobalExecutionContext('Root', $this->walker, $metadataFactory); + $this->context = new ExecutionContext($this->globalContext, 'value', 'bar', 'MyGroup', 'ClassName', 'propertyName'); $this->validator = new CollectionValidator(); $this->validator->initialize($this->context); @@ -42,6 +46,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase { $this->validator = null; $this->walker = null; + $this->globalContext = null; $this->context = null; } @@ -75,9 +80,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testWalkSingleConstraint() { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('foo'); - $constraint = new Min(4); $array = array('foo' => 3); @@ -85,7 +87,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase foreach ($array as $key => $value) { $this->walker->expects($this->once()) ->method('walkConstraint') - ->with($this->equalTo($constraint), $this->equalTo($value), $this->equalTo('MyGroup'), $this->equalTo('foo['.$key.']')); + ->with($this->equalTo($constraint), $this->equalTo($value), $this->equalTo('MyGroup'), $this->equalTo('bar['.$key.']')); } $data = $this->prepareTestData($array); @@ -99,9 +101,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testWalkMultipleConstraints() { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('foo'); - $constraints = array( new Min(4), new NotNull(), @@ -112,7 +111,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase foreach ($constraints as $i => $constraint) { $this->walker->expects($this->at($i)) ->method('walkConstraint') - ->with($this->equalTo($constraint), $this->equalTo($value), $this->equalTo('MyGroup'), $this->equalTo('foo['.$key.']')); + ->with($this->equalTo($constraint), $this->equalTo($value), $this->equalTo('MyGroup'), $this->equalTo('bar['.$key.']')); } } @@ -132,9 +131,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase 'baz' => 6, )); - $this->context->setCurrentValue($data); - $this->context->setPropertyPath('bar'); - $this->assertFalse($this->validator->isValid($data, new Collection(array( 'fields' => array( 'foo' => new Min(4), @@ -186,8 +182,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testMissingFieldsDisallowed() { $data = $this->prepareTestData(array()); - $this->context->setCurrentValue($data); - $this->context->setPropertyPath('bar'); $this->assertFalse($this->validator->isValid($data, new Collection(array( 'fields' => array( @@ -240,9 +234,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testOptionalFieldSingleConstraint() { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('bar'); - $array = array( 'foo' => 5, ); @@ -262,9 +253,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testOptionalFieldMultipleConstraints() { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('bar'); - $array = array( 'foo' => 5, ); @@ -309,9 +297,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testRequiredFieldSingleConstraint() { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('bar'); - $array = array( 'foo' => 5, ); @@ -331,9 +316,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase public function testRequiredFieldMultipleConstraints() { - $this->context->setGroup('MyGroup'); - $this->context->setPropertyPath('bar'); - $array = array( 'foo' => 5, ); diff --git a/tests/Symfony/Tests/Component/Validator/ExecutionContextTest.php b/tests/Symfony/Tests/Component/Validator/ExecutionContextTest.php index fa30e76fe6..84b4156023 100644 --- a/tests/Symfony/Tests/Component/Validator/ExecutionContextTest.php +++ b/tests/Symfony/Tests/Component/Validator/ExecutionContextTest.php @@ -11,6 +11,8 @@ namespace Symfony\Tests\Component\Validator; +use Symfony\Component\Validator\GlobalExecutionContext; + use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; @@ -21,34 +23,44 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase { protected $walker; protected $metadataFactory; + protected $globalContext; protected $context; protected function setUp() { $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); $this->metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); - $this->context = new ExecutionContext('Root', $this->walker, $this->metadataFactory); + $this->globalContext = new GlobalExecutionContext('Root', $this->walker, $this->metadataFactory); + $this->context = new ExecutionContext($this->globalContext, 'currentValue', 'foo.bar', 'Group', 'ClassName', 'propertyName'); } protected function tearDown() { - $this->walker = null; - $this->metadataFactory = null; + $this->globalContext = null; $this->context = null; } + public function testInit() + { + $this->assertCount(0, $this->context->getViolations()); + $this->assertSame('Root', $this->context->getRoot()); + $this->assertSame('foo.bar', $this->context->getPropertyPath()); + $this->assertSame('ClassName', $this->context->getCurrentClass()); + $this->assertSame('propertyName', $this->context->getCurrentProperty()); + $this->assertSame('Group', $this->context->getGroup()); + $this->assertSame($this->walker, $this->context->getGraphWalker()); + $this->assertSame($this->metadataFactory, $this->context->getMetadataFactory()); + } + public function testClone() { $clone = clone $this->context; - $this->assertNotSame($this->context, $clone); + $this->assertNotSame($this->context->getViolations(), $clone->getViolations()); } public function testAddViolation() { - $this->assertCount(0, $this->context->getViolations()); - - $this->context->setPropertyPath('foo.bar'); $this->context->addViolation('Error', array('foo' => 'bar'), 'invalid'); $this->assertEquals(new ConstraintViolationList(array( @@ -64,10 +76,6 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase public function testAddViolationUsesPreconfiguredValueIfNotPassed() { - $this->assertCount(0, $this->context->getViolations()); - - $this->context->setPropertyPath('foo.bar'); - $this->context->setCurrentValue('invalid'); $this->context->addViolation('Error'); $this->assertEquals(new ConstraintViolationList(array( @@ -76,18 +84,13 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase array(), 'Root', 'foo.bar', - 'invalid' + 'currentValue' ), )), $this->context->getViolations()); } public function testAddViolationUsesPassedNulValue() { - $this->assertCount(0, $this->context->getViolations()); - - $this->context->setPropertyPath('foo.bar'); - $this->context->setCurrentValue('invalid'); - // passed null value should override preconfigured value "invalid" $this->context->addViolation('Error', array('foo' => 'bar'), null); @@ -104,10 +107,6 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase public function testAddViolationAt() { - $this->assertCount(0, $this->context->getViolations()); - - $this->context->setPropertyPath('foo.bar'); - // override preconfigured property path $this->context->addViolationAt('bar.baz', 'Error', array('foo' => 'bar'), 'invalid'); @@ -124,10 +123,6 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase public function testAddViolationAtUsesPreconfiguredValueIfNotPassed() { - $this->assertCount(0, $this->context->getViolations()); - - $this->context->setPropertyPath('foo.bar'); - $this->context->setCurrentValue('invalid'); $this->context->addViolationAt('bar.baz', 'Error'); $this->assertEquals(new ConstraintViolationList(array( @@ -136,18 +131,13 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase array(), 'Root', 'bar.baz', - 'invalid' + 'currentValue' ), )), $this->context->getViolations()); } public function testAddViolationAtUsesPassedNulValue() { - $this->assertCount(0, $this->context->getViolations()); - - $this->context->setPropertyPath('foo.bar'); - $this->context->setCurrentValue('invalid'); - // passed null value should override preconfigured value "invalid" $this->context->addViolationAt('bar.baz', 'Error', array('foo' => 'bar'), null); @@ -162,114 +152,100 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase )), $this->context->getViolations()); } - public function testGetViolations() + public function testAddNestedViolationAt() { - $this->context->addViolation('', array(), ''); + // override preconfigured property path + $this->context->addNestedViolationAt('bam.baz', 'Error', array('foo' => 'bar'), 'invalid'); - $violations = $this->context->getViolations(); - - $this->assertCount(1, $violations); - $this->assertInstanceOf('Symfony\Component\Validator\ConstraintViolationList', $violations); - - $this->assertInstanceOf('ArrayIterator', $violations->getIterator()); - - $this->assertTrue(isset($violations[0])); - $this->assertFalse(isset($violations[1])); - - $violations[] = 'fake'; - $this->assertEquals('fake', $violations[1]); - $this->assertTrue(isset($violations[1])); - - unset($violations[1]); - $this->assertFalse(isset($violations[1])); - - $violations[0] = 'fake'; - $this->assertEquals('fake', $violations[0]); + $this->assertEquals(new ConstraintViolationList(array( + new ConstraintViolation( + 'Error', + array('foo' => 'bar'), + 'Root', + 'foo.bar.bam.baz', + 'invalid' + ), + )), $this->context->getViolations()); } - public function testViolationsMerge() + public function testAddNestedViolationAtWithIndexPath() { - $this->context->addViolation('Message 1', array(), ''); - $this->context->addViolation('Message 2', array(), ''); + // override preconfigured property path + $this->context->addNestedViolationAt('[bam]', 'Error', array('foo' => 'bar'), 'invalid'); - $violations1 = $this->context->getViolations(); - - $this->context->addViolation('', array(), ''); - - $violations2 = $this->context->getViolations(); - unset($violations2[1]); - - $violations1->addAll($violations2); - - $this->assertEmpty($violations1[2]->getMessage()); + $this->assertEquals(new ConstraintViolationList(array( + new ConstraintViolation( + 'Error', + array('foo' => 'bar'), + 'Root', + 'foo.bar[bam]', + 'invalid' + ), + )), $this->context->getViolations()); } - public function testViolationsAsString() + public function testAddNestedViolationAtWithEmptyPath() { - $this->context->addViolation('Message 1', array(), ''); - $this->context->addViolation('Message 2', array(), ''); + // override preconfigured property path + $this->context->addNestedViolationAt('', 'Error', array('foo' => 'bar'), 'invalid'); - $violations = $this->context->getViolations(); - - $expected = <<assertEquals($expected, $violations->__toString()); + $this->assertEquals(new ConstraintViolationList(array( + new ConstraintViolation( + 'Error', + array('foo' => 'bar'), + 'Root', + 'foo.bar', + 'invalid' + ), + )), $this->context->getViolations()); } - public function testGetRoot() + public function testAddNestedViolationAtWithEmptyCurrentPropertyPath() { - $this->assertEquals('Root', $this->context->getRoot()); + $this->context = new ExecutionContext($this->globalContext, 'currentValue', '', 'Group', 'ClassName', 'propertyName'); + + // override preconfigured property path + $this->context->addNestedViolationAt('bam.baz', 'Error', array('foo' => 'bar'), 'invalid'); + + $this->assertEquals(new ConstraintViolationList(array( + new ConstraintViolation( + 'Error', + array('foo' => 'bar'), + 'Root', + 'bam.baz', + 'invalid' + ), + )), $this->context->getViolations()); } - public function testSetGetPropertyPath() + public function testAddNestedViolationAtUsesPreconfiguredValueIfNotPassed() { - $this->context->setPropertyPath('property_path'); + $this->context->addNestedViolationAt('bam.baz', 'Error'); - $this->assertEquals('property_path', $this->context->getPropertyPath()); + $this->assertEquals(new ConstraintViolationList(array( + new ConstraintViolation( + 'Error', + array(), + 'Root', + 'foo.bar.bam.baz', + 'currentValue' + ), + )), $this->context->getViolations()); } - public function testSetGetCurrentClass() + public function testAddNestedViolationAtUsesPassedNulValue() { - $this->context->setCurrentClass('current_class'); + // passed null value should override preconfigured value "invalid" + $this->context->addNestedViolationAt('bam.baz', 'Error', array('foo' => 'bar'), null); - $this->assertEquals('current_class', $this->context->getCurrentClass()); - } - - public function testSetGetCurrentProperty() - { - $this->context->setCurrentProperty('current_property'); - - $this->assertEquals('current_property', $this->context->getCurrentProperty()); - } - - public function testSetGetGroup() - { - $this->context->setGroup('group'); - - $this->assertEquals('group', $this->context->getGroup()); - } - - public function testGetGraphWalker() - { - $this->assertSame($this->walker, $this->context->getGraphWalker()); - $this->assertInstanceOf( - 'Symfony\Component\Validator\GraphWalker', - $this->context->getGraphWalker() - ); - } - - public function testGetMetadataFactory() - { - $this->assertSame($this->metadataFactory, $this->context->getMetadataFactory()); - $this->assertInstanceOf( - 'Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface', - $this->context->getMetadataFactory() - ); + $this->assertEquals(new ConstraintViolationList(array( + new ConstraintViolation( + 'Error', + array('foo' => 'bar'), + 'Root', + 'foo.bar.bam.baz', + null + ), + )), $this->context->getViolations()); } } diff --git a/tests/Symfony/Tests/Component/Validator/Fixtures/ConstraintAValidator.php b/tests/Symfony/Tests/Component/Validator/Fixtures/ConstraintAValidator.php index e1c15d3b52..b052081700 100644 --- a/tests/Symfony/Tests/Component/Validator/Fixtures/ConstraintAValidator.php +++ b/tests/Symfony/Tests/Component/Validator/Fixtures/ConstraintAValidator.php @@ -4,9 +4,19 @@ namespace Symfony\Tests\Component\Validator\Fixtures; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\ExecutionContext; class ConstraintAValidator extends ConstraintValidator { + static public $passedContext; + + public function initialize(ExecutionContext $context) + { + parent::initialize($context); + + self::$passedContext = $context; + } + public function isValid($value, Constraint $constraint) { if ('VALID' != $value) { diff --git a/tests/Symfony/Tests/Component/Validator/GlobalExecutionContextTest.php b/tests/Symfony/Tests/Component/Validator/GlobalExecutionContextTest.php new file mode 100644 index 0000000000..0e2e410704 --- /dev/null +++ b/tests/Symfony/Tests/Component/Validator/GlobalExecutionContextTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Validator; + +use Symfony\Component\Validator\ConstraintViolation; + +use Symfony\Component\Validator\ConstraintViolationList; + +use Symfony\Component\Validator\GlobalExecutionContext; + +class GlobalExecutionContextTest extends \PHPUnit_Framework_TestCase +{ + protected $walker; + protected $metadataFactory; + protected $context; + + protected function setUp() + { + $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); + $this->metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); + $this->context = new GlobalExecutionContext('Root', $this->walker, $this->metadataFactory); + } + + protected function tearDown() + { + $this->walker = null; + $this->metadataFactory = null; + $this->context = null; + } + + public function testInit() + { + $this->assertCount(0, $this->context->getViolations()); + $this->assertSame('Root', $this->context->getRoot()); + $this->assertSame($this->walker, $this->context->getGraphWalker()); + $this->assertSame($this->metadataFactory, $this->context->getMetadataFactory()); + } + + public function testClone() + { + $clone = clone $this->context; + + $this->assertNotSame($this->context->getViolations(), $clone->getViolations()); + } + + public function testAddViolation() + { + $violation = new ConstraintViolation('Error', array(), 'Root', 'foo.bar', 'invalid'); + + $this->context->addViolation($violation); + + $this->assertEquals(new ConstraintViolationList(array($violation)), $this->context->getViolations()); + } +} diff --git a/tests/Symfony/Tests/Component/Validator/GraphWalkerTest.php b/tests/Symfony/Tests/Component/Validator/GraphWalkerTest.php index 30d2f3dda9..c89a4e7952 100644 --- a/tests/Symfony/Tests/Component/Validator/GraphWalkerTest.php +++ b/tests/Symfony/Tests/Component/Validator/GraphWalkerTest.php @@ -11,6 +11,8 @@ namespace Symfony\Tests\Component\Validator; +use Symfony\Tests\Component\Validator\Fixtures\ConstraintAValidator; + require_once __DIR__.'/Fixtures/Entity.php'; require_once __DIR__.'/Fixtures/Reference.php'; require_once __DIR__.'/Fixtures/ConstraintA.php'; @@ -54,15 +56,17 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase $this->metadata = null; } - public function testWalkObjectUpdatesContext() + public function testWalkObjectPassesCorrectClassAndProperty() { $this->metadata->addConstraint(new ConstraintA()); $entity = new Entity(); $this->walker->walkObject($this->metadata, $entity, 'Default', ''); - $this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $this->getContext()->getCurrentClass()); - $this->assertEquals($entity, $this->getContext()->getCurrentValue()); + $context = ConstraintAValidator::$passedContext; + + $this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $context->getCurrentClass()); + $this->assertNull($context->getCurrentProperty()); } public function testWalkObjectValidatesConstraints() @@ -214,15 +218,16 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase $this->assertEquals($violations, $this->walker->getViolations()); } - public function testWalkPropertyUpdatesContext() + public function testWalkPropertyPassesCorrectClassAndProperty() { $this->metadata->addPropertyConstraint('firstName', new ConstraintA()); $this->walker->walkPropertyValue($this->metadata, 'firstName', 'value', 'Default', ''); - $this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $this->getContext()->getCurrentClass()); - $this->assertEquals('firstName', $this->getContext()->getCurrentProperty()); - $this->assertEquals('value', $this->getContext()->getCurrentValue()); + $context = ConstraintAValidator::$passedContext; + + $this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $context->getCurrentClass()); + $this->assertEquals('firstName', $context->getCurrentProperty()); } public function testWalkPropertyValueValidatesConstraints() @@ -461,9 +466,9 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase $this->assertEquals('collection[foo][bar]', $violations[0]->getPropertyPath()); } - protected function getContext() + protected function getProperty($property) { - $p = new \ReflectionProperty($this->walker, 'context'); + $p = new \ReflectionProperty($this->walker, $property); $p->setAccessible(true); return $p->getValue($this->walker);