[Validator] Completed inline documentation of the Node classes and the NodeTraverser

This commit is contained in:
Bernhard Schussek 2014-02-21 13:47:18 +01:00
parent dbce5a2f6a
commit 822fe4712f
10 changed files with 382 additions and 130 deletions

View File

@ -18,6 +18,13 @@ use Symfony\Component\Validator\Mapping\TraversalStrategy;
/**
* Represents an object and its class metadata in the validation graph.
*
* If the object is a collection which should be traversed, a new
* {@link CollectionNode} instance will be created for that object:
*
* (TagList:ClassNode)
* \
* (TagList:CollectionNode)
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>
*/
@ -31,19 +38,22 @@ class ClassNode extends Node
/**
* Creates a new class node.
*
* @param object $object The validated object
* @param ClassMetadataInterface $metadata The class metadata of that
* object
* @param string $propertyPath The property path leading
* to this node
* @param string[] $groups The groups in which this
* node should be validated
* @param string[]|null $cascadedGroups The groups in which
* cascaded objects should be
* validated
* @param integer $traversalStrategy
* @param object $object The validated object
* @param ClassMetadataInterface $metadata The class metadata of
* that object
* @param string $propertyPath The property path leading
* to this node
* @param string[] $groups The groups in which this
* node should be validated
* @param string[]|null $cascadedGroups The groups in which
* cascaded objects should
* be validated
* @param integer $traversalStrategy The strategy used for
* traversing the object
*
* @throws \Symfony\Component\Validator\Exception\UnexpectedTypeException
* @throws UnexpectedTypeException If $object is not an object
*
* @see \Symfony\Component\Validator\Mapping\TraversalStrategy
*/
public function __construct($object, ClassMetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null, $traversalStrategy = TraversalStrategy::IMPLICIT)
{

View File

@ -15,7 +15,7 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
/**
* Represents an traversable collection in the validation graph.
* Represents a traversable value in the validation graph.
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>
@ -33,13 +33,19 @@ class CollectionNode extends Node
* @param string[]|null $cascadedGroups The groups in which
* cascaded objects should be
* validated
* @param integer $traversalStrategy The traversal strategy
* @param integer $traversalStrategy The strategy used for
* traversing the collection
*
* @throws \Symfony\Component\Validator\Exception\ConstraintDefinitionException
* @throws ConstraintDefinitionException If $collection is not an array or a
* \Traversable
*
* @see \Symfony\Component\Validator\Mapping\TraversalStrategy
*/
public function __construct($collection, $propertyPath, array $groups, $cascadedGroups = null, $traversalStrategy = TraversalStrategy::TRAVERSE)
{
if (!is_array($collection) && !$collection instanceof \Traversable) {
// Must throw a ConstraintDefinitionException for backwards
// compatibility reasons with Symfony < 2.5
throw new ConstraintDefinitionException(sprintf(
'Traversal was enabled for "%s", but this class '.
'does not implement "\Traversable".',

View File

@ -16,8 +16,7 @@ namespace Symfony\Component\Validator\Node;
* attached to it.
*
* Together with {@link \Symfony\Component\Validator\Mapping\GenericMetadata},
* this node type can be used to validate a value against some given
* constraints.
* this node type can be used to validate a value against some constraints.
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>

View File

@ -16,7 +16,7 @@ use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
/**
* A node in the validated graph.
* A node in the validation graph.
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>
@ -59,7 +59,11 @@ abstract class Node
public $cascadedGroups;
/**
* The strategy used for traversing the validated value.
*
* @var integer
*
* @see \Symfony\Component\Validator\Mapping\TraversalStrategy
*/
public $traversalStrategy;

View File

@ -19,16 +19,23 @@ use Symfony\Component\Validator\Mapping\TraversalStrategy;
* Represents the value of a property and its associated metadata.
*
* If the property contains an object and should be cascaded, a new
* {@link ClassNode} instance will be created for that object.
*
* Example:
* {@link ClassNode} instance will be created for that object:
*
* (Article:ClassNode)
* \
* (author:PropertyNode)
* (->author:PropertyNode)
* \
* (Author:ClassNode)
*
* If the property contains a collection which should be traversed, a new
* {@link CollectionNode} instance will be created for that collection:
*
* (Article:ClassNode)
* \
* (->tags:PropertyNode)
* \
* (array:CollectionNode)
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>
*/
@ -61,6 +68,8 @@ class PropertyNode extends Node
* @param integer $traversalStrategy
*
* @throws UnexpectedTypeException If $object is not an object
*
* @see \Symfony\Component\Validator\Mapping\TraversalStrategy
*/
public function __construct($object, $value, PropertyMetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null, $traversalStrategy = TraversalStrategy::IMPLICIT)
{

View File

@ -12,17 +12,85 @@
namespace Symfony\Component\Validator\NodeTraverser;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Node\Node;
use Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface;
/**
* @since %%NextVersion%%
* Traverses the nodes of the validation graph.
*
* You can attach visitors to the traverser that are invoked during the
* traversal. Before starting the traversal, the
* {@link \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface::beforeTraversal()}
* method of each visitor is called. For each node in the graph, the
* {@link \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface::visit()}
* of each visitor is called. At the end of the traversal, the traverser invokes
* {@link \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface::afterTraversal()}
* on each visitor.
*
* The visitors should be called in the same order in which they are added to
* the traverser.
*
* The validation graph typically contains nodes of the following types:
*
* - {@link \Symfony\Component\Validator\Node\ClassNode}:
* An object with associated class metadata
* - {@link \Symfony\Component\Validator\Node\PropertyNode}:
* A property value with associated property metadata
* - {@link \Symfony\Component\Validator\Node\GenericNode}:
* A generic value with associated constraints
* - {@link \Symfony\Component\Validator\Node\CollectionNode}:
* A traversable collection
*
* Generic nodes are mostly useful when you want to validate a value that has
* neither associated class nor property metadata. Generic nodes usually come
* with {@link \Symfony\Component\Validator\Mapping\GenericMetadata}, that
* contains the constraints that the value should be validated against.
*
* Whenever a class, property or generic node is validated that contains a
* traversable value which should be traversed (according to the
* {@link \Symfony\Component\Validator\Mapping\TraversalStrategy} specified
* in the node or its metadata), a new
* {@link \Symfony\Component\Validator\Node\CollectionNode} will be attached
* to the node graph.
*
* For example:
*
* (TagList:ClassNode)
* \
* (TagList:CollectionNode)
*
* When writing custom visitors, be aware that collection nodes usually contain
* values that have already been passed to the visitor before through a class
* node, a property node or a generic node.
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface NodeTraverserInterface
{
/**
* Adds a new visitor to the traverser.
*
* Visitors that have already been added before are ignored.
*
* @param NodeVisitorInterface $visitor The visitor to add
*/
public function addVisitor(NodeVisitorInterface $visitor);
/**
* Removes a visitor from the traverser.
*
* Non-existing visitors are ignored.
*
* @param NodeVisitorInterface $visitor The visitor to remove
*/
public function removeVisitor(NodeVisitorInterface $visitor);
/**
* Traverses the given nodes in the given context.
*
* @param Node[] $nodes The nodes to traverse
* @param ExecutionContextInterface $context The validation context
*/
public function traverse(array $nodes, ExecutionContextInterface $context);
}

View File

@ -24,10 +24,37 @@ use Symfony\Component\Validator\Node\PropertyNode;
use Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface;
/**
* @since %%NextVersion%%
* Non-recursive implementation of {@link NodeTraverserInterface}.
*
* This implementation uses a Depth First algorithm to traverse the node
* graph. Instead of loading the complete node graph into memory before the
* traversal, the traverser only expands the successor nodes of a node once
* that node is traversed. For example, when traversing a class node, the
* nodes for all constrained properties of that class are loaded into memory.
* When the traversal of the class node is over, the node is discarded.
*
* Next, one of the class' property nodes is traversed. At that point, the
* successor nodes of that property node (a class node, if the property should
* be cascaded, or a collection node, if the property should be traversed) are
* loaded into memory. As soon as the traversal of the property node is over,
* it is discarded as well.
*
* This leads to an average memory consumption of O(log N * B), where N is the
* number of nodes in the graph and B is the average number of successor nodes
* of a node.
*
* In order to maintain a small execution stack, nodes are not validated
* recursively, but iteratively. Internally, a stack is used to store all the
* nodes that should be processed. Whenever a node is traversed, its successor
* nodes are put on the stack. The traverser keeps fetching and traversing nodes
* from the stack until the stack is empty and all nodes have been traversed.
*
* @since 2.5
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see NodeTraverserInterface
*/
class NodeTraverser implements NodeTraverserInterface
class NonRecursiveNodeTraverser implements NodeTraverserInterface
{
/**
* @var NodeVisitorInterface[]
@ -39,20 +66,34 @@ class NodeTraverser implements NodeTraverserInterface
*/
private $metadataFactory;
/**
* @var Boolean
*/
private $traversalStarted = false;
/**
* Creates a new traverser.
*
* @param MetadataFactoryInterface $metadataFactory The metadata factory
*/
public function __construct(MetadataFactoryInterface $metadataFactory)
{
$this->visitors = new \SplObjectStorage();
$this->nodeQueue = new \SplQueue();
$this->nodeStack = new \SplStack();
$this->metadataFactory = $metadataFactory;
}
/**
* {@inheritdoc}
*/
public function addVisitor(NodeVisitorInterface $visitor)
{
$this->visitors->attach($visitor);
}
/**
* {@inheritdoc}
*/
public function removeVisitor(NodeVisitorInterface $visitor)
{
$this->visitors->detach($visitor);
@ -63,45 +104,67 @@ class NodeTraverser implements NodeTraverserInterface
*/
public function traverse(array $nodes, ExecutionContextInterface $context)
{
// beforeTraversal() and afterTraversal() are only executed for the
// top-level call of traverse()
$isTopLevelCall = !$this->traversalStarted;
if ($isTopLevelCall) {
// Remember that the traversal was already started for the case of
// recursive calls to traverse()
$this->traversalStarted = true;
foreach ($this->visitors as $visitor) {
/** @var NodeVisitorInterface $visitor */
$visitor->beforeTraversal($nodes, $context);
}
}
$nodeQueue = new \SplQueue();
// This stack contains all the nodes that should be traversed
// A stack is used rather than a queue in order to traverse the graph
// in a Depth First approach (the last added node is processed first).
// In this way, the order in which the nodes are passed to the visitors
// is similar to a recursive implementation (except that the successor
// nodes of a node are traversed right-to-left instead of left-to-right).
$nodeStack = new \SplStack();
foreach ($nodes as $node) {
$nodeQueue->enqueue($node);
// Push a node to the stack and immediately process it. This way,
// the successor nodes are traversed before the next node in $nodes
$nodeStack->push($node);
while (!$nodeQueue->isEmpty()) {
$node = $nodeQueue->dequeue();
// Fetch nodes from the stack and traverse them until no more nodes
// are left. Then continue with the next node in $nodes.
while (!$nodeStack->isEmpty()) {
$node = $nodeStack->pop();
if ($node instanceof ClassNode) {
$this->traverseClassNode($node, $nodeQueue, $context);
$this->traverseClassNode($node, $context, $nodeStack);
} elseif ($node instanceof CollectionNode) {
$this->traverseCollectionNode($node, $nodeQueue, $context);
$this->traverseCollectionNode($node, $context, $nodeStack);
} else {
$this->traverseNode($node, $nodeQueue, $context);
$this->traverseNode($node, $context, $nodeStack);
}
}
}
if ($isTopLevelCall) {
foreach ($this->visitors as $visitor) {
/** @var NodeVisitorInterface $visitor */
$visitor->afterTraversal($nodes, $context);
}
// Put the traverser back into its initial state
$this->traversalStarted = false;
}
}
/**
* Executes the {@link NodeVisitorInterface::visit()} method of each
* visitor.
*
* @param Node $node The visited node
* @param ExecutionContextInterface $context The current execution context
*
* @return Boolean Whether to traverse the node's successor nodes
*/
private function visit(Node $node, ExecutionContextInterface $context)
{
foreach ($this->visitors as $visitor) {
@ -113,87 +176,27 @@ class NodeTraverser implements NodeTraverserInterface
return true;
}
private function traverseNode(Node $node, \SplQueue $nodeQueue, ExecutionContextInterface $context)
{
// Visitors have two possibilities to influence the traversal:
//
// 1. If a visitor's visit() method returns false, the traversal is
// skipped entirely.
// 2. If a visitor's visit() method removes a group from the node,
// that group will be skipped in the subtree of that node.
if (false === $this->visit($node, $context)) {
return;
}
if (null === $node->value) {
return;
}
// The "cascadedGroups" property is set by the NodeValidationVisitor when
// traversing group sequences
$cascadedGroups = null !== $node->cascadedGroups
? $node->cascadedGroups
: $node->groups;
if (0 === count($cascadedGroups)) {
return;
}
$cascadingStrategy = $node->metadata->getCascadingStrategy();
$traversalStrategy = $node->metadata->getTraversalStrategy();
if (is_array($node->value)) {
// Arrays are always traversed, independent of the specified
// traversal strategy
// (BC with Symfony < 2.5)
$nodeQueue->enqueue(new CollectionNode(
$node->value,
$node->propertyPath,
$cascadedGroups,
null,
$traversalStrategy
));
return;
}
if ($cascadingStrategy & CascadingStrategy::CASCADE) {
// If the value is a scalar, pass it anyway, because we want
// a NoSuchMetadataException to be thrown in that case
// (BC with Symfony < 2.5)
$this->cascadeObject(
$node->value,
$node->propertyPath,
$cascadedGroups,
$traversalStrategy,
$nodeQueue
);
return;
}
// Traverse only if IMPLICIT or TRAVERSE
if (!($traversalStrategy & (TraversalStrategy::IMPLICIT | TraversalStrategy::TRAVERSE))) {
return;
}
// If IMPLICIT, stop unless we deal with a Traversable
if ($traversalStrategy & TraversalStrategy::IMPLICIT && !$node->value instanceof \Traversable) {
return;
}
// If TRAVERSE, the constructor will fail if we have no Traversable
$nodeQueue->enqueue(new CollectionNode(
$node->value,
$node->propertyPath,
$cascadedGroups,
null,
$traversalStrategy
));
}
private function traverseClassNode(ClassNode $node, \SplQueue $nodeQueue, ExecutionContextInterface $context)
/**
* Traverses a class node.
*
* At first, each visitor is invoked for this node. Then, unless any
* of the visitors aborts the traversal by returning false, a property
* node is put on the node stack for each constrained property of the class.
* At last, if the class is traversable and should be traversed according
* to the selected traversal strategy, a new collection node is put on the
* stack.
*
* @param ClassNode $node The class node
* @param ExecutionContextInterface $context The current execution context
* @param \SplStack $nodeStack The stack for storing the
* successor nodes
*
* @see ClassNode
* @see PropertyNode
* @see CollectionNode
* @see TraversalStrategy
*/
private function traverseClassNode(ClassNode $node, ExecutionContextInterface $context, \SplStack $nodeStack)
{
// Visitors have two possibilities to influence the traversal:
//
@ -212,7 +215,7 @@ class NodeTraverser implements NodeTraverserInterface
foreach ($node->metadata->getConstrainedProperties() as $propertyName) {
foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
$nodeQueue->enqueue(new PropertyNode(
$nodeStack->push(new PropertyNode(
$node->value,
$propertyMetadata->getPropertyValue($node->value),
$propertyMetadata,
@ -246,7 +249,7 @@ class NodeTraverser implements NodeTraverserInterface
}
// If TRAVERSE, the constructor will fail if we have no Traversable
$nodeQueue->enqueue(new CollectionNode(
$nodeStack->push(new CollectionNode(
$node->value,
$node->propertyPath,
$node->groups,
@ -255,7 +258,31 @@ class NodeTraverser implements NodeTraverserInterface
));
}
private function traverseCollectionNode(CollectionNode $node, \SplQueue $nodeQueue, ExecutionContextInterface $context)
/**
* Traverses a collection node.
*
* At first, each visitor is invoked for this node. Then, unless any
* of the visitors aborts the traversal by returning false, the successor
* nodes of the collection node are put on the stack:
*
* - for each object in the collection with associated class metadata, a
* new class node is put on the stack;
* - if an object has no associated class metadata, but is traversable, and
* unless the {@link TraversalStrategy::STOP_RECURSION} flag is set for
* collection node, a new collection node is put on the stack for that
* object;
* - for each array in the collection, a new collection node is put on the
* stack.
*
* @param CollectionNode $node The collection node
* @param ExecutionContextInterface $context The current execution context
* @param \SplStack $nodeStack The stack for storing the
* successor nodes
*
* @see ClassNode
* @see CollectionNode
*/
private function traverseCollectionNode(CollectionNode $node, ExecutionContextInterface $context, \SplStack $nodeStack)
{
// Visitors have two possibilities to influence the traversal:
//
@ -285,7 +312,7 @@ class NodeTraverser implements NodeTraverserInterface
// Arrays are always cascaded, independent of the specified
// traversal strategy
// (BC with Symfony < 2.5)
$nodeQueue->enqueue(new CollectionNode(
$nodeStack->push(new CollectionNode(
$value,
$node->propertyPath.'['.$key.']',
$node->groups,
@ -304,13 +331,142 @@ class NodeTraverser implements NodeTraverserInterface
$node->propertyPath.'['.$key.']',
$node->groups,
$traversalStrategy,
$nodeQueue
$nodeStack
);
}
}
}
private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy, \SplQueue $nodeQueue)
/**
* Traverses a node that is neither a class nor a collection node.
*
* At first, each visitor is invoked for this node. Then, unless any
* of the visitors aborts the traversal by returning false, the successor
* nodes of the collection node are put on the stack:
*
* - if the node contains an object with associated class metadata, a new
* class node is put on the stack;
* - if the node contains a traversable object without associated class
* metadata and traversal is enabled according to the selected traversal
* strategy, a collection node is put on the stack;
* - if the node contains an array, a collection node is put on the stack.
*
* @param Node $node The node
* @param ExecutionContextInterface $context The current execution context
* @param \SplStack $nodeStack The stack for storing the
* successor nodes
*/
private function traverseNode(Node $node, ExecutionContextInterface $context, \SplStack $nodeStack)
{
// Visitors have two possibilities to influence the traversal:
//
// 1. If a visitor's visit() method returns false, the traversal is
// skipped entirely.
// 2. If a visitor's visit() method removes a group from the node,
// that group will be skipped in the subtree of that node.
if (false === $this->visit($node, $context)) {
return;
}
if (null === $node->value) {
return;
}
// The "cascadedGroups" property is set by the NodeValidationVisitor when
// traversing group sequences
$cascadedGroups = null !== $node->cascadedGroups
? $node->cascadedGroups
: $node->groups;
if (0 === count($cascadedGroups)) {
return;
}
$cascadingStrategy = $node->metadata->getCascadingStrategy();
$traversalStrategy = $node->traversalStrategy;
// If no specific traversal strategy was requested when this method
// was called, use the traversal strategy of the node's metadata
if ($traversalStrategy & TraversalStrategy::IMPLICIT) {
// Keep the STOP_RECURSION flag, if it was set
$traversalStrategy = $node->metadata->getTraversalStrategy()
| ($traversalStrategy & TraversalStrategy::STOP_RECURSION);
}
if (is_array($node->value)) {
// Arrays are always traversed, independent of the specified
// traversal strategy
// (BC with Symfony < 2.5)
$nodeStack->push(new CollectionNode(
$node->value,
$node->propertyPath,
$cascadedGroups,
null,
$traversalStrategy
));
return;
}
if ($cascadingStrategy & CascadingStrategy::CASCADE) {
// If the value is a scalar, pass it anyway, because we want
// a NoSuchMetadataException to be thrown in that case
// (BC with Symfony < 2.5)
$this->cascadeObject(
$node->value,
$node->propertyPath,
$cascadedGroups,
$traversalStrategy,
$nodeStack
);
return;
}
// Traverse only if IMPLICIT or TRAVERSE
if (!($traversalStrategy & (TraversalStrategy::IMPLICIT | TraversalStrategy::TRAVERSE))) {
return;
}
// If IMPLICIT, stop unless we deal with a Traversable
if ($traversalStrategy & TraversalStrategy::IMPLICIT && !$node->value instanceof \Traversable) {
return;
}
// If TRAVERSE, the constructor will fail if we have no Traversable
$nodeStack->push(new CollectionNode(
$node->value,
$node->propertyPath,
$cascadedGroups,
null,
$traversalStrategy
));
}
/**
* Executes the cascading logic for an object.
*
* If class metadata is available for the object, a class node is put on
* the node stack. Otherwise, if the selected traversal strategy allows
* traversal of the object, a new collection node is put on the stack.
* Otherwise, an exception is thrown.
*
* @param object $object The object to cascade
* @param string $propertyPath The current property path
* @param string[] $groups The validated groups
* @param integer $traversalStrategy The strategy for traversing the
* cascaded object
* @param \SplStack $nodeStack The stack for storing the successor
* nodes
*
* @throws NoSuchMetadataException If the object has no associated metadata
* and does not implement {@link \Traversable}
* or if traversal is disabled via the
* $traversalStrategy argument
*
*/
private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy, \SplStack $nodeStack)
{
try {
$classMetadata = $this->metadataFactory->getMetadataFor($object);
@ -319,7 +475,7 @@ class NodeTraverser implements NodeTraverserInterface
// error
}
$nodeQueue->enqueue(new ClassNode(
$nodeStack->push(new ClassNode(
$object,
$classMetadata,
$propertyPath,
@ -338,7 +494,7 @@ class NodeTraverser implements NodeTraverserInterface
throw $e;
}
$nodeQueue->enqueue(new CollectionNode(
$nodeStack->push(new CollectionNode(
$object,
$propertyPath,
$groups,

View File

@ -18,7 +18,7 @@ use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\NodeVisitor\ContextUpdateVisitor;
use Symfony\Component\Validator\NodeVisitor\GroupSequenceResolvingVisitor;
use Symfony\Component\Validator\NodeVisitor\NodeValidationVisitor;
use Symfony\Component\Validator\NodeTraverser\NodeTraverser;
use Symfony\Component\Validator\NodeTraverser\NonRecursiveNodeTraverser;
use Symfony\Component\Validator\Validator\LegacyValidator;
class LegacyValidator2Dot5ApiTest extends Abstract2Dot5ApiTest
@ -34,7 +34,7 @@ class LegacyValidator2Dot5ApiTest extends Abstract2Dot5ApiTest
protected function createValidator(MetadataFactoryInterface $metadataFactory)
{
$nodeTraverser = new NodeTraverser($metadataFactory);
$nodeTraverser = new NonRecursiveNodeTraverser($metadataFactory);
$nodeValidator = new NodeValidationVisitor($nodeTraverser, new ConstraintValidatorFactory());
$contextFactory = new LegacyExecutionContextFactory($nodeValidator, new DefaultTranslator());
$validator = new LegacyValidator($contextFactory, $nodeTraverser, $metadataFactory);

View File

@ -18,7 +18,7 @@ use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\NodeVisitor\ContextUpdateVisitor;
use Symfony\Component\Validator\NodeVisitor\GroupSequenceResolvingVisitor;
use Symfony\Component\Validator\NodeVisitor\NodeValidationVisitor;
use Symfony\Component\Validator\NodeTraverser\NodeTraverser;
use Symfony\Component\Validator\NodeTraverser\NonRecursiveNodeTraverser;
use Symfony\Component\Validator\Validator\LegacyValidator;
class LegacyValidatorLegacyApiTest extends AbstractLegacyApiTest
@ -34,7 +34,7 @@ class LegacyValidatorLegacyApiTest extends AbstractLegacyApiTest
protected function createValidator(MetadataFactoryInterface $metadataFactory)
{
$nodeTraverser = new NodeTraverser($metadataFactory);
$nodeTraverser = new NonRecursiveNodeTraverser($metadataFactory);
$nodeValidator = new NodeValidationVisitor($nodeTraverser, new ConstraintValidatorFactory());
$contextFactory = new LegacyExecutionContextFactory($nodeValidator, new DefaultTranslator());
$validator = new LegacyValidator($contextFactory, $nodeTraverser, $metadataFactory);

View File

@ -18,14 +18,14 @@ use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\NodeVisitor\ContextUpdateVisitor;
use Symfony\Component\Validator\NodeVisitor\GroupSequenceResolvingVisitor;
use Symfony\Component\Validator\NodeVisitor\NodeValidationVisitor;
use Symfony\Component\Validator\NodeTraverser\NodeTraverser;
use Symfony\Component\Validator\NodeTraverser\NonRecursiveNodeTraverser;
use Symfony\Component\Validator\Validator\Validator;
class Validator2Dot5ApiTest extends Abstract2Dot5ApiTest
{
protected function createValidator(MetadataFactoryInterface $metadataFactory)
{
$nodeTraverser = new NodeTraverser($metadataFactory);
$nodeTraverser = new NonRecursiveNodeTraverser($metadataFactory);
$nodeValidator = new NodeValidationVisitor($nodeTraverser, new ConstraintValidatorFactory());
$contextFactory = new ExecutionContextFactory($nodeValidator, new DefaultTranslator());
$validator = new Validator($contextFactory, $nodeTraverser, $metadataFactory);