From a40189ccb7c90c8919effa68261488103648d4aa Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Mon, 17 Feb 2014 14:43:28 +0100 Subject: [PATCH] [Validator] Decoupled the new classes a bit --- .../Validator/Context/ExecutionContext.php | 14 +- .../Context/ExecutionContextInterface.php | 2 - .../Context/ExecutionContextManager.php | 11 +- .../Validator/NodeTraverser/NodeTraverser.php | 4 +- .../NodeTraverser/NodeTraverserInterface.php | 2 - .../Validator/TraversingValidatorTest.php | 14 +- .../Validator/Validator/AbstractValidator.php | 15 ++- .../Validator/Validator/NodeValidator.php | 121 ++++++++++-------- .../Validator/ValidatorInterface.php | 4 + 9 files changed, 95 insertions(+), 92 deletions(-) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 09dc2644e2..ba4b0cb801 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -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; diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index c7ee62dc23..4a8765f27b 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -88,8 +88,6 @@ interface ExecutionContextInterface */ public function getMetadata(); - public function getMetadataFor($object); - /** * Returns the validation group that is currently being validated. * diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextManager.php b/src/Symfony/Component/Validator/Context/ExecutionContextManager.php index 08442b94e8..c6b3da0601 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextManager.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextManager.php @@ -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; } diff --git a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php index 3d3c7bfe03..b1962c263a 100644 --- a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php +++ b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php @@ -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%% diff --git a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php index 048a1458b4..501c0ee7d1 100644 --- a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php +++ b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php @@ -25,8 +25,6 @@ interface NodeTraverserInterface /** * @param Node[] $nodes - * - * @return mixed */ public function traverse(array $nodes); } diff --git a/src/Symfony/Component/Validator/Tests/Validator/TraversingValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/TraversingValidatorTest.php index 4f3a212aa1..2dc35387c7 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/TraversingValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/TraversingValidatorTest.php @@ -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); diff --git a/src/Symfony/Component/Validator/Validator/AbstractValidator.php b/src/Symfony/Component/Validator/Validator/AbstractValidator.php index c0d9f54b52..5401fc6dbe 100644 --- a/src/Symfony/Component/Validator/Validator/AbstractValidator.php +++ b/src/Symfony/Component/Validator/Validator/AbstractValidator.php @@ -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) diff --git a/src/Symfony/Component/Validator/Validator/NodeValidator.php b/src/Symfony/Component/Validator/Validator/NodeValidator.php index 20177341a8..7de32e88b1 100644 --- a/src/Symfony/Component/Validator/Validator/NodeValidator.php +++ b/src/Symfony/Component/Validator/Validator/NodeValidator.php @@ -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; + } + } } diff --git a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php index 4f557710c4..f02ed79d9b 100644 --- a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php +++ b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php @@ -85,4 +85,8 @@ interface ValidatorInterface * @return ContextualValidatorInterface */ public function inContext(ExecutionContextInterface $context); + + public function getMetadataFor($object); + + public function hasMetadataFor($object); }