[Validator] Made ExecutionContext immutable and introduced new class GlobalExecutionContext

A new ExecutionContext is now created everytime that GraphWalker::walkConstraint() is
launched. Because of this, a validator B launched from within a validator A can't break
A anymore by changing the context.

Because we have a new ExecutionContext for every constraint validation, there is no point
in modifying its state anymore. Because of this it is now immutable.
This commit is contained in:
Bernhard Schussek 2012-01-31 21:35:48 +01:00
parent fe85bbdb06
commit a30a679135
19 changed files with 558 additions and 387 deletions

View File

@ -299,6 +299,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
individual fields individual fields
* changed default value for `extraFieldsMessage` and `missingFieldsMessage` * changed default value for `extraFieldsMessage` and `missingFieldsMessage`
in Collection constraint in Collection constraint
* made ExecutionContext immutable
### Yaml ### Yaml

View File

@ -102,10 +102,7 @@ class UniqueEntityValidator extends ConstraintValidator
return true; return true;
} }
$oldPath = $this->context->getPropertyPath(); $this->context->addNestedViolationAt($fields[0], $constraint->message, array(), $criteria[$fields[0]]);
$this->context->setPropertyPath( empty($oldPath) ? $fields[0] : $oldPath.'.'.$fields[0]);
$this->context->addViolation($constraint->message, array(), $criteria[$fields[0]]);
$this->context->setPropertyPath($oldPath);
return true; // all true, we added the violation already! return true; // all true, we added the violation already!
} }

View File

@ -111,10 +111,6 @@ class DelegatingValidator implements FormValidatorInterface
$propertyPath = $context->getPropertyPath(); $propertyPath = $context->getPropertyPath();
$graphWalker = $context->getGraphWalker(); $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 // Adjust the property path accordingly
if (!empty($propertyPath)) { if (!empty($propertyPath)) {
$propertyPath .= '.'; $propertyPath .= '.';
@ -134,10 +130,6 @@ class DelegatingValidator implements FormValidatorInterface
$propertyPath = $context->getPropertyPath(); $propertyPath = $context->getPropertyPath();
$graphWalker = $context->getGraphWalker(); $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 // Adjust the property path accordingly
if (!empty($propertyPath)) { if (!empty($propertyPath)) {
$propertyPath .= '.'; $propertyPath .= '.';

View File

@ -78,13 +78,13 @@ EOF;
/** /**
* Merge an existing ConstraintViolationList into this list. * Merge an existing ConstraintViolationList into this list.
* *
* @param ConstraintViolationList $violations * @param ConstraintViolationList $otherList
* *
* @api * @api
*/ */
public function addAll(ConstraintViolationList $violations) public function addAll(ConstraintViolationList $otherList)
{ {
foreach ($violations->violations as $violation) { foreach ($otherList->violations as $violation) {
$this->violations[] = $violation; $this->violations[] = $violation;
} }
} }

View File

@ -48,13 +48,6 @@ class CallbackValidator extends ConstraintValidator
} }
$methods = $constraint->methods; $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) { foreach ($methods as $method) {
if (is_array($method) || $method instanceof \Closure) { 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])); 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 { } else {
if (!method_exists($object, $method)) { if (!method_exists($object, $method)) {
throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist', $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; return true;

View File

@ -68,7 +68,7 @@ class CollectionValidator extends ConstraintValidator
$walker->walkConstraint($constr, $value[$field], $group, $propertyPath.'['.$field.']'); $walker->walkConstraint($constr, $value[$field], $group, $propertyPath.'['.$field.']');
} }
} elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) { } elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) {
$this->context->addViolationAt($propertyPath.'['.$field.']', $constraint->missingFieldsMessage, array( $this->context->addNestedViolationAt('['.$field.']', $constraint->missingFieldsMessage, array(
'{{ field }}' => '"'.$field.'"' '{{ field }}' => '"'.$field.'"'
), null); ), null);
$valid = false; $valid = false;
@ -78,7 +78,7 @@ class CollectionValidator extends ConstraintValidator
if (!$constraint->allowExtraFields) { if (!$constraint->allowExtraFields) {
foreach ($value as $field => $fieldValue) { foreach ($value as $field => $fieldValue) {
if (!isset($constraint->fields[$field])) { if (!isset($constraint->fields[$field])) {
$this->context->addViolationAt($propertyPath.'['.$field.']', $constraint->extraFieldsMessage, array( $this->context->addNestedViolationAt('['.$field.']', $constraint->extraFieldsMessage, array(
'{{ field }}' => '"'.$field.'"' '{{ field }}' => '"'.$field.'"'
), $fieldValue); ), $fieldValue);
$valid = false; $valid = false;

View File

@ -16,8 +16,10 @@ use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
/** /**
* Stores the state of the current node in the validation graph. * Stores the state of the current node in the validation graph.
* *
* This object is used by the GraphWalker to initialize validation of different * This class is immutable by design.
* items and keep track of the violations. *
* It is used by the GraphWalker to initialize validation of different items
* and keep track of the violations.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
@ -26,60 +28,97 @@ use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
*/ */
class ExecutionContext class ExecutionContext
{ {
protected $root; private $globalContext;
protected $propertyPath; private $propertyPath;
protected $class; private $value;
protected $property; private $group;
protected $value; private $class;
protected $group; private $property;
protected $violations;
protected $graphWalker;
protected $metadataFactory;
public function __construct( public function __construct(GlobalExecutionContext $globalContext, $value, $propertyPath, $group, $class = null, $property = null)
$root,
GraphWalker $graphWalker,
ClassMetadataFactoryInterface $metadataFactory
)
{ {
$this->root = $root; $this->globalContext = $globalContext;
$this->graphWalker = $graphWalker; $this->value = $value;
$this->metadataFactory = $metadataFactory; $this->propertyPath = $propertyPath;
$this->violations = new ConstraintViolationList(); $this->group = $group;
$this->class = $class;
$this->property = $property;
} }
public function __clone() 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 * @api
*/ */
public function addViolation($message, array $params = array(), $invalidValue = null) public function addViolation($message, array $params = array(), $invalidValue = null)
{ {
$this->violations->add(new ConstraintViolation( $this->globalContext->addViolation(new ConstraintViolation(
$message, $message,
$params, $params,
$this->root, $this->globalContext->getRoot(),
$this->propertyPath, $this->propertyPath,
// check using func_num_args() to allow passing null values // check using func_num_args() to allow passing null values
func_num_args() === 3 ? $invalidValue : $this->value 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) public function addViolationAt($propertyPath, $message, array $params = array(), $invalidValue = null)
{ {
$this->violations->add(new ConstraintViolation( $this->globalContext->addViolation(new ConstraintViolation(
$message, $message,
$params, $params,
$this->root, $this->globalContext->getRoot(),
$propertyPath, $propertyPath,
// check using func_num_args() to allow passing null values // check using func_num_args() to allow passing null values
func_num_args() === 4 ? $invalidValue : $this->value 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 * @return ConstraintViolationList
* *
@ -87,17 +126,12 @@ class ExecutionContext
*/ */
public function getViolations() public function getViolations()
{ {
return $this->violations; return $this->globalContext->getViolations();
} }
public function getRoot() public function getRoot()
{ {
return $this->root; return $this->globalContext->getRoot();
}
public function setPropertyPath($propertyPath)
{
$this->propertyPath = $propertyPath;
} }
public function getPropertyPath() public function getPropertyPath()
@ -105,41 +139,21 @@ class ExecutionContext
return $this->propertyPath; return $this->propertyPath;
} }
public function setCurrentClass($class)
{
$this->class = $class;
}
public function getCurrentClass() public function getCurrentClass()
{ {
return $this->class; return $this->class;
} }
public function setCurrentProperty($property)
{
$this->property = $property;
}
public function getCurrentProperty() public function getCurrentProperty()
{ {
return $this->property; return $this->property;
} }
public function setCurrentValue($value)
{
$this->value = $value;
}
public function getCurrentValue() public function getCurrentValue()
{ {
return $this->value; return $this->value;
} }
public function setGroup($group)
{
$this->group = $group;
}
public function getGroup() public function getGroup()
{ {
return $this->group; return $this->group;
@ -150,7 +164,7 @@ class ExecutionContext
*/ */
public function getGraphWalker() public function getGraphWalker()
{ {
return $this->graphWalker; return $this->globalContext->getGraphWalker();
} }
/** /**
@ -158,6 +172,6 @@ class ExecutionContext
*/ */
public function getMetadataFactory() public function getMetadataFactory()
{ {
return $this->metadataFactory; return $this->globalContext->getMetadataFactory();
} }
} }

View File

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*/
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;
}
}

View File

@ -27,15 +27,15 @@ use Symfony\Component\Validator\Mapping\MemberMetadata;
*/ */
class GraphWalker class GraphWalker
{ {
protected $context; private $globalContext;
protected $validatorFactory; private $validatorFactory;
protected $metadataFactory; private $metadataFactory;
protected $validatorInitializers = array(); private $validatorInitializers = array();
protected $validatedObjects = array(); private $validatedObjects = array();
public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = 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->validatorFactory = $factory;
$this->metadataFactory = $metadataFactory; $this->metadataFactory = $metadataFactory;
$this->validatorInitializers = $validatorInitializers; $this->validatorInitializers = $validatorInitializers;
@ -46,7 +46,7 @@ class GraphWalker
*/ */
public function getViolations() public function getViolations()
{ {
return $this->context->getViolations(); return $this->globalContext->getViolations();
} }
/** /**
@ -67,8 +67,6 @@ class GraphWalker
$initializer->initialize($object); $initializer->initialize($object);
} }
$this->context->setCurrentClass($metadata->getClassName());
if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) { if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
$groups = $metadata->getGroupSequence(); $groups = $metadata->getGroupSequence();
foreach ($groups as $group) { foreach ($groups as $group) {
@ -100,8 +98,10 @@ class GraphWalker
// traversing the object graph // traversing the object graph
$this->validatedObjects[$hash][$group] = true; $this->validatedObjects[$hash][$group] = true;
$currentClass = $metadata->getClassName();
foreach ($metadata->findConstraints($group) as $constraint) { foreach ($metadata->findConstraints($group) as $constraint) {
$this->walkConstraint($constraint, $object, $group, $propertyPath); $this->walkConstraint($constraint, $object, $group, $propertyPath, $currentClass);
} }
if (null !== $object) { if (null !== $object) {
@ -129,11 +129,11 @@ class GraphWalker
protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null) protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null)
{ {
$this->context->setCurrentClass($metadata->getClassName()); $currentClass = $metadata->getClassName();
$this->context->setCurrentProperty($metadata->getPropertyName()); $currentProperty = $metadata->getPropertyName();
foreach ($metadata->findConstraints($group) as $constraint) { foreach ($metadata->findConstraints($group) as $constraint) {
$this->walkConstraint($constraint, $value, $group, $propertyPath); $this->walkConstraint($constraint, $value, $group, $propertyPath, $currentClass, $currentProperty);
} }
if ($metadata->isCascaded()) { 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); $validator = $this->validatorFactory->getInstance($constraint);
$this->context->setCurrentValue($value); $localContext = new ExecutionContext(
$this->context->setPropertyPath($propertyPath); $this->globalContext,
$this->context->setGroup($group); $value,
$propertyPath,
$group,
$currentClass,
$currentProperty
);
$validator->initialize($this->context); $validator->initialize($localContext);
if (!$validator->isValid($value, $constraint)) { if (!$validator->isValid($value, $constraint)) {
$messageTemplate = $validator->getMessageTemplate(); $messageTemplate = $validator->getMessageTemplate();
@ -180,15 +185,9 @@ class GraphWalker
// Somewhat ugly hack: Don't add a violation if no message is set. // Somewhat ugly hack: Don't add a violation if no message is set.
// This is required if the validator added its violations directly // This is required if the validator added its violations directly
// to the context already // to the globalContext already
if (!empty($messageTemplate)) { if (!empty($messageTemplate)) {
// Resetting the property path. This is needed because some $localContext->addViolation($messageTemplate, $messageParams);
// 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);
} }
} }
} }

View File

@ -11,6 +11,8 @@
namespace Symfony\Tests\Component\Form\Extension\Validator\Validator; namespace Symfony\Tests\Component\Form\Extension\Validator\Validator;
use Symfony\Component\Validator\GlobalExecutionContext;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormError; 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); 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) protected function getConstraintViolation($propertyPath)
{ {
return new ConstraintViolation($this->message, $this->params, null, $propertyPath, null); return new ConstraintViolation($this->message, $this->params, null, $propertyPath, null);
@ -589,9 +600,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormData() public function testValidateFormData()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext();
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$form = $this->getBuilder() $form = $this->getBuilder()
->setAttribute('validation_groups', array('group1', 'group2')) ->setAttribute('validation_groups', array('group1', 'group2'))
@ -611,9 +621,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormDataCanHandleCallbackValidationGroups() public function testValidateFormDataCanHandleCallbackValidationGroups()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext();
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$form = $this->getBuilder() $form = $this->getBuilder()
->setAttribute('validation_groups', array($this, 'getValidationGroups')) ->setAttribute('validation_groups', array($this, 'getValidationGroups'))
@ -633,9 +642,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormDataCanHandleClosureValidationGroups() public function testValidateFormDataCanHandleClosureValidationGroups()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext();
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$form = $this->getBuilder() $form = $this->getBuilder()
->setAttribute('validation_groups', function(FormInterface $form){ ->setAttribute('validation_groups', function(FormInterface $form){
@ -657,10 +665,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormDataUsesInheritedValidationGroup() public function testValidateFormDataUsesInheritedValidationGroup()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext('foo.bar');
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$context->setPropertyPath('path');
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$parent = $this->getBuilder() $parent = $this->getBuilder()
@ -675,17 +681,15 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
$graphWalker->expects($this->once()) $graphWalker->expects($this->once())
->method('walkReference') ->method('walkReference')
->with($object, 'group', 'path.data', true); ->with($object, 'group', 'foo.bar.data', true);
DelegatingValidator::validateFormData($child, $context); DelegatingValidator::validateFormData($child, $context);
} }
public function testValidateFormDataUsesInheritedCallbackValidationGroup() public function testValidateFormDataUsesInheritedCallbackValidationGroup()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext('foo.bar');
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$context->setPropertyPath('path');
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$parent = $this->getBuilder() $parent = $this->getBuilder()
@ -700,20 +704,18 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
$graphWalker->expects($this->at(0)) $graphWalker->expects($this->at(0))
->method('walkReference') ->method('walkReference')
->with($object, 'group1', 'path.data', true); ->with($object, 'group1', 'foo.bar.data', true);
$graphWalker->expects($this->at(1)) $graphWalker->expects($this->at(1))
->method('walkReference') ->method('walkReference')
->with($object, 'group2', 'path.data', true); ->with($object, 'group2', 'foo.bar.data', true);
DelegatingValidator::validateFormData($child, $context); DelegatingValidator::validateFormData($child, $context);
} }
public function testValidateFormDataUsesInheritedClosureValidationGroup() public function testValidateFormDataUsesInheritedClosureValidationGroup()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext('foo.bar');
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$context->setPropertyPath('path');
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$parent = $this->getBuilder() $parent = $this->getBuilder()
@ -730,46 +732,24 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
$graphWalker->expects($this->at(0)) $graphWalker->expects($this->at(0))
->method('walkReference') ->method('walkReference')
->with($object, 'group1', 'path.data', true); ->with($object, 'group1', 'foo.bar.data', true);
$graphWalker->expects($this->at(1)) $graphWalker->expects($this->at(1))
->method('walkReference') ->method('walkReference')
->with($object, 'group2', 'path.data', true); ->with($object, 'group2', 'foo.bar.data', true);
DelegatingValidator::validateFormData($child, $context); DelegatingValidator::validateFormData($child, $context);
} }
public function testValidateFormDataAppendsPropertyPath() public function testValidateFormDataAppendsPropertyPath()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext('foo.bar');
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$context->setPropertyPath('path');
$object = $this->getMock('\stdClass'); $object = $this->getMock('\stdClass');
$form = $this->getForm(); $form = $this->getForm();
$graphWalker->expects($this->once()) $graphWalker->expects($this->once())
->method('walkReference') ->method('walkReference')
->with($object, 'Default', 'path.data', true); ->with($object, 'Default', 'foo.bar.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());
}));
$form->setData($object); $form->setData($object);
@ -778,9 +758,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormDataDoesNotWalkScalars() public function testValidateFormDataDoesNotWalkScalars()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext();
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$clientTransformer = $this->getMockTransformer(); $clientTransformer = $this->getMockTransformer();
$form = $this->getBuilder() $form = $this->getBuilder()
@ -801,9 +780,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormChildren() public function testValidateFormChildren()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext();
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$form = $this->getBuilder() $form = $this->getBuilder()
->setAttribute('cascade_validation', true) ->setAttribute('cascade_validation', true)
->setAttribute('validation_groups', array('group1', 'group2')) ->setAttribute('validation_groups', array('group1', 'group2'))
@ -821,10 +799,8 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
public function testValidateFormChildrenAppendsPropertyPath() public function testValidateFormChildrenAppendsPropertyPath()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext('foo.bar');
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$context->setPropertyPath('path');
$form = $this->getBuilder() $form = $this->getBuilder()
->setAttribute('cascade_validation', true) ->setAttribute('cascade_validation', true)
->getForm(); ->getForm();
@ -832,36 +808,15 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
$graphWalker->expects($this->once()) $graphWalker->expects($this->once())
->method('walkReference') ->method('walkReference')
->with($form->getChildren(), 'Default', 'path.children', true); ->with($form->getChildren(), 'Default', 'foo.bar.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());
}));
DelegatingValidator::validateFormChildren($form, $context); DelegatingValidator::validateFormChildren($form, $context);
} }
public function testValidateFormChildrenDoesNothingIfDisabled() public function testValidateFormChildrenDoesNothingIfDisabled()
{ {
$graphWalker = $this->getMockGraphWalker(); $context = $this->getExecutionContext();
$metadataFactory = $this->getMockMetadataFactory(); $graphWalker = $context->getGraphWalker();
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
$form = $this->getBuilder() $form = $this->getBuilder()
->setAttribute('cascade_validation', false) ->setAttribute('cascade_validation', false)
->getForm(); ->getForm();

View File

@ -0,0 +1,134 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 = <<<EOF
Root:
Error 1
Root.foo.bar:
Error 2
Root[baz]:
Error 3
foo.bar:
Error 4
[baz]:
Error 5
EOF;
$this->assertEquals($expected, (string) $this->list);
}
protected function getViolation($message, $root = null, $propertyPath = null)
{
return new ConstraintViolation($message, array(), $root, $propertyPath, null);
}
}

View File

@ -11,6 +11,8 @@
namespace Symfony\Tests\Component\Validator\Constraints; namespace Symfony\Tests\Component\Validator\Constraints;
use Symfony\Component\Validator\GlobalExecutionContext;
use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\ExecutionContext;
use Symfony\Component\Validator\Constraints\Min; use Symfony\Component\Validator\Constraints\Min;
use Symfony\Component\Validator\Constraints\All; 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); $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false);
$metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); $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 = new AllValidator();
$this->validator->initialize($this->context); $this->validator->initialize($this->context);
@ -57,9 +60,6 @@ class AllValidatorTest extends \PHPUnit_Framework_TestCase
*/ */
public function testWalkSingleConstraint($array) public function testWalkSingleConstraint($array)
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('foo');
$constraint = new Min(4); $constraint = new Min(4);
foreach ($array as $key => $value) { foreach ($array as $key => $value) {
@ -76,9 +76,6 @@ class AllValidatorTest extends \PHPUnit_Framework_TestCase
*/ */
public function testWalkMultipleConstraints($array) public function testWalkMultipleConstraints($array)
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('foo');
$constraint = new Min(4); $constraint = new Min(4);
// can only test for twice the same constraint because PHPUnits mocking // can only test for twice the same constraint because PHPUnits mocking
// can't test method calls with different arguments // can't test method calls with different arguments

View File

@ -11,6 +11,7 @@
namespace Symfony\Tests\Component\Validator\Constraints; namespace Symfony\Tests\Component\Validator\Constraints;
use Symfony\Component\Validator\GlobalExecutionContext;
use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\ExecutionContext;
use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ConstraintViolationList;
@ -21,11 +22,6 @@ class CallbackValidatorTest_Class
{ {
public static function validateStatic($object, ExecutionContext $context) 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'); $context->addViolation('Static message', array('parameter'), 'invalidValue');
} }
} }
@ -34,11 +30,6 @@ class CallbackValidatorTest_Object
{ {
public function validateOne(ExecutionContext $context) 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'); $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); $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false);
$metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); $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, 'value', 'foo.bar', 'Group', 'ClassName', 'propertyName');
$this->context->setCurrentClass('InitialClass');
$this->context->setCurrentProperty('initialProperty');
$this->context->setGroup('InitialGroup');
$this->context->setPropertyPath('initial.property.path');
$this->validator = new CallbackValidator(); $this->validator = new CallbackValidator();
$this->validator->initialize($this->context); $this->validator->initialize($this->context);
} }
@ -97,10 +84,6 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase
)); ));
$this->assertEquals($violations, $this->context->getViolations()); $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() public function testCallbackSingleStaticMethod()
@ -121,10 +104,6 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase
)); ));
$this->assertEquals($violations, $this->context->getViolations()); $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() public function testCallbackMultipleMethods()
@ -143,13 +122,11 @@ class CallbackValidatorTest extends \PHPUnit_Framework_TestCase
'foo.bar', 'foo.bar',
'invalidValue' 'invalidValue'
)); ));
// context was reset
$violations->add(new ConstraintViolation( $violations->add(new ConstraintViolation(
'Other message', 'Other message',
array('other parameter'), array('other parameter'),
'Root', 'Root',
'initial.property.path', 'foo.bar',
'otherInvalidValue' 'otherInvalidValue'
)); ));

View File

@ -11,6 +11,8 @@
namespace Symfony\Tests\Component\Validator\Constraints; namespace Symfony\Tests\Component\Validator\Constraints;
use Symfony\Component\Validator\GlobalExecutionContext;
use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\ExecutionContext;
use Symfony\Component\Validator\Constraints\Choice; use Symfony\Component\Validator\Constraints\Choice;
use Symfony\Component\Validator\Constraints\ChoiceValidator; 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); $walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false);
$factory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); $factory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
$context = new ExecutionContext('root', $walker, $factory); $globalContext = new GlobalExecutionContext('root', $walker, $factory);
$context->setCurrentClass(__CLASS__); $context = new ExecutionContext($globalContext, null, null, null, __CLASS__, null);
$this->validator = new ChoiceValidator(); $this->validator = new ChoiceValidator();
$this->validator->initialize($context); $this->validator->initialize($context);
} }

View File

@ -11,6 +11,8 @@
namespace Symfony\Tests\Component\Validator\Constraints; namespace Symfony\Tests\Component\Validator\Constraints;
use Symfony\Component\Validator\GlobalExecutionContext;
use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\ExecutionContext; use Symfony\Component\Validator\ExecutionContext;
@ -25,6 +27,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
{ {
protected $validator; protected $validator;
protected $walker; protected $walker;
protected $globalContext;
protected $context; protected $context;
protected function setUp() 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); $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false);
$metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); $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 = new CollectionValidator();
$this->validator->initialize($this->context); $this->validator->initialize($this->context);
@ -42,6 +46,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
{ {
$this->validator = null; $this->validator = null;
$this->walker = null; $this->walker = null;
$this->globalContext = null;
$this->context = null; $this->context = null;
} }
@ -75,9 +80,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testWalkSingleConstraint() public function testWalkSingleConstraint()
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('foo');
$constraint = new Min(4); $constraint = new Min(4);
$array = array('foo' => 3); $array = array('foo' => 3);
@ -85,7 +87,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
foreach ($array as $key => $value) { foreach ($array as $key => $value) {
$this->walker->expects($this->once()) $this->walker->expects($this->once())
->method('walkConstraint') ->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); $data = $this->prepareTestData($array);
@ -99,9 +101,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testWalkMultipleConstraints() public function testWalkMultipleConstraints()
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('foo');
$constraints = array( $constraints = array(
new Min(4), new Min(4),
new NotNull(), new NotNull(),
@ -112,7 +111,7 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
foreach ($constraints as $i => $constraint) { foreach ($constraints as $i => $constraint) {
$this->walker->expects($this->at($i)) $this->walker->expects($this->at($i))
->method('walkConstraint') ->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, 'baz' => 6,
)); ));
$this->context->setCurrentValue($data);
$this->context->setPropertyPath('bar');
$this->assertFalse($this->validator->isValid($data, new Collection(array( $this->assertFalse($this->validator->isValid($data, new Collection(array(
'fields' => array( 'fields' => array(
'foo' => new Min(4), 'foo' => new Min(4),
@ -186,8 +182,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testMissingFieldsDisallowed() public function testMissingFieldsDisallowed()
{ {
$data = $this->prepareTestData(array()); $data = $this->prepareTestData(array());
$this->context->setCurrentValue($data);
$this->context->setPropertyPath('bar');
$this->assertFalse($this->validator->isValid($data, new Collection(array( $this->assertFalse($this->validator->isValid($data, new Collection(array(
'fields' => array( 'fields' => array(
@ -240,9 +234,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testOptionalFieldSingleConstraint() public function testOptionalFieldSingleConstraint()
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('bar');
$array = array( $array = array(
'foo' => 5, 'foo' => 5,
); );
@ -262,9 +253,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testOptionalFieldMultipleConstraints() public function testOptionalFieldMultipleConstraints()
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('bar');
$array = array( $array = array(
'foo' => 5, 'foo' => 5,
); );
@ -309,9 +297,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testRequiredFieldSingleConstraint() public function testRequiredFieldSingleConstraint()
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('bar');
$array = array( $array = array(
'foo' => 5, 'foo' => 5,
); );
@ -331,9 +316,6 @@ abstract class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
public function testRequiredFieldMultipleConstraints() public function testRequiredFieldMultipleConstraints()
{ {
$this->context->setGroup('MyGroup');
$this->context->setPropertyPath('bar');
$array = array( $array = array(
'foo' => 5, 'foo' => 5,
); );

View File

@ -11,6 +11,8 @@
namespace Symfony\Tests\Component\Validator; namespace Symfony\Tests\Component\Validator;
use Symfony\Component\Validator\GlobalExecutionContext;
use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ConstraintViolationList;
@ -21,34 +23,44 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
{ {
protected $walker; protected $walker;
protected $metadataFactory; protected $metadataFactory;
protected $globalContext;
protected $context; protected $context;
protected function setUp() protected function setUp()
{ {
$this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false); $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false);
$this->metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface'); $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() protected function tearDown()
{ {
$this->walker = null; $this->globalContext = null;
$this->metadataFactory = null;
$this->context = 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() public function testClone()
{ {
$clone = clone $this->context; $clone = clone $this->context;
$this->assertNotSame($this->context, $clone); $this->assertNotSame($this->context->getViolations(), $clone->getViolations());
} }
public function testAddViolation() public function testAddViolation()
{ {
$this->assertCount(0, $this->context->getViolations());
$this->context->setPropertyPath('foo.bar');
$this->context->addViolation('Error', array('foo' => 'bar'), 'invalid'); $this->context->addViolation('Error', array('foo' => 'bar'), 'invalid');
$this->assertEquals(new ConstraintViolationList(array( $this->assertEquals(new ConstraintViolationList(array(
@ -64,10 +76,6 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
public function testAddViolationUsesPreconfiguredValueIfNotPassed() public function testAddViolationUsesPreconfiguredValueIfNotPassed()
{ {
$this->assertCount(0, $this->context->getViolations());
$this->context->setPropertyPath('foo.bar');
$this->context->setCurrentValue('invalid');
$this->context->addViolation('Error'); $this->context->addViolation('Error');
$this->assertEquals(new ConstraintViolationList(array( $this->assertEquals(new ConstraintViolationList(array(
@ -76,18 +84,13 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
array(), array(),
'Root', 'Root',
'foo.bar', 'foo.bar',
'invalid' 'currentValue'
), ),
)), $this->context->getViolations()); )), $this->context->getViolations());
} }
public function testAddViolationUsesPassedNulValue() 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" // passed null value should override preconfigured value "invalid"
$this->context->addViolation('Error', array('foo' => 'bar'), null); $this->context->addViolation('Error', array('foo' => 'bar'), null);
@ -104,10 +107,6 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
public function testAddViolationAt() public function testAddViolationAt()
{ {
$this->assertCount(0, $this->context->getViolations());
$this->context->setPropertyPath('foo.bar');
// override preconfigured property path // override preconfigured property path
$this->context->addViolationAt('bar.baz', 'Error', array('foo' => 'bar'), 'invalid'); $this->context->addViolationAt('bar.baz', 'Error', array('foo' => 'bar'), 'invalid');
@ -124,10 +123,6 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
public function testAddViolationAtUsesPreconfiguredValueIfNotPassed() 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->context->addViolationAt('bar.baz', 'Error');
$this->assertEquals(new ConstraintViolationList(array( $this->assertEquals(new ConstraintViolationList(array(
@ -136,18 +131,13 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
array(), array(),
'Root', 'Root',
'bar.baz', 'bar.baz',
'invalid' 'currentValue'
), ),
)), $this->context->getViolations()); )), $this->context->getViolations());
} }
public function testAddViolationAtUsesPassedNulValue() 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" // passed null value should override preconfigured value "invalid"
$this->context->addViolationAt('bar.baz', 'Error', array('foo' => 'bar'), null); $this->context->addViolationAt('bar.baz', 'Error', array('foo' => 'bar'), null);
@ -162,114 +152,100 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase
)), $this->context->getViolations()); )), $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->assertEquals(new ConstraintViolationList(array(
new ConstraintViolation(
$this->assertCount(1, $violations); 'Error',
$this->assertInstanceOf('Symfony\Component\Validator\ConstraintViolationList', $violations); array('foo' => 'bar'),
'Root',
$this->assertInstanceOf('ArrayIterator', $violations->getIterator()); 'foo.bar.bam.baz',
'invalid'
$this->assertTrue(isset($violations[0])); ),
$this->assertFalse(isset($violations[1])); )), $this->context->getViolations());
$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]);
} }
public function testViolationsMerge() public function testAddNestedViolationAtWithIndexPath()
{ {
$this->context->addViolation('Message 1', array(), ''); // override preconfigured property path
$this->context->addViolation('Message 2', array(), ''); $this->context->addNestedViolationAt('[bam]', 'Error', array('foo' => 'bar'), 'invalid');
$violations1 = $this->context->getViolations(); $this->assertEquals(new ConstraintViolationList(array(
new ConstraintViolation(
$this->context->addViolation('', array(), ''); 'Error',
array('foo' => 'bar'),
$violations2 = $this->context->getViolations(); 'Root',
unset($violations2[1]); 'foo.bar[bam]',
'invalid'
$violations1->addAll($violations2); ),
)), $this->context->getViolations());
$this->assertEmpty($violations1[2]->getMessage());
} }
public function testViolationsAsString() public function testAddNestedViolationAtWithEmptyPath()
{ {
$this->context->addViolation('Message 1', array(), ''); // override preconfigured property path
$this->context->addViolation('Message 2', array(), ''); $this->context->addNestedViolationAt('', 'Error', array('foo' => 'bar'), 'invalid');
$violations = $this->context->getViolations(); $this->assertEquals(new ConstraintViolationList(array(
new ConstraintViolation(
$expected = <<<EOF 'Error',
Root: array('foo' => 'bar'),
Message 1 'Root',
Root: 'foo.bar',
Message 2 'invalid'
),
EOF; )), $this->context->getViolations());
$this->assertEquals($expected, $violations->__toString());
} }
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()); $this->assertEquals(new ConstraintViolationList(array(
} new ConstraintViolation(
'Error',
public function testSetGetCurrentProperty() array('foo' => 'bar'),
{ 'Root',
$this->context->setCurrentProperty('current_property'); 'foo.bar.bam.baz',
null
$this->assertEquals('current_property', $this->context->getCurrentProperty()); ),
} )), $this->context->getViolations());
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()
);
} }
} }

View File

@ -4,9 +4,19 @@ namespace Symfony\Tests\Component\Validator\Fixtures;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\ExecutionContext;
class ConstraintAValidator extends ConstraintValidator class ConstraintAValidator extends ConstraintValidator
{ {
static public $passedContext;
public function initialize(ExecutionContext $context)
{
parent::initialize($context);
self::$passedContext = $context;
}
public function isValid($value, Constraint $constraint) public function isValid($value, Constraint $constraint)
{ {
if ('VALID' != $value) { if ('VALID' != $value) {

View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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());
}
}

View File

@ -11,6 +11,8 @@
namespace Symfony\Tests\Component\Validator; namespace Symfony\Tests\Component\Validator;
use Symfony\Tests\Component\Validator\Fixtures\ConstraintAValidator;
require_once __DIR__.'/Fixtures/Entity.php'; require_once __DIR__.'/Fixtures/Entity.php';
require_once __DIR__.'/Fixtures/Reference.php'; require_once __DIR__.'/Fixtures/Reference.php';
require_once __DIR__.'/Fixtures/ConstraintA.php'; require_once __DIR__.'/Fixtures/ConstraintA.php';
@ -54,15 +56,17 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
$this->metadata = null; $this->metadata = null;
} }
public function testWalkObjectUpdatesContext() public function testWalkObjectPassesCorrectClassAndProperty()
{ {
$this->metadata->addConstraint(new ConstraintA()); $this->metadata->addConstraint(new ConstraintA());
$entity = new Entity(); $entity = new Entity();
$this->walker->walkObject($this->metadata, $entity, 'Default', ''); $this->walker->walkObject($this->metadata, $entity, 'Default', '');
$this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $this->getContext()->getCurrentClass()); $context = ConstraintAValidator::$passedContext;
$this->assertEquals($entity, $this->getContext()->getCurrentValue());
$this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $context->getCurrentClass());
$this->assertNull($context->getCurrentProperty());
} }
public function testWalkObjectValidatesConstraints() public function testWalkObjectValidatesConstraints()
@ -214,15 +218,16 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($violations, $this->walker->getViolations()); $this->assertEquals($violations, $this->walker->getViolations());
} }
public function testWalkPropertyUpdatesContext() public function testWalkPropertyPassesCorrectClassAndProperty()
{ {
$this->metadata->addPropertyConstraint('firstName', new ConstraintA()); $this->metadata->addPropertyConstraint('firstName', new ConstraintA());
$this->walker->walkPropertyValue($this->metadata, 'firstName', 'value', 'Default', ''); $this->walker->walkPropertyValue($this->metadata, 'firstName', 'value', 'Default', '');
$this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $this->getContext()->getCurrentClass()); $context = ConstraintAValidator::$passedContext;
$this->assertEquals('firstName', $this->getContext()->getCurrentProperty());
$this->assertEquals('value', $this->getContext()->getCurrentValue()); $this->assertEquals('Symfony\Tests\Component\Validator\Fixtures\Entity', $context->getCurrentClass());
$this->assertEquals('firstName', $context->getCurrentProperty());
} }
public function testWalkPropertyValueValidatesConstraints() public function testWalkPropertyValueValidatesConstraints()
@ -461,9 +466,9 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('collection[foo][bar]', $violations[0]->getPropertyPath()); $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); $p->setAccessible(true);
return $p->getValue($this->walker); return $p->getValue($this->walker);