[Validator] Decoupled the new classes a bit

This commit is contained in:
Bernhard Schussek 2014-02-17 14:43:28 +01:00
parent a6ed4cae5d
commit a40189ccb7
9 changed files with 95 additions and 92 deletions

View File

@ -15,7 +15,6 @@ use Symfony\Component\Validator\ClassBasedInterface;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Group\GroupManagerInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Node\Node;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@ -39,11 +38,6 @@ class ExecutionContext implements ExecutionContextInterface
*/
private $nodeStack;
/**
* @var MetadataFactoryInterface
*/
private $metadataFactory;
/**
* @var ValidatorInterface
*/
@ -54,9 +48,8 @@ class ExecutionContext implements ExecutionContextInterface
*/
private $groupManager;
public function __construct(MetadataFactoryInterface $metadataFactory, ValidatorInterface $validator, GroupManagerInterface $groupManager)
public function __construct(ValidatorInterface $validator, GroupManagerInterface $groupManager)
{
$this->metadataFactory = $metadataFactory;
$this->validator = $validator;
$this->groupManager = $groupManager;
$this->violations = new ConstraintViolationList();
@ -105,11 +98,6 @@ class ExecutionContext implements ExecutionContextInterface
}
public function getMetadataFor($object)
{
}
public function getViolations()
{
return $this->violations;

View File

@ -88,8 +88,6 @@ interface ExecutionContextInterface
*/
public function getMetadata();
public function getMetadataFor($object);
/**
* Returns the validation group that is currently being validated.
*

View File

@ -12,7 +12,6 @@
namespace Symfony\Component\Validator\Context;
use Symfony\Component\Validator\Group\GroupManagerInterface;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Node\Node;
use Symfony\Component\Validator\NodeTraverser\AbstractVisitor;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@ -23,11 +22,6 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
*/
class ExecutionContextManager extends AbstractVisitor implements ExecutionContextManagerInterface
{
/**
* @var MetadataFactoryInterface
*/
private $metadataFactory;
/**
* @var GroupManagerInterface
*/
@ -48,9 +42,8 @@ class ExecutionContextManager extends AbstractVisitor implements ExecutionContex
*/
private $contextStack;
public function __construct(MetadataFactoryInterface $metadataFactory, GroupManagerInterface $groupManager)
public function __construct(GroupManagerInterface $groupManager)
{
$this->metadataFactory = $metadataFactory;
$this->groupManager = $groupManager;
$this->reset();
@ -67,7 +60,7 @@ class ExecutionContextManager extends AbstractVisitor implements ExecutionContex
$this->contextStack->push($this->currentContext);
}
$this->currentContext = new ExecutionContext($this->metadataFactory, $this->validator, $this->groupManager);
$this->currentContext = new ExecutionContext($this->validator, $this->groupManager);
return $this->currentContext;
}

View File

@ -12,8 +12,10 @@
namespace Symfony\Component\Validator\NodeTraverser;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Context\ExecutionContextManagerInterface;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Node\ClassNode;
use Symfony\Component\Validator\Node\Node;
use Symfony\Component\Validator\Node\PropertyNode;
/**
* @since %%NextVersion%%

View File

@ -25,8 +25,6 @@ interface NodeTraverserInterface
/**
* @param Node[] $nodes
*
* @return mixed
*/
public function traverse(array $nodes);
}

View File

@ -15,23 +15,27 @@ use Symfony\Component\Validator\Context\ExecutionContextManager;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Tests\AbstractValidatorTest;
use Symfony\Component\Validator\NodeTraverser\NodeTraverser;
use Symfony\Component\Validator\NodeTraverser\NodeVisitor\NodeValidator;
use Symfony\Component\Validator\DefaultTranslator;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\Validator\NodeValidator;
use Symfony\Component\Validator\Validator\Validator;
class TraversingValidatorTest extends AbstractValidatorTest
{
protected function createValidator(MetadataFactoryInterface $metadataFactory)
{
$validatorFactory = new ConstraintValidatorFactory();
$nodeTraverser = new NodeTraverser($metadataFactory);
$nodeValidator = new NodeValidator($validatorFactory, $nodeTraverser);
$contextManager = new ExecutionContextManager($metadataFactory, $nodeValidator, new DefaultTranslator());
$nodeValidator = new NodeValidator($nodeTraverser, new ConstraintValidatorFactory());
$contextManager = new ExecutionContextManager($nodeValidator, new DefaultTranslator());
$validator = new Validator($nodeTraverser, $metadataFactory, $contextManager);
// The context manager needs the validator for passing it to created
// contexts
$contextManager->initialize($validator);
$nodeValidator->setContextManager($contextManager);
// The node validator needs the context manager for passing the current
// context to the constraint validators
$nodeValidator->initialize($contextManager);
$nodeTraverser->addVisitor($contextManager);
$nodeTraverser->addVisitor($nodeValidator);

View File

@ -16,10 +16,10 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\ValueMetadata;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\NodeTraverser\ClassNode;
use Symfony\Component\Validator\Node\ClassNode;
use Symfony\Component\Validator\Node\PropertyNode;
use Symfony\Component\Validator\Node\ValueNode;
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
use Symfony\Component\Validator\NodeTraverser\PropertyNode;
use Symfony\Component\Validator\NodeTraverser\ValueNode;
/**
* @since %%NextVersion%%
@ -60,9 +60,14 @@ abstract class AbstractValidator implements ValidatorInterface
return new ContextualValidator($this->nodeTraverser, $this->metadataFactory, $context);
}
public function getMetadataFactory()
public function getMetadataFor($object)
{
return $this->metadataFactory;
return $this->metadataFactory->getMetadataFor($object);
}
public function hasMetadataFor($object)
{
return $this->metadataFactory->hasMetadataFor($object);
}
protected function traverseObject($object, $groups = null)

View File

@ -26,7 +26,7 @@ use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
*/
class NodeValidator extends AbstractVisitor implements GroupManagerInterface
{
private $validatedNodes = array();
private $validatedObjects = array();
/**
* @var ConstraintValidatorFactoryInterface
@ -45,25 +45,25 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface
private $currentGroup;
public function __construct(ConstraintValidatorFactoryInterface $validatorFactory, NodeTraverserInterface $nodeTraverser)
public function __construct(NodeTraverserInterface $nodeTraverser, ConstraintValidatorFactoryInterface $validatorFactory)
{
$this->validatorFactory = $validatorFactory;
$this->nodeTraverser = $nodeTraverser;
}
public function setContextManager(ExecutionContextManagerInterface $contextManager)
public function initialize(ExecutionContextManagerInterface $contextManager)
{
$this->contextManager = $contextManager;
}
public function afterTraversal(array $nodes)
{
$this->validatedNodes = array();
$this->validatedObjects = array();
}
public function enterNode(Node $node)
{
$cacheKey = $node instanceof ClassNode
$objectHash = $node instanceof ClassNode
? spl_object_hash($node->value)
: null;
@ -75,73 +75,38 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface
foreach ($node->groups as $group) {
// Validate object nodes only once per group
if (null !== $cacheKey) {
if (null !== $objectHash) {
// Use the object hash for group sequences
$groupKey = is_object($group) ? spl_object_hash($group) : $group;
$groupHash = is_object($group) ? spl_object_hash($group) : $group;
// Exit, if the object is already validated for the current group
if (isset($this->validatedNodes[$cacheKey][$groupKey])) {
if (isset($this->validatedObjects[$objectHash][$groupHash])) {
return false;
}
// Remember validating this object before starting and possibly
// traversing the object graph
$this->validatedNodes[$cacheKey][$groupKey] = true;
$this->validatedObjects[$objectHash][$groupHash] = true;
}
// Validate group sequence until a violation is generated
if ($group instanceof GroupSequence) {
// Rename for clarity
$groupSequence = $group;
if (!$group instanceof GroupSequence) {
$this->validateNodeForGroup($node, $group);
// Only evaluate group sequences at class, not at property level
if (!$node instanceof ClassNode) {
continue;
}
$context = $this->contextManager->getCurrentContext();
$violationCount = count($context->getViolations());
foreach ($groupSequence->groups as $groupInSequence) {
$this->nodeTraverser->traverse(array(new ClassNode(
$node->value,
$node->metadata,
$node->propertyPath,
array($groupInSequence),
array($groupSequence->cascadedGroup ?: $groupInSequence)
)));
// Abort sequence validation if a violation was generated
if (count($context->getViolations()) > $violationCount) {
break;
}
}
// Optimization: If the groups only contain the group sequence,
// we can skip the traversal for the properties of the object
if (1 === count($node->groups)) {
return false;
}
// We're done for the current loop execution.
continue;
}
// Validate normal group (non group sequences)
try {
$this->currentGroup = $group;
// Only traverse group sequences at class, not at property level
if (!$node instanceof ClassNode) {
continue;
}
foreach ($node->metadata->findConstraints($group) as $constraint) {
$validator = $this->validatorFactory->getInstance($constraint);
$validator->initialize($this->contextManager->getCurrentContext());
$validator->validate($node->value, $constraint);
}
$this->traverseGroupSequence($node, $group);
$this->currentGroup = null;
} catch (\Exception $e) {
$this->currentGroup = null;
throw $e;
// Optimization: If the groups only contain the group sequence,
// we can skip the traversal for the properties of the object
if (1 === count($node->groups)) {
return false;
}
}
@ -152,4 +117,50 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface
{
return $this->currentGroup;
}
private function traverseGroupSequence(ClassNode $node, GroupSequence $groupSequence)
{
$context = $this->contextManager->getCurrentContext();
$violationCount = count($context->getViolations());
foreach ($groupSequence->groups as $groupInSequence) {
$this->nodeTraverser->traverse(array(new ClassNode(
$node->value,
$node->metadata,
$node->propertyPath,
array($groupInSequence),
array($groupSequence->cascadedGroup ?: $groupInSequence)
)));
// Abort sequence validation if a violation was generated
if (count($context->getViolations()) > $violationCount) {
break;
}
}
}
/**
* @param Node $node
* @param $group
*
* @throws \Exception
*/
private function validateNodeForGroup(Node $node, $group)
{
try {
$this->currentGroup = $group;
foreach ($node->metadata->findConstraints($group) as $constraint) {
$validator = $this->validatorFactory->getInstance($constraint);
$validator->initialize($this->contextManager->getCurrentContext());
$validator->validate($node->value, $constraint);
}
$this->currentGroup = null;
} catch (\Exception $e) {
$this->currentGroup = null;
throw $e;
}
}
}

View File

@ -85,4 +85,8 @@ interface ValidatorInterface
* @return ContextualValidatorInterface
*/
public function inContext(ExecutionContextInterface $context);
public function getMetadataFor($object);
public function hasMetadataFor($object);
}