diff --git a/src/Symfony/Component/Validator/Node/ClassNode.php b/src/Symfony/Component/Validator/Node/ClassNode.php deleted file mode 100644 index f52a68366b..0000000000 --- a/src/Symfony/Component/Validator/Node/ClassNode.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Node; - -use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Symfony\Component\Validator\Mapping\ClassMetadataInterface; -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 - */ -class ClassNode extends Node -{ - /** - * @var ClassMetadataInterface - */ - public $metadata; - - /** - * 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 The strategy used for - * traversing the object - * - * @throws UnexpectedTypeException If $object is not an object - * - * @see \Symfony\Component\Validator\Mapping\TraversalStrategy - */ - public function __construct($object, $cacheKey, ClassMetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null, $traversalStrategy = TraversalStrategy::IMPLICIT) - { - if (!is_object($object)) { - throw new UnexpectedTypeException($object, 'object'); - } - - parent::__construct( - $object, - $cacheKey, - $metadata, - $propertyPath, - $groups, - $cascadedGroups, - $traversalStrategy - ); - } -} diff --git a/src/Symfony/Component/Validator/Node/CollectionNode.php b/src/Symfony/Component/Validator/Node/CollectionNode.php deleted file mode 100644 index 79a49ff6e3..0000000000 --- a/src/Symfony/Component/Validator/Node/CollectionNode.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Node; - -use Symfony\Component\Validator\Exception\ConstraintDefinitionException; -use Symfony\Component\Validator\Mapping\TraversalStrategy; - -/** - * Represents a traversable value in the validation graph. - * - * @since 2.5 - * @author Bernhard Schussek - */ -class CollectionNode extends Node -{ - /** - * Creates a new collection node. - * - * @param array|\Traversable $collection The validated collection - * @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 collection - * - * @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".', - get_class($collection) - )); - } - - parent::__construct( - $collection, - null, - null, - $propertyPath, - $groups, - $cascadedGroups, - $traversalStrategy - ); - } -} diff --git a/src/Symfony/Component/Validator/Node/GenericNode.php b/src/Symfony/Component/Validator/Node/GenericNode.php deleted file mode 100644 index 9c628e0a46..0000000000 --- a/src/Symfony/Component/Validator/Node/GenericNode.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Node; - -/** - * Represents a value that has neither class metadata nor property metadata - * attached to it. - * - * Together with {@link \Symfony\Component\Validator\Mapping\GenericMetadata}, - * this node type can be used to validate a value against some constraints. - * - * @since 2.5 - * @author Bernhard Schussek - */ -class GenericNode extends Node -{ -} diff --git a/src/Symfony/Component/Validator/Node/Node.php b/src/Symfony/Component/Validator/Node/Node.php deleted file mode 100644 index 93099844be..0000000000 --- a/src/Symfony/Component/Validator/Node/Node.php +++ /dev/null @@ -1,101 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Node; - -use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Symfony\Component\Validator\Mapping\MetadataInterface; -use Symfony\Component\Validator\Mapping\TraversalStrategy; - -/** - * A node in the validation graph. - * - * @since 2.5 - * @author Bernhard Schussek - */ -abstract class Node -{ - /** - * The validated value. - * - * @var mixed - */ - public $value; - - public $cacheKey; - - /** - * The metadata specifying how the value should be validated. - * - * @var MetadataInterface|null - */ - public $metadata; - - /** - * The property path leading to this node. - * - * @var string - */ - public $propertyPath; - - /** - * The groups in which the value should be validated. - * - * @var string[] - */ - public $groups; - - /** - * The groups in which cascaded values should be validated. - * - * @var string[] - */ - public $cascadedGroups; - - /** - * The strategy used for traversing the validated value. - * - * @var integer - * - * @see \Symfony\Component\Validator\Mapping\TraversalStrategy - */ - public $traversalStrategy; - - /** - * Creates a new property node. - * - * @param mixed $value The property value - * @param MetadataInterface|null $metadata The property's metadata - * @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 - * - * @throws UnexpectedTypeException If $cascadedGroups is invalid - */ - public function __construct($value, $cacheKey, MetadataInterface $metadata = null, $propertyPath, array $groups, $cascadedGroups = null, $traversalStrategy = TraversalStrategy::IMPLICIT) - { - if (null !== $cascadedGroups && !is_array($cascadedGroups)) { - throw new UnexpectedTypeException($cascadedGroups, 'null or array'); - } - - $this->value = $value; - $this->cacheKey = $cacheKey; - $this->metadata = $metadata; - $this->propertyPath = $propertyPath; - $this->groups = $groups; - $this->cascadedGroups = $cascadedGroups; - $this->traversalStrategy = $traversalStrategy; - } -} diff --git a/src/Symfony/Component/Validator/Node/PropertyNode.php b/src/Symfony/Component/Validator/Node/PropertyNode.php deleted file mode 100644 index 4ee7ac5918..0000000000 --- a/src/Symfony/Component/Validator/Node/PropertyNode.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Node; - -use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; -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: - * - * (Article:ClassNode) - * \ - * (->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 - */ -class PropertyNode extends Node -{ - /** - * @var PropertyMetadataInterface - */ - public $metadata; - - /** - * Creates a new property node. - * - * @param object $object The object the property - * belongs to - * @param mixed $value The property value - * @param PropertyMetadataInterface $metadata The property's metadata - * @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 - * - * @throws UnexpectedTypeException If $object is not an object - * - * @see \Symfony\Component\Validator\Mapping\TraversalStrategy - */ - public function __construct($value, $cacheKey, PropertyMetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null, $traversalStrategy = TraversalStrategy::IMPLICIT) - { - parent::__construct( - $value, - $cacheKey, - $metadata, - $propertyPath, - $groups, - $cascadedGroups, - $traversalStrategy - ); - } - -} diff --git a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php deleted file mode 100644 index 36e559ba68..0000000000 --- a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\NodeTraverser; - -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Node\Node; -use Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface; - -/** - * 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 are called in the same order in which they are - * added to the traverser. - * - * If the {@link traverse()} method is called recursively, the - * {@link \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface::beforeTraversal()} - * and {@link \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface::afterTraversal()} - * methods of the visitors will be invoked for each call. - * - * 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 - */ -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($nodes, ExecutionContextInterface $context); -} diff --git a/src/Symfony/Component/Validator/NodeTraverser/NonRecursiveNodeTraverser.php b/src/Symfony/Component/Validator/NodeTraverser/NonRecursiveNodeTraverser.php deleted file mode 100644 index c29bac71e2..0000000000 --- a/src/Symfony/Component/Validator/NodeTraverser/NonRecursiveNodeTraverser.php +++ /dev/null @@ -1,560 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\NodeTraverser; - -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Exception\NoSuchMetadataException; -use Symfony\Component\Validator\Exception\UnsupportedMetadataException; -use Symfony\Component\Validator\Mapping\CascadingStrategy; -use Symfony\Component\Validator\Mapping\ClassMetadataInterface; -use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; -use Symfony\Component\Validator\Mapping\TraversalStrategy; -use Symfony\Component\Validator\MetadataFactoryInterface; -use Symfony\Component\Validator\Node\ClassNode; -use Symfony\Component\Validator\Node\CollectionNode; -use Symfony\Component\Validator\Node\Node; -use Symfony\Component\Validator\Node\PropertyNode; -use Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface; - -/** - * 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 - * - * @see NodeTraverserInterface - * @see CascadingStrategy - * @see TraversalStrategy - */ -class NonRecursiveNodeTraverser implements NodeTraverserInterface -{ - /** - * @var NodeVisitorInterface[] - */ - private $visitors; - - /** - * @var MetadataFactoryInterface - */ - private $metadataFactory; - - /** - * Creates a new traverser. - * - * @param MetadataFactoryInterface $metadataFactory The metadata factory - */ - public function __construct(MetadataFactoryInterface $metadataFactory) - { - $this->visitors = new \SplObjectStorage(); - $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); - } - - /** - * {@inheritdoc} - */ - public function traverse($nodes, ExecutionContextInterface $context) - { - if (!is_array($nodes)) { - $nodes = array($nodes); - } - - $numberOfInitializedVisitors = $this->beforeTraversal($nodes, $context); - - // If any of the visitors requested to abort the traversal, do so, but - // clean up before - if ($numberOfInitializedVisitors < count($this->visitors)) { - $this->afterTraversal($nodes, $context, $numberOfInitializedVisitors); - - return; - } - - // 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) { - // 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); - - // 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, $context, $nodeStack); - } elseif ($node instanceof CollectionNode) { - $this->traverseCollectionNode($node, $context, $nodeStack); - } else { - $this->traverseNode($node, $context, $nodeStack); - } - } - } - - $this->afterTraversal($nodes, $context); - } - - /** - * Executes the {@link NodeVisitorInterface::beforeTraversal()} method of - * each visitor. - * - * @param Node[] $nodes The traversed nodes - * @param ExecutionContextInterface $context The current execution context - * - * @return integer The number of successful calls. This is lower than - * the number of visitors if any of the visitors' - * beforeTraversal() methods returned false - */ - private function beforeTraversal($nodes, ExecutionContextInterface $context) - { - $numberOfCalls = 1; - - foreach ($this->visitors as $visitor) { - if (false === $visitor->beforeTraversal($nodes, $context)) { - break; - } - - ++$numberOfCalls; - } - - return $numberOfCalls; - } - - /** - * Executes the {@link NodeVisitorInterface::beforeTraversal()} method of - * each visitor. - * - * @param Node[] $nodes The traversed nodes - * @param ExecutionContextInterface $context The current execution context - * @param integer|null $limit Limits the number of visitors - * on which beforeTraversal() - * should be called. All visitors - * will be called by default - */ - private function afterTraversal($nodes, ExecutionContextInterface $context, $limit = null) - { - if (null === $limit) { - $limit = count($this->visitors); - } - - $numberOfCalls = 0; - - foreach ($this->visitors as $visitor) { - $visitor->afterTraversal($nodes, $context); - - if (++$numberOfCalls === $limit) { - return; - } - } - } - - /** - * 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) { - if (false === $visitor->visit($node, $context)) { - return false; - } - } - - return true; - } - - /** - * 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 - * - * @throws UnsupportedMetadataException If a property metadata does not - * implement {@link PropertyMetadataInterface} - * - * @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: - // - // 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 (0 === count($node->groups)) { - return; - } - - foreach ($node->metadata->getConstrainedProperties() as $propertyName) { - foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) { - if (!$propertyMetadata instanceof PropertyMetadataInterface) { - throw new UnsupportedMetadataException(sprintf( - 'The property metadata instances should implement '. - '"Symfony\Component\Validator\Mapping\PropertyMetadataInterface", '. - 'got: "%s".', - is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata) - )); - } - - $nodeStack->push(new PropertyNode( - $propertyMetadata->getPropertyValue($node->value), - $node->cacheKey.':'.$propertyName, - $propertyMetadata, - $node->propertyPath - ? $node->propertyPath.'.'.$propertyName - : $propertyName, - $node->groups, - $node->cascadedGroups - )); - } - } - - $traversalStrategy = $node->traversalStrategy; - - // If no specific traversal strategy was requested when this method - // was called, use the traversal strategy of the class' metadata - if ($traversalStrategy & TraversalStrategy::IMPLICIT) { - // Keep the STOP_RECURSION flag, if it was set - $traversalStrategy = $node->metadata->getTraversalStrategy() - | ($traversalStrategy & TraversalStrategy::STOP_RECURSION); - } - - // 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, - $node->groups, - $node->cascadedGroups, - $traversalStrategy - )); - } - - /** - * 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: - // - // 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 (0 === count($node->groups)) { - return; - } - - $traversalStrategy = $node->traversalStrategy; - - if ($traversalStrategy & TraversalStrategy::STOP_RECURSION) { - $traversalStrategy = TraversalStrategy::NONE; - } else { - $traversalStrategy = TraversalStrategy::IMPLICIT; - } - - foreach ($node->value as $key => $value) { - if (is_array($value)) { - // Arrays are always cascaded, independent of the specified - // traversal strategy - // (BC with Symfony < 2.5) - $nodeStack->push(new CollectionNode( - $value, - $node->propertyPath.'['.$key.']', - $node->groups, - null, - $traversalStrategy - )); - - continue; - } - - // Scalar and null values in the collection are ignored - // (BC with Symfony < 2.5) - if (is_object($value)) { - $this->cascadeObject( - $value, - $node->propertyPath.'['.$key.']', - $node->groups, - $traversalStrategy, - $nodeStack - ); - } - } - } - - /** - * 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; - } - - // Currently, the traversal strategy can only be TRAVERSE for a - // generic node if the cascading strategy is CASCADE. Thus, traversable - // objects will always be handled within cascadeObject() and there's - // nothing more to do here. - - // see GenericMetadata::addConstraint() - } - - /** - * 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 - * @throws UnsupportedMetadataException If the metadata returned by the - * metadata factory does not implement - * {@link ClassMetadataInterface} - */ - private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy, \SplStack $nodeStack) - { - try { - $classMetadata = $this->metadataFactory->getMetadataFor($object); - - if (!$classMetadata instanceof ClassMetadataInterface) { - throw new UnsupportedMetadataException(sprintf( - 'The metadata factory should return instances of '. - '"Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. - 'got: "%s".', - is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) - )); - } - - $nodeStack->push(new ClassNode( - $object, - spl_object_hash($object), - $classMetadata, - $propertyPath, - $groups, - null, - $traversalStrategy - )); - } catch (NoSuchMetadataException $e) { - // Rethrow if not Traversable - if (!$object instanceof \Traversable) { - throw $e; - } - - // Rethrow unless IMPLICIT or TRAVERSE - if (!($traversalStrategy & (TraversalStrategy::IMPLICIT | TraversalStrategy::TRAVERSE))) { - throw $e; - } - - $nodeStack->push(new CollectionNode( - $object, - $propertyPath, - $groups, - null, - $traversalStrategy - )); - } - } -} diff --git a/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php b/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php deleted file mode 100644 index c516f4a8d2..0000000000 --- a/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\NodeVisitor; - -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Node\Node; - -/** - * Base visitor with empty method stubs. - * - * @since 2.5 - * @author Bernhard Schussek - * - * @see NodeVisitorInterface - */ -abstract class AbstractVisitor implements NodeVisitorInterface -{ - /** - * {@inheritdoc} - */ - public function beforeTraversal($nodes, ExecutionContextInterface $context) - { - } - - /** - * {@inheritdoc} - */ - public function afterTraversal($nodes, ExecutionContextInterface $context) - { - } - - /** - * {@inheritdoc} - */ - public function visit(Node $node, ExecutionContextInterface $context) - { - } -} diff --git a/src/Symfony/Component/Validator/NodeVisitor/NodeValidationVisitor.php b/src/Symfony/Component/Validator/NodeVisitor/NodeValidationVisitor.php deleted file mode 100644 index 5eee760c8a..0000000000 --- a/src/Symfony/Component/Validator/NodeVisitor/NodeValidationVisitor.php +++ /dev/null @@ -1,207 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\NodeVisitor; - -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\GroupSequence; -use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Node\ClassNode; -use Symfony\Component\Validator\Node\CollectionNode; -use Symfony\Component\Validator\Node\Node; -use Symfony\Component\Validator\Node\PropertyNode; -use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; - -/** - * Validates a node's value against the constraints defined in it's metadata. - * - * @since 2.5 - * @author Bernhard Schussek - */ -class NodeValidationVisitor extends AbstractVisitor -{ - /** - * @var ConstraintValidatorFactoryInterface - */ - private $validatorFactory; - - /** - * @var NodeTraverserInterface - */ - private $nodeTraverser; - - /** - * Creates a new visitor. - * - * @param NodeTraverserInterface $nodeTraverser The node traverser - * @param ConstraintValidatorFactoryInterface $validatorFactory The validator factory - */ - public function __construct(NodeTraverserInterface $nodeTraverser, ConstraintValidatorFactoryInterface $validatorFactory) - { - $this->validatorFactory = $validatorFactory; - $this->nodeTraverser = $nodeTraverser; - } - - /** - * Validates a node's value against the constraints defined in the node's - * metadata. - * - * Objects and constraints that were validated before in the same context - * will be skipped. - * - * @param Node $node The current node - * @param ExecutionContextInterface $context The execution context - * - * @return Boolean Whether to traverse the successor nodes - */ - public function visit(Node $node, ExecutionContextInterface $context) - { - if ($node instanceof CollectionNode) { - return true; - } - - $context->setNode($node->value, $node->metadata, $node->propertyPath); - - // if group (=[,G3,G4]) contains group sequence (=) - // then call traverse() with each entry of the group sequence and abort - // if necessary (G1, G2) - // finally call traverse() with remaining entries ([G3,G4]) or - // simply continue traversal (if possible) - - foreach ($node->groups as $key => $group) { - $cascadedGroup = null; - - // Even if we remove the following clause, the constraints on an - // object won't be validated again due to the measures taken in - // validateNodeForGroup(). - // The following shortcut, however, prevents validatedNodeForGroup() - // from being called at all and enhances performance a bit. - if ($node instanceof ClassNode) { - // Use the object hash for group sequences - $groupHash = is_object($group) ? spl_object_hash($group) : $group; - - if ($context->isGroupValidated($node->cacheKey, $groupHash)) { - // Skip this group when validating the successor nodes - // (property and/or collection nodes) - unset($node->groups[$key]); - - continue; - } - - $context->markGroupAsValidated($node->cacheKey, $groupHash); - - // Replace the "Default" group by the group sequence defined - // for the class, if applicable - // This is done after checking the cache, so that - // spl_object_hash() isn't called for this sequence and - // "Default" is used instead in the cache. This is useful - // if the getters below return different group sequences in - // every call. - if (Constraint::DEFAULT_GROUP === $group) { - if ($node->metadata->hasGroupSequence()) { - // The group sequence is statically defined for the class - $group = $node->metadata->getGroupSequence(); - $cascadedGroup = Constraint::DEFAULT_GROUP; - } elseif ($node->metadata->isGroupSequenceProvider()) { - // The group sequence is dynamically obtained from the validated - // object - /** @var \Symfony\Component\Validator\GroupSequenceProviderInterface $value */ - $group = $node->value->getGroupSequence(); - $cascadedGroup = Constraint::DEFAULT_GROUP; - - if (!$group instanceof GroupSequence) { - $group = new GroupSequence($group); - } - } - } - } - - if ($group instanceof GroupSequence) { - // Traverse group sequence until a violation is generated - $this->traverseGroupSequence($node, $group, $cascadedGroup, $context); - - // Skip the group sequence when validating successor nodes - unset($node->groups[$key]); - - continue; - } - - // Validate normal group - $this->validateInGroup($node, $group, $context); - } - - return true; - } - - /** - * Validates a node's value in each group of a group sequence. - * - * If any of the groups' constraints generates a violation, subsequent - * groups are not validated anymore. - * - * @param Node $node The validated node - * @param GroupSequence $groupSequence The group sequence - * @param ExecutionContextInterface $context The execution context - */ - private function traverseGroupSequence(Node $node, GroupSequence $groupSequence, $cascadedGroup, ExecutionContextInterface $context) - { - $violationCount = count($context->getViolations()); - $cascadedGroups = $cascadedGroup ? array($cascadedGroup) : null; - - foreach ($groupSequence->groups as $groupInSequence) { - $node = clone $node; - $node->groups = array($groupInSequence); - $node->cascadedGroups = $cascadedGroups; - - $this->nodeTraverser->traverse(array($node), $context); - - // Abort sequence validation if a violation was generated - if (count($context->getViolations()) > $violationCount) { - break; - } - } - } - - /** - * Validates a node's value against all constraints in the given group. - * - * @param Node $node The validated node - * @param string $group The group to validate - * @param ExecutionContextInterface $context The execution context - * @param string $objectHash The hash of the node's - * object (if any) - * - * @throws \Exception - */ - private function validateInGroup(Node $node, $group, ExecutionContextInterface $context) - { - $context->setGroup($group); - - foreach ($node->metadata->findConstraints($group) as $constraint) { - // Prevent duplicate validation of constraints, in the case - // that constraints belong to multiple validated groups - if (null !== $node->cacheKey) { - $constraintHash = spl_object_hash($constraint); - - if ($context->isConstraintValidated($node->cacheKey, $constraintHash)) { - continue; - } - - $context->markConstraintAsValidated($node->cacheKey, $constraintHash); - } - - $validator = $this->validatorFactory->getInstance($constraint); - $validator->initialize($context); - $validator->validate($node->value, $constraint); - } - } -} diff --git a/src/Symfony/Component/Validator/NodeVisitor/NodeVisitorInterface.php b/src/Symfony/Component/Validator/NodeVisitor/NodeVisitorInterface.php deleted file mode 100644 index ec05923f1b..0000000000 --- a/src/Symfony/Component/Validator/NodeVisitor/NodeVisitorInterface.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\NodeVisitor; - -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Node\Node; - -/** - * A node visitor invoked by the node traverser. - * - * At the beginning of the traversal, the method {@link beforeTraversal()} is - * called. For each traversed node, the method {@link visit()} is called. At - * last, the method {@link afterTraversal()} is called when the traversal is - * complete. - * - * @since 2.5 - * @author Bernhard Schussek - * - * @see \Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface - */ -interface NodeVisitorInterface -{ - /** - * Called at the beginning of a traversal. - * - * @param Node[] $nodes A list of Node instances - * @param ExecutionContextInterface $context The execution context - * - * @return Boolean Whether to continue the traversal - */ - public function beforeTraversal($nodes, ExecutionContextInterface $context); - - /** - * Called at the end of a traversal. - * - * @param Node[] $nodes A list of Node instances - * @param ExecutionContextInterface $context The execution context - */ - public function afterTraversal($nodes, ExecutionContextInterface $context); - - /** - * Called for each node during a traversal. - * - * @param Node $node The current node - * @param ExecutionContextInterface $context The execution context - * - * @return Boolean Whether to traverse the node's successor nodes - */ - public function visit(Node $node, ExecutionContextInterface $context); -} diff --git a/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializationVisitor.php b/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializationVisitor.php deleted file mode 100644 index 18fe19a955..0000000000 --- a/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializationVisitor.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\NodeVisitor; - -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Exception\InvalidArgumentException; -use Symfony\Component\Validator\Node\ClassNode; -use Symfony\Component\Validator\Node\Node; -use Symfony\Component\Validator\ObjectInitializerInterface; - -/** - * Initializes the objects of all class nodes. - * - * You have to pass at least one instance of {@link ObjectInitializerInterface} - * to the constructor of this visitor. - * - * @since 2.5 - * @author Bernhard Schussek - */ -class ObjectInitializationVisitor extends AbstractVisitor -{ - /** - * @var ObjectInitializerInterface[] - */ - private $initializers; - - /** - * Creates a new visitor. - * - * @param ObjectInitializerInterface[] $initializers The object initializers - * - * @throws InvalidArgumentException - */ - public function __construct(array $initializers) - { - foreach ($initializers as $initializer) { - if (!$initializer instanceof ObjectInitializerInterface) { - throw new InvalidArgumentException(sprintf( - 'Validator initializers must implement '. - '"Symfony\Component\Validator\ObjectInitializerInterface". '. - 'Got: "%s"', - is_object($initializer) ? get_class($initializer) : gettype($initializer) - )); - } - } - - // If no initializer is present, this visitor should not even be created - if (0 === count($initializers)) { - throw new InvalidArgumentException('Please pass at least one initializer.'); - } - - $this->initializers = $initializers; - } - - /** - * Calls the {@link ObjectInitializerInterface::initialize()} method for - * the object of each class node. - * - * @param Node $node The current node - * @param ExecutionContextInterface $context The execution context - * - * @return Boolean Always returns true - */ - public function visit(Node $node, ExecutionContextInterface $context) - { - if ($node instanceof ClassNode) { - foreach ($this->initializers as $initializer) { - $initializer->initialize($node->value); - } - } - - return true; - } -} diff --git a/src/Symfony/Component/Validator/Tests/Node/ClassNodeTest.php b/src/Symfony/Component/Validator/Tests/Node/ClassNodeTest.php deleted file mode 100644 index c79f4c838f..0000000000 --- a/src/Symfony/Component/Validator/Tests/Node/ClassNodeTest.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Node; - -use Symfony\Component\Validator\Node\ClassNode; - -/** - * @since 2.5 - * @author Bernhard Schussek - */ -class ClassNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException - */ - public function testConstructorExpectsObject() - { - $metadata = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataInterface'); - - new ClassNode('foobar', null, $metadata, '', array(), array()); - } -} diff --git a/src/Symfony/Component/Validator/Tests/NodeTraverser/NonRecursiveNodeTraverserTest.php b/src/Symfony/Component/Validator/Tests/NodeTraverser/NonRecursiveNodeTraverserTest.php deleted file mode 100644 index 09e26bcaf9..0000000000 --- a/src/Symfony/Component/Validator/Tests/NodeTraverser/NonRecursiveNodeTraverserTest.php +++ /dev/null @@ -1,91 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\NodeTraverser; - -use Symfony\Component\Validator\Mapping\GenericMetadata; -use Symfony\Component\Validator\Node\GenericNode; -use Symfony\Component\Validator\NodeTraverser\NonRecursiveNodeTraverser; -use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; - -/** - * @since 2.5 - * @author Bernhard Schussek - */ -class NonRecursiveNodeTraverserTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var FakeMetadataFactory - */ - private $metadataFactory; - - /** - * @var NonRecursiveNodeTraverser - */ - private $traverser; - - protected function setUp() - { - $this->metadataFactory = new FakeMetadataFactory(); - $this->traverser = new NonRecursiveNodeTraverser($this->metadataFactory); - } - - public function testVisitorsMayPreventTraversal() - { - $nodes = array(new GenericNode('value', null, new GenericMetadata(), '', array('Default'))); - $context = $this->getMock('Symfony\Component\Validator\Context\ExecutionContextInterface'); - - $visitor1 = $this->getMock('Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface'); - $visitor2 = $this->getMock('Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface'); - $visitor3 = $this->getMock('Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface'); - - $visitor1->expects($this->once()) - ->method('beforeTraversal') - ->with($nodes, $context); - - // abort traversal - $visitor2->expects($this->once()) - ->method('beforeTraversal') - ->with($nodes, $context) - ->will($this->returnValue(false)); - - // never called - $visitor3->expects($this->never()) - ->method('beforeTraversal'); - - $visitor1->expects($this->never()) - ->method('visit'); - $visitor2->expects($this->never()) - ->method('visit'); - $visitor2->expects($this->never()) - ->method('visit'); - - // called in order to clean up - $visitor1->expects($this->once()) - ->method('afterTraversal') - ->with($nodes, $context); - - // abort traversal - $visitor2->expects($this->once()) - ->method('afterTraversal') - ->with($nodes, $context); - - // never called, because beforeTraversal() wasn't called either - $visitor3->expects($this->never()) - ->method('afterTraversal'); - - $this->traverser->addVisitor($visitor1); - $this->traverser->addVisitor($visitor2); - $this->traverser->addVisitor($visitor3); - - $this->traverser->traverse($nodes, $context); - } -} diff --git a/src/Symfony/Component/Validator/Tests/Validator/TraversingValidator2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/TraversingValidator2Dot5ApiTest.php deleted file mode 100644 index 5c76a173a4..0000000000 --- a/src/Symfony/Component/Validator/Tests/Validator/TraversingValidator2Dot5ApiTest.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Tests\Validator; - -use Symfony\Component\Validator\ConstraintValidatorFactory; -use Symfony\Component\Validator\Context\ExecutionContextFactory; -use Symfony\Component\Validator\DefaultTranslator; -use Symfony\Component\Validator\MetadataFactoryInterface; -use Symfony\Component\Validator\NodeTraverser\NonRecursiveNodeTraverser; -use Symfony\Component\Validator\NodeVisitor\NodeValidationVisitor; -use Symfony\Component\Validator\Validator\TraversingValidator; - -class TraversingValidator2Dot5ApiTest extends Abstract2Dot5ApiTest -{ - protected function createValidator(MetadataFactoryInterface $metadataFactory) - { - $nodeTraverser = new NonRecursiveNodeTraverser($metadataFactory); - $contextFactory = new ExecutionContextFactory(new DefaultTranslator()); - $validator = new TraversingValidator($contextFactory, $nodeTraverser, $metadataFactory); - - $nodeTraverser->addVisitor(new NodeValidationVisitor($nodeTraverser, new ConstraintValidatorFactory())); - - return $validator; - } -} diff --git a/src/Symfony/Component/Validator/Tests/ValidatorBuilderTest.php b/src/Symfony/Component/Validator/Tests/ValidatorBuilderTest.php index 5cd1198654..9228d4564c 100644 --- a/src/Symfony/Component/Validator/Tests/ValidatorBuilderTest.php +++ b/src/Symfony/Component/Validator/Tests/ValidatorBuilderTest.php @@ -130,7 +130,7 @@ class ValidatorBuilderTest extends \PHPUnit_Framework_TestCase public function testSetApiVersion25() { $this->assertSame($this->builder, $this->builder->setApiVersion(Validation::API_VERSION_2_5)); - $this->assertInstanceOf('Symfony\Component\Validator\Validator\TraversingValidator', $this->builder->getValidator()); + $this->assertInstanceOf('Symfony\Component\Validator\Validator\RecursiveValidator', $this->builder->getValidator()); } public function testSetApiVersion24And25() diff --git a/src/Symfony/Component/Validator/Validator/TraversingContextualValidator.php b/src/Symfony/Component/Validator/Validator/TraversingContextualValidator.php deleted file mode 100644 index bd749eeb97..0000000000 --- a/src/Symfony/Component/Validator/Validator/TraversingContextualValidator.php +++ /dev/null @@ -1,242 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Validator; - -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Exception\RuntimeException; -use Symfony\Component\Validator\Exception\UnsupportedMetadataException; -use Symfony\Component\Validator\Exception\ValidatorException; -use Symfony\Component\Validator\Mapping\ClassMetadataInterface; -use Symfony\Component\Validator\Mapping\GenericMetadata; -use Symfony\Component\Validator\MetadataFactoryInterface; -use Symfony\Component\Validator\Node\ClassNode; -use Symfony\Component\Validator\Node\CollectionNode; -use Symfony\Component\Validator\Node\GenericNode; -use Symfony\Component\Validator\Node\PropertyNode; -use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; -use Symfony\Component\Validator\Util\PropertyPath; - -/** - * Default implementation of {@link ContextualValidatorInterface}. - * - * @since 2.5 - * @author Bernhard Schussek - */ -class TraversingContextualValidator implements ContextualValidatorInterface -{ - /** - * @var ExecutionContextInterface - */ - private $context; - - /** - * @var NodeTraverserInterface - */ - private $nodeTraverser; - - /** - * @var MetadataFactoryInterface - */ - private $metadataFactory; - - /** - * Creates a validator for the given context. - * - * @param ExecutionContextInterface $context The execution context - * @param NodeTraverserInterface $nodeTraverser The node traverser - * @param MetadataFactoryInterface $metadataFactory The factory for fetching - * the metadata of validated - * objects - */ - public function __construct(ExecutionContextInterface $context, NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory) - { - $this->context = $context; - $this->defaultPropertyPath = $context->getPropertyPath(); - $this->defaultGroups = array($context->getGroup() ?: Constraint::DEFAULT_GROUP); - $this->nodeTraverser = $nodeTraverser; - $this->metadataFactory = $metadataFactory; - } - - /** - * {@inheritdoc} - */ - public function atPath($path) - { - $this->defaultPropertyPath = $this->context->getPropertyPath($path); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function validate($value, $constraints = null, $groups = null) - { - $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - - if (null !== $constraints) { - if (!is_array($constraints)) { - $constraints = array($constraints); - } - - $metadata = new GenericMetadata(); - $metadata->addConstraints($constraints); - - $node = new GenericNode( - $value, - is_object($value) ? spl_object_hash($value) : null, - $metadata, - $this->defaultPropertyPath, - $groups - ); - } elseif (is_array($value) || $value instanceof \Traversable && !$this->metadataFactory->hasMetadataFor($value)) { - $node = new CollectionNode( - $value, - $this->defaultPropertyPath, - $groups - ); - } elseif (is_object($value)) { - $metadata = $this->metadataFactory->getMetadataFor($value); - - if (!$metadata instanceof ClassMetadataInterface) { - throw new UnsupportedMetadataException(sprintf( - 'The metadata factory should return instances of '. - '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. - 'got: "%s".', - is_object($metadata) ? get_class($metadata) : gettype($metadata) - )); - } - - $node = new ClassNode( - $value, - spl_object_hash($value), - $metadata, - $this->defaultPropertyPath, - $groups - ); - } else { - throw new RuntimeException(sprintf( - 'Cannot validate values of type "%s" automatically. Please '. - 'provide a constraint.', - gettype($value) - )); - } - - $this->nodeTraverser->traverse(array($node), $this->context); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function validateProperty($object, $propertyName, $groups = null) - { - $classMetadata = $this->metadataFactory->getMetadataFor($object); - - if (!$classMetadata instanceof ClassMetadataInterface) { - // Cannot be UnsupportedMetadataException because of BC with - // Symfony < 2.5 - throw new ValidatorException(sprintf( - 'The metadata factory should return instances of '. - '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. - 'got: "%s".', - is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) - )); - } - - $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); - $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - $cacheKey = spl_object_hash($object); - $nodes = array(); - - foreach ($propertyMetadatas as $propertyMetadata) { - $propertyValue = $propertyMetadata->getPropertyValue($object); - - $nodes[] = new PropertyNode( - $propertyValue, - $cacheKey.':'.$propertyName, - $propertyMetadata, - PropertyPath::append($this->defaultPropertyPath, $propertyName), - $groups - ); - } - - $this->nodeTraverser->traverse($nodes, $this->context); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function validatePropertyValue($object, $propertyName, $value, $groups = null) - { - $classMetadata = $this->metadataFactory->getMetadataFor($object); - - if (!$classMetadata instanceof ClassMetadataInterface) { - // Cannot be UnsupportedMetadataException because of BC with - // Symfony < 2.5 - throw new ValidatorException(sprintf( - 'The metadata factory should return instances of '. - '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. - 'got: "%s".', - is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) - )); - } - - $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); - $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - $cacheKey = spl_object_hash($object); - $nodes = array(); - - foreach ($propertyMetadatas as $propertyMetadata) { - $nodes[] = new PropertyNode( - $value, - $cacheKey.':'.$propertyName, - $propertyMetadata, - PropertyPath::append($this->defaultPropertyPath, $propertyName), - $groups, - $groups - ); - } - - $this->nodeTraverser->traverse($nodes, $this->context); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getViolations() - { - return $this->context->getViolations(); - } - - /** - * Normalizes the given group or list of groups to an array. - * - * @param mixed $groups The groups to normalize - * - * @return array A group array - */ - protected function normalizeGroups($groups) - { - if (is_array($groups)) { - return $groups; - } - - return array($groups); - } -} diff --git a/src/Symfony/Component/Validator/Validator/TraversingValidator.php b/src/Symfony/Component/Validator/Validator/TraversingValidator.php deleted file mode 100644 index 8fe07f630f..0000000000 --- a/src/Symfony/Component/Validator/Validator/TraversingValidator.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Validator\Validator; - -use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\MetadataFactoryInterface; -use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; - -/** - * Default implementation of {@link ValidatorInterface}. - * - * @since 2.5 - * @author Bernhard Schussek - */ -class TraversingValidator implements ValidatorInterface -{ - /** - * @var ExecutionContextFactoryInterface - */ - protected $contextFactory; - - /** - * @var NodeTraverserInterface - */ - protected $nodeTraverser; - - /** - * @var MetadataFactoryInterface - */ - protected $metadataFactory; - - /** - * Creates a new validator. - * - * @param ExecutionContextFactoryInterface $contextFactory The factory for - * creating new contexts - * @param NodeTraverserInterface $nodeTraverser The node traverser - * @param MetadataFactoryInterface $metadataFactory The factory for - * fetching the metadata - * of validated objects - */ - public function __construct(ExecutionContextFactoryInterface $contextFactory, NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory) - { - $this->contextFactory = $contextFactory; - $this->nodeTraverser = $nodeTraverser; - $this->metadataFactory = $metadataFactory; - } - - /** - * {@inheritdoc} - */ - public function startContext($root = null) - { - return new TraversingContextualValidator( - $this->contextFactory->createContext($this, $root), - $this->nodeTraverser, - $this->metadataFactory - ); - } - - /** - * {@inheritdoc} - */ - public function inContext(ExecutionContextInterface $context) - { - return new TraversingContextualValidator( - $context, - $this->nodeTraverser, - $this->metadataFactory - ); - } - - /** - * {@inheritdoc} - */ - public function getMetadataFor($object) - { - return $this->metadataFactory->getMetadataFor($object); - } - - /** - * {@inheritdoc} - */ - public function hasMetadataFor($object) - { - return $this->metadataFactory->hasMetadataFor($object); - } - - /** - * {@inheritdoc} - */ - public function validate($value, $constraints = null, $groups = null) - { - return $this->startContext($value) - ->validate($value, $constraints, $groups) - ->getViolations(); - } - - /** - * {@inheritdoc} - */ - public function validateProperty($object, $propertyName, $groups = null) - { - return $this->startContext($object) - ->validateProperty($object, $propertyName, $groups) - ->getViolations(); - } - - /** - * {@inheritdoc} - */ - public function validatePropertyValue($object, $propertyName, $value, $groups = null) - { - return $this->startContext($object) - ->validatePropertyValue($object, $propertyName, $value, $groups) - ->getViolations(); - } -} diff --git a/src/Symfony/Component/Validator/ValidatorBuilder.php b/src/Symfony/Component/Validator/ValidatorBuilder.php index 2d66fa9f6a..8250ea6684 100644 --- a/src/Symfony/Component/Validator/ValidatorBuilder.php +++ b/src/Symfony/Component/Validator/ValidatorBuilder.php @@ -35,6 +35,7 @@ use Symfony\Component\Validator\NodeTraverser\NonRecursiveNodeTraverser; use Symfony\Component\Validator\NodeVisitor\NodeValidationVisitor; use Symfony\Component\Validator\NodeVisitor\ObjectInitializationVisitor; use Symfony\Component\Validator\Validator\LegacyValidator; +use Symfony\Component\Validator\Validator\RecursiveValidator; use Symfony\Component\Validator\Validator\TraversingValidator; use Symfony\Component\Validator\Validator as ValidatorV24; @@ -416,13 +417,7 @@ class ValidatorBuilder implements ValidatorBuilderInterface $contextFactory = new LegacyExecutionContextFactory($metadataFactory, $translator, $this->translationDomain); if (Validation::API_VERSION_2_5 === $apiVersion) { - $nodeTraverser = new NonRecursiveNodeTraverser($metadataFactory); - if (count($this->initializers) > 0) { - $nodeTraverser->addVisitor(new ObjectInitializationVisitor($this->initializers)); - } - $nodeTraverser->addVisitor(new NodeValidationVisitor($nodeTraverser, $validatorFactory)); - - return new TraversingValidator($contextFactory, $nodeTraverser, $metadataFactory); + return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory); } return new LegacyValidator($contextFactory, $metadataFactory, $validatorFactory);