[Validator] Decoupled the new classes a bit
This commit is contained in:
parent
a6ed4cae5d
commit
a40189ccb7
|
@ -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;
|
||||
|
|
|
@ -88,8 +88,6 @@ interface ExecutionContextInterface
|
|||
*/
|
||||
public function getMetadata();
|
||||
|
||||
public function getMetadataFor($object);
|
||||
|
||||
/**
|
||||
* Returns the validation group that is currently being validated.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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%%
|
||||
|
|
|
@ -25,8 +25,6 @@ interface NodeTraverserInterface
|
|||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function traverse(array $nodes);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,4 +85,8 @@ interface ValidatorInterface
|
|||
* @return ContextualValidatorInterface
|
||||
*/
|
||||
public function inContext(ExecutionContextInterface $context);
|
||||
|
||||
public function getMetadataFor($object);
|
||||
|
||||
public function hasMetadataFor($object);
|
||||
}
|
||||
|
|
Reference in New Issue