diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index f96370288c..187de382fb 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -25,7 +25,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Violation\ConstraintViolationBuilder; /** - * The context used and created by {@link ExecutionContextManager}. + * The context used and created by {@link ExecutionContextFactory}. * * @since 2.5 * @author Bernhard Schussek @@ -34,6 +34,11 @@ use Symfony\Component\Validator\Violation\ConstraintViolationBuilder; */ class ExecutionContext implements ExecutionContextInterface, LegacyExecutionContextInterface { + /** + * @var ValidatorInterface + */ + private $validator; + /** * The root value of the validated object graph. * @@ -41,6 +46,21 @@ class ExecutionContext implements ExecutionContextInterface, LegacyExecutionCont */ private $root; + /** + * @var GroupManagerInterface + */ + private $groupManager; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var string + */ + private $translationDomain; + /** * The violations generated in the current context. * @@ -62,32 +82,12 @@ class ExecutionContext implements ExecutionContextInterface, LegacyExecutionCont */ private $nodeStack; - /** - * @var ValidatorInterface - */ - private $validator; - - /** - * @var GroupManagerInterface - */ - private $groupManager; - - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * @var string - */ - private $translationDomain; - /** * Creates a new execution context. * + * @param ValidatorInterface $validator The validator * @param mixed $root The root value of the * validated object graph - * @param ValidatorInterface $validator The validator * @param GroupManagerInterface $groupManager The manager for accessing * the currently validated * group @@ -96,13 +96,13 @@ class ExecutionContext implements ExecutionContextInterface, LegacyExecutionCont * use for translating * violation messages * - * @internal Called by {@link ExecutionContextManager}. Should not be used + * @internal Called by {@link ExecutionContextFactory}. Should not be used * in user code. */ - public function __construct($root, ValidatorInterface $validator, GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) + public function __construct(ValidatorInterface $validator, $root, GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) { - $this->root = $root; $this->validator = $validator; + $this->root = $root; $this->groupManager = $groupManager; $this->translator = $translator; $this->translationDomain = $translationDomain; diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php new file mode 100644 index 0000000000..3305e1a943 --- /dev/null +++ b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Context; + +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Validator\Group\GroupManagerInterface; +use Symfony\Component\Validator\Validator\ValidatorInterface; + +/** + * Creates new {@link ExecutionContext} instances. + * + * @since 2.5 + * @author Bernhard Schussek + */ +class ExecutionContextFactory implements ExecutionContextFactoryInterface +{ + /** + * @var GroupManagerInterface + */ + private $groupManager; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var string|null + */ + private $translationDomain; + + /** + * Creates a new context factory. + * + * @param GroupManagerInterface $groupManager The manager for accessing + * the currently validated + * group + * @param TranslatorInterface $translator The translator + * @param string|null $translationDomain The translation domain to + * use for translating + * violation messages + */ + public function __construct(GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) + { + $this->groupManager = $groupManager; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + /** + * {@inheritdoc} + */ + public function createContext(ValidatorInterface $validator, $root) + { + return new ExecutionContext( + $validator, + $root, + $this->groupManager, + $this->translator, + $this->translationDomain + ); + } +} diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php new file mode 100644 index 0000000000..f0ee00174f --- /dev/null +++ b/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Context; + +use Symfony\Component\Validator\Validator\ValidatorInterface; + +/** + * Creates instances of {@link ExecutionContextInterface}. + * + * You can use a custom factory if you want to customize the execution context + * that is passed through the validation run. + * + * @since 2.5 + * @author Bernhard Schussek + */ +interface ExecutionContextFactoryInterface +{ + /** + * Creates a new execution context. + * + * @param ValidatorInterface $validator The validator + * @param mixed $root The root value of the validated + * object graph + * + * @return ExecutionContextInterface The new execution context + */ + public function createContext(ValidatorInterface $validator, $root); +} diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextManager.php b/src/Symfony/Component/Validator/Context/ExecutionContextManager.php deleted file mode 100644 index 6214eabf46..0000000000 --- a/src/Symfony/Component/Validator/Context/ExecutionContextManager.php +++ /dev/null @@ -1,215 +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\Context; - -use Symfony\Component\Translation\TranslatorInterface; -use Symfony\Component\Validator\Exception\RuntimeException; -use Symfony\Component\Validator\Group\GroupManagerInterface; -use Symfony\Component\Validator\Node\Node; -use Symfony\Component\Validator\NodeVisitor\AbstractVisitor; -use Symfony\Component\Validator\Validator\ValidatorInterface; - -/** - * The default implementation of {@link ExecutionContextManagerInterface}. - * - * This class implements {@link \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface} - * and updates the current context with the current node of the validation - * traversal. - * - * After creating a new instance, the method {@link initialize()} must be - * called with a {@link ValidatorInterface} instance. Calling methods such as - * {@link startContext()} or {@link enterNode()} without initializing the - * manager first will lead to errors. - * - * @since 2.5 - * @author Bernhard Schussek - * - * @see ExecutionContextManagerInterface - * @see \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface - */ -class ExecutionContextManager extends AbstractVisitor implements ExecutionContextManagerInterface -{ - /** - * @var GroupManagerInterface - */ - private $groupManager; - - /** - * @var ValidatorInterface - */ - private $validator; - - /** - * @var ExecutionContext - */ - private $currentContext; - - /** - * @var \SplStack|ExecutionContext[] - */ - private $contextStack; - - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * @var string|null - */ - private $translationDomain; - - /** - * Creates a new context manager. - * - * @param GroupManagerInterface $groupManager The manager for accessing - * the currently validated - * group - * @param TranslatorInterface $translator The translator - * @param string|null $translationDomain The translation domain to - * use for translating - * violation messages - */ - public function __construct(GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) - { - $this->groupManager = $groupManager; - $this->translator = $translator; - $this->translationDomain = $translationDomain; - $this->contextStack = new \SplStack(); - } - - /** - * Initializes the manager with a validator. - * - * @param ValidatorInterface $validator The validator - */ - public function initialize(ValidatorInterface $validator) - { - $this->validator = $validator; - } - - /** - * {@inheritdoc} - * - * @throws RuntimeException If {@link initialize()} wasn't called - */ - public function startContext($root) - { - if (null === $this->validator) { - throw new RuntimeException( - 'initialize() must be called before startContext().' - ); - } - - $this->currentContext = $this->createContext( - $root, - $this->validator, - $this->groupManager, - $this->translator, - $this->translationDomain - ); - - $this->contextStack->push($this->currentContext); - - return $this->currentContext; - } - - /** - * {@inheritdoc} - * - * @throws RuntimeException If {@link startContext()} wasn't called - */ - public function stopContext() - { - if (0 === count($this->contextStack)) { - throw new RuntimeException( - 'No context was started yet. Call startContext() before '. - 'stopContext().' - ); - } - - // Remove the current context from the stack - $stoppedContext = $this->contextStack->pop(); - - // Adjust the current context to the previous context - $this->currentContext = count($this->contextStack) > 0 - ? $this->contextStack->top() - : null; - - return $stoppedContext; - } - - /** - * {@inheritdoc} - */ - public function getCurrentContext() - { - return $this->currentContext; - } - - /** - * {@inheritdoc} - * - * @throws RuntimeException If {@link initialize()} wasn't called - */ - public function enterNode(Node $node) - { - if (null === $this->currentContext) { - throw new RuntimeException( - 'No context was started yet. Call startContext() before '. - 'enterNode().' - ); - } - - $this->currentContext->pushNode($node); - } - - /** - * {@inheritdoc} - * - * @throws RuntimeException If {@link initialize()} wasn't called - */ - public function leaveNode(Node $node) - { - if (null === $this->currentContext) { - throw new RuntimeException( - 'No context was started yet. Call startContext() before '. - 'leaveNode().' - ); - } - - $this->currentContext->popNode(); - } - - /** - * Creates a new context. - * - * Can be overridden by subclasses. - * - * @param mixed $root The root value of the - * validated object graph - * @param ValidatorInterface $validator The validator - * @param GroupManagerInterface $groupManager The manager for accessing - * the currently validated - * group - * @param TranslatorInterface $translator The translator - * @param string|null $translationDomain The translation domain to - * use for translating - * violation messages - * - * @return ExecutionContextInterface The created context - */ - protected function createContext($root, ValidatorInterface $validator, GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain) - { - return new ExecutionContext($root, $validator, $groupManager, $translator, $translationDomain); - } -} diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextManagerInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextManagerInterface.php deleted file mode 100644 index a2dd04d7dc..0000000000 --- a/src/Symfony/Component/Validator/Context/ExecutionContextManagerInterface.php +++ /dev/null @@ -1,86 +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\Context; - -/** - * Manages the creation and deletion of {@link ExecutionContextInterface} - * instances. - * - * Start a new context with {@link startContext()}. You can retrieve the context - * with {@link getCurrentContext()} and stop it again with {@link stopContext()}. - * - * $contextManager->startContext(); - * $context = $contextManager->getCurrentContext(); - * $contextManager->stopContext(); - * - * You can also start several nested contexts. The {@link getCurrentContext()} - * method will always return the most recently started context. - * - * // Start context 1 - * $contextManager->startContext(); - * - * // Start context 2 - * $contextManager->startContext(); - * - * // Returns context 2 - * $context = $contextManager->getCurrentContext(); - * - * // Stop context 2 - * $contextManager->stopContext(); - * - * // Returns context 1 - * $context = $contextManager->getCurrentContext(); - * - * See also {@link ExecutionContextInterface} for more information. - * - * @since 2.5 - * @author Bernhard Schussek - * - * @see ExecutionContextInterface - */ -interface ExecutionContextManagerInterface -{ - /** - * Starts a new context. - * - * The newly started context is returned. You can subsequently access the - * context with {@link getCurrentContext()}. - * - * @param mixed $root The root value of the object graph in the new context - * - * @return ExecutionContextInterface The started context - */ - public function startContext($root); - - /** - * Stops the current context. - * - * If multiple contexts have been started, the most recently started context - * is stopped. The stopped context is returned from this method. - * - * After calling this method, {@link getCurrentContext()} will return the - * context that was started before the stopped context. - * - * @return ExecutionContextInterface The stopped context - */ - public function stopContext(); - - /** - * Returns the current context. - * - * If multiple contexts have been started, the current context refers to the - * most recently started context. - * - * @return ExecutionContextInterface The current context - */ - public function getCurrentContext(); -} diff --git a/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php b/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php index 685f06931b..2d85ab7905 100644 --- a/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php @@ -39,8 +39,11 @@ class LegacyExecutionContext extends ExecutionContext implements LegacyExecution * it does not, an {@link InvalidArgumentException} is thrown. * * @see ExecutionContext::__construct() + * + * @internal Called by {@link LegacyExecutionContextFactory}. Should not be used + * in user code. */ - public function __construct($root, ValidatorInterface $validator, GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) + public function __construct(ValidatorInterface $validator, $root, GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) { if (!$validator instanceof LegacyValidatorInterface) { throw new InvalidArgumentException( @@ -49,7 +52,13 @@ class LegacyExecutionContext extends ExecutionContext implements LegacyExecution ); } - parent::__construct($root, $validator, $groupManager, $translator, $translationDomain); + parent::__construct( + $validator, + $root, + $groupManager, + $translator, + $translationDomain + ); } /** diff --git a/src/Symfony/Component/Validator/Context/LegacyExecutionContextFactory.php b/src/Symfony/Component/Validator/Context/LegacyExecutionContextFactory.php new file mode 100644 index 0000000000..181b4f47fd --- /dev/null +++ b/src/Symfony/Component/Validator/Context/LegacyExecutionContextFactory.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Context; + +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Validator\Group\GroupManagerInterface; +use Symfony\Component\Validator\Validator\ValidatorInterface; + +/** + * Creates new {@link LegacyExecutionContext} instances. + * + * @since 2.5 + * @author Bernhard Schussek + * + * @deprecated Implemented for backwards compatibility with Symfony < 2.5. To be + * removed in 3.0. + */ +class LegacyExecutionContextFactory implements ExecutionContextFactoryInterface +{ + /** + * @var GroupManagerInterface + */ + private $groupManager; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var string|null + */ + private $translationDomain; + + /** + * Creates a new context factory. + * + * @param GroupManagerInterface $groupManager The manager for accessing + * the currently validated + * group + * @param TranslatorInterface $translator The translator + * @param string|null $translationDomain The translation domain to + * use for translating + * violation messages + */ + public function __construct(GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain = null) + { + $this->groupManager = $groupManager; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + /** + * {@inheritdoc} + */ + public function createContext(ValidatorInterface $validator, $root) + { + return new LegacyExecutionContext( + $validator, + $root, + $this->groupManager, + $this->translator, + $this->translationDomain + ); + } +} diff --git a/src/Symfony/Component/Validator/Context/LegacyExecutionContextManager.php b/src/Symfony/Component/Validator/Context/LegacyExecutionContextManager.php deleted file mode 100644 index 361445bbfc..0000000000 --- a/src/Symfony/Component/Validator/Context/LegacyExecutionContextManager.php +++ /dev/null @@ -1,36 +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\Context; - -use Symfony\Component\Translation\TranslatorInterface; -use Symfony\Component\Validator\Group\GroupManagerInterface; -use Symfony\Component\Validator\Validator\ValidatorInterface; - -/** - * A context manager that creates contexts compatible to the API < Symfony 2.5. - * - * @since 2.5 - * @author Bernhard Schussek - * - * @see ExecutionContextManagerInterface - * @see \Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface - */ -class LegacyExecutionContextManager extends ExecutionContextManager -{ - /** - * {@inheritdoc} - */ - protected function createContext($root, ValidatorInterface $validator, GroupManagerInterface $groupManager, TranslatorInterface $translator, $translationDomain) - { - return new LegacyExecutionContext($root, $validator, $groupManager, $translator, $translationDomain); - } -} diff --git a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php index eacfd85c2c..9d6f4d6394 100644 --- a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php +++ b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\NodeTraverser; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\NoSuchMetadataException; use Symfony\Component\Validator\Mapping\CascadingStrategy; @@ -59,7 +60,7 @@ class NodeTraverser implements NodeTraverserInterface /** * {@inheritdoc} */ - public function traverse(array $nodes) + public function traverse(array $nodes, ExecutionContextInterface $context) { $isTopLevelCall = !$this->traversalStarted; @@ -68,39 +69,34 @@ class NodeTraverser implements NodeTraverserInterface foreach ($this->visitors as $visitor) { /** @var NodeVisitorInterface $visitor */ - $visitor->beforeTraversal($nodes); + $visitor->beforeTraversal($nodes, $context); } } foreach ($nodes as $node) { if ($node instanceof ClassNode) { - $this->traverseClassNode($node); + $this->traverseClassNode($node, $context); } else { - $this->traverseNode($node); + $this->traverseNode($node, $context); } } if ($isTopLevelCall) { foreach ($this->visitors as $visitor) { /** @var NodeVisitorInterface $visitor */ - $visitor->afterTraversal($nodes); + $visitor->afterTraversal($nodes, $context); } $this->traversalStarted = false; } } - /** - * @param Node $node - * - * @return Boolean - */ - private function enterNode(Node $node) + private function enterNode(Node $node, ExecutionContextInterface $context) { $continueTraversal = true; foreach ($this->visitors as $visitor) { - if (false === $visitor->enterNode($node)) { + if (false === $visitor->enterNode($node, $context)) { $continueTraversal = false; // Continue, so that the enterNode() method of all visitors @@ -111,19 +107,16 @@ class NodeTraverser implements NodeTraverserInterface return $continueTraversal; } - /** - * @param Node $node - */ - private function leaveNode(Node $node) + private function leaveNode(Node $node, ExecutionContextInterface $context) { foreach ($this->visitors as $visitor) { - $visitor->leaveNode($node); + $visitor->leaveNode($node, $context); } } - private function traverseNode(Node $node) + private function traverseNode(Node $node, ExecutionContextInterface $context) { - $continue = $this->enterNode($node); + $continue = $this->enterNode($node, $context); // Visitors have two possibilities to influence the traversal: // @@ -133,13 +126,13 @@ class NodeTraverser implements NodeTraverserInterface // that group will be skipped in the subtree of that node. if (false === $continue) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } if (null === $node->value) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } @@ -151,7 +144,7 @@ class NodeTraverser implements NodeTraverserInterface : $node->groups; if (0 === count($cascadedGroups)) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } @@ -166,11 +159,12 @@ class NodeTraverser implements NodeTraverserInterface $this->cascadeEachObjectIn( $node->value, $node->propertyPath, - $node->cascadedGroups, - $traversalStrategy + $cascadedGroups, + $traversalStrategy, + $context ); - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } @@ -182,25 +176,26 @@ class NodeTraverser implements NodeTraverserInterface $this->cascadeObject( $node->value, $node->propertyPath, - $node->cascadedGroups, - $traversalStrategy + $cascadedGroups, + $traversalStrategy, + $context ); - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } // Traverse only if the TRAVERSE bit is set if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } if (!$node->value instanceof \Traversable) { if ($traversalStrategy & TraversalStrategy::IGNORE_NON_TRAVERSABLE) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } @@ -215,16 +210,17 @@ class NodeTraverser implements NodeTraverserInterface $this->cascadeEachObjectIn( $node->value, $node->propertyPath, - $node->groups, - $traversalStrategy + $cascadedGroups, + $traversalStrategy, + $context ); - $this->leaveNode($node); + $this->leaveNode($node, $context); } - private function traverseClassNode(ClassNode $node, $traversalStrategy = TraversalStrategy::IMPLICIT) + private function traverseClassNode(ClassNode $node, ExecutionContextInterface $context, $traversalStrategy = TraversalStrategy::IMPLICIT) { - $continue = $this->enterNode($node); + $continue = $this->enterNode($node, $context); // Visitors have two possibilities to influence the traversal: // @@ -234,28 +230,30 @@ class NodeTraverser implements NodeTraverserInterface // that group will be skipped in the subtree of that node. if (false === $continue) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } if (0 === count($node->groups)) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } foreach ($node->metadata->getConstrainedProperties() as $propertyName) { foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) { - $this->traverseNode(new PropertyNode( - $propertyMetadata->getPropertyValue($node->value), - $propertyMetadata, - $node->propertyPath - ? $node->propertyPath.'.'.$propertyName - : $propertyName, - $node->groups, - $node->cascadedGroups - )); + $propertyNode = new PropertyNode( + $propertyMetadata->getPropertyValue($node->value), + $propertyMetadata, + $node->propertyPath + ? $node->propertyPath.'.'.$propertyName + : $propertyName, + $node->groups, + $node->cascadedGroups + ); + + $this->traverseNode($propertyNode, $context); } } @@ -267,14 +265,14 @@ class NodeTraverser implements NodeTraverserInterface // Traverse only if the TRAVERSE bit is set if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } if (!$node->value instanceof \Traversable) { if ($traversalStrategy & TraversalStrategy::IGNORE_NON_TRAVERSABLE) { - $this->leaveNode($node); + $this->leaveNode($node, $context); return; } @@ -290,13 +288,14 @@ class NodeTraverser implements NodeTraverserInterface $node->value, $node->propertyPath, $node->groups, - $traversalStrategy + $traversalStrategy, + $context ); - $this->leaveNode($node); + $this->leaveNode($node, $context); } - private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy) + private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy, ExecutionContextInterface $context) { try { $classMetadata = $this->metadataFactory->getMetadataFor($object); @@ -312,7 +311,7 @@ class NodeTraverser implements NodeTraverserInterface $groups ); - $this->traverseClassNode($classNode, $traversalStrategy); + $this->traverseClassNode($classNode, $context, $traversalStrategy); } catch (NoSuchMetadataException $e) { // Rethrow if the TRAVERSE bit is not set if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) { @@ -329,12 +328,13 @@ class NodeTraverser implements NodeTraverserInterface $object, $propertyPath, $groups, - $traversalStrategy + $traversalStrategy, + $context ); } } - private function cascadeEachObjectIn($collection, $propertyPath, array $groups, $traversalStrategy) + private function cascadeEachObjectIn($collection, $propertyPath, array $groups, $traversalStrategy, ExecutionContextInterface $context) { if ($traversalStrategy & TraversalStrategy::RECURSIVE) { // Try to traverse nested objects, but ignore if they do not @@ -356,7 +356,8 @@ class NodeTraverser implements NodeTraverserInterface $value, $propertyPath.'['.$key.']', $groups, - $traversalStrategy + $traversalStrategy, + $context ); continue; @@ -369,7 +370,8 @@ class NodeTraverser implements NodeTraverserInterface $value, $propertyPath.'['.$key.']', $groups, - $traversalStrategy + $traversalStrategy, + $context ); } } diff --git a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php index d9ce42f025..6084403d9b 100644 --- a/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php +++ b/src/Symfony/Component/Validator/NodeTraverser/NodeTraverserInterface.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Validator\NodeTraverser; -use Symfony\Component\Validator\Node\Node; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface; /** @@ -24,8 +24,5 @@ interface NodeTraverserInterface public function removeVisitor(NodeVisitorInterface $visitor); - /** - * @param Node[] $nodes - */ - public function traverse(array $nodes); + public function traverse(array $nodes, ExecutionContextInterface $context); } diff --git a/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php b/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php index 31b49250eb..47631556f0 100644 --- a/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php +++ b/src/Symfony/Component/Validator/NodeVisitor/AbstractVisitor.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\NodeVisitor; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Node\Node; /** @@ -19,19 +20,19 @@ use Symfony\Component\Validator\Node\Node; */ abstract class AbstractVisitor implements NodeVisitorInterface { - public function beforeTraversal(array $nodes) + public function beforeTraversal(array $nodes, ExecutionContextInterface $context) { } - public function afterTraversal(array $nodes) + public function afterTraversal(array $nodes, ExecutionContextInterface $context) { } - public function enterNode(Node $node) + public function enterNode(Node $node, ExecutionContextInterface $context) { } - public function leaveNode(Node $node) + public function leaveNode(Node $node, ExecutionContextInterface $context) { } } diff --git a/src/Symfony/Component/Validator/NodeVisitor/ContextRefresher.php b/src/Symfony/Component/Validator/NodeVisitor/ContextRefresher.php new file mode 100644 index 0000000000..e647e7c1ad --- /dev/null +++ b/src/Symfony/Component/Validator/NodeVisitor/ContextRefresher.php @@ -0,0 +1,60 @@ + + * + * 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\ExecutionContext; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Exception\RuntimeException; +use Symfony\Component\Validator\Node\Node; + +/** + * Updates the current context with the current node of the validation + * traversal. + * + * @since 2.5 + * @author Bernhard Schussek + */ +class ContextRefresher extends AbstractVisitor +{ + public function enterNode(Node $node, ExecutionContextInterface $context) + { + if (!$context instanceof ExecutionContext) { + throw new RuntimeException(sprintf( + 'The ContextRefresher only supports instances of class '. + '"Symfony\Component\Validator\Context\ExecutionContext". '. + 'An instance of class "%s" was given.', + get_class($context) + )); + } + + $context->pushNode($node); + } + + /** + * {@inheritdoc} + * + * @throws RuntimeException If {@link initialize()} wasn't called + */ + public function leaveNode(Node $node, ExecutionContextInterface $context) + { + if (!$context instanceof ExecutionContext) { + throw new RuntimeException(sprintf( + 'The ContextRefresher only supports instances of class '. + '"Symfony\Component\Validator\Context\ExecutionContext". '. + 'An instance of class "%s" was given.', + get_class($context) + )); + } + + $context->popNode(); + } +} diff --git a/src/Symfony/Component/Validator/NodeVisitor/GroupSequenceResolver.php b/src/Symfony/Component/Validator/NodeVisitor/GroupSequenceResolver.php index 868d6fc428..b5887ba4b0 100644 --- a/src/Symfony/Component/Validator/NodeVisitor/GroupSequenceResolver.php +++ b/src/Symfony/Component/Validator/NodeVisitor/GroupSequenceResolver.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Validator\NodeVisitor; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GroupSequence; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Node\ClassNode; use Symfony\Component\Validator\Node\Node; @@ -22,7 +23,7 @@ use Symfony\Component\Validator\Node\Node; */ class GroupSequenceResolver extends AbstractVisitor { - public function enterNode(Node $node) + public function enterNode(Node $node, ExecutionContextInterface $context) { if (!$node instanceof ClassNode) { return; diff --git a/src/Symfony/Component/Validator/NodeVisitor/NodeValidator.php b/src/Symfony/Component/Validator/NodeVisitor/NodeValidator.php index 6ee8b7f826..e410cb259e 100644 --- a/src/Symfony/Component/Validator/NodeVisitor/NodeValidator.php +++ b/src/Symfony/Component/Validator/NodeVisitor/NodeValidator.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Validator\NodeVisitor; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; -use Symfony\Component\Validator\Context\ExecutionContextManagerInterface; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Group\GroupManagerInterface; use Symfony\Component\Validator\Node\ClassNode; use Symfony\Component\Validator\Node\Node; @@ -35,11 +35,6 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface */ private $validatorFactory; - /** - * @var ExecutionContextManagerInterface - */ - private $contextManager; - /** * @var NodeTraverserInterface */ @@ -56,19 +51,14 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface $this->objectHashStack = new \SplStack(); } - public function initialize(ExecutionContextManagerInterface $contextManager) - { - $this->contextManager = $contextManager; - } - - public function afterTraversal(array $nodes) + public function afterTraversal(array $nodes, ExecutionContextInterface $context) { $this->validatedObjects = array(); $this->validatedConstraints = array(); $this->objectHashStack = new \SplStack(); } - public function enterNode(Node $node) + public function enterNode(Node $node, ExecutionContextInterface $context) { if ($node instanceof ClassNode) { $objectHash = spl_object_hash($node->value); @@ -105,7 +95,7 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface // Validate normal group if (!$group instanceof GroupSequence) { - $this->validateNodeForGroup($objectHash, $node, $group); + $this->validateNodeForGroup($objectHash, $node, $group, $context); continue; } @@ -114,7 +104,7 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface unset($node->groups[$key]); // Traverse group sequence until a violation is generated - $this->traverseGroupSequence($node, $group); + $this->traverseGroupSequence($node, $group, $context); // Optimization: If the groups only contain the group sequence, // we can skip the traversal for the properties of the object @@ -126,7 +116,7 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface return true; } - public function leaveNode(Node $node) + public function leaveNode(Node $node, ExecutionContextInterface $context) { if ($node instanceof ClassNode) { $this->objectHashStack->pop(); @@ -138,9 +128,8 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface return $this->currentGroup; } - private function traverseGroupSequence(Node $node, GroupSequence $groupSequence) + private function traverseGroupSequence(Node $node, GroupSequence $groupSequence, ExecutionContextInterface $context) { - $context = $this->contextManager->getCurrentContext(); $violationCount = count($context->getViolations()); foreach ($groupSequence->groups as $groupInSequence) { @@ -151,7 +140,7 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface $node->cascadedGroups = array($groupSequence->cascadedGroup); } - $this->nodeTraverser->traverse(array($node)); + $this->nodeTraverser->traverse(array($node), $context); // Abort sequence validation if a violation was generated if (count($context->getViolations()) > $violationCount) { @@ -160,14 +149,7 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface } } - /** - * @param $objectHash - * @param Node $node - * @param $group - * - * @throws \Exception - */ - private function validateNodeForGroup($objectHash, Node $node, $group) + private function validateNodeForGroup($objectHash, Node $node, $group, ExecutionContextInterface $context) { try { $this->currentGroup = $group; @@ -187,7 +169,7 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface } $validator = $this->validatorFactory->getInstance($constraint); - $validator->initialize($this->contextManager->getCurrentContext()); + $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 index a7542d515a..6736cc23ba 100644 --- a/src/Symfony/Component/Validator/NodeVisitor/NodeVisitorInterface.php +++ b/src/Symfony/Component/Validator/NodeVisitor/NodeVisitorInterface.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\NodeVisitor; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Node\Node; /** @@ -19,11 +20,11 @@ use Symfony\Component\Validator\Node\Node; */ interface NodeVisitorInterface { - public function beforeTraversal(array $nodes); + public function beforeTraversal(array $nodes, ExecutionContextInterface $context); - public function afterTraversal(array $nodes); + public function afterTraversal(array $nodes, ExecutionContextInterface $context); - public function enterNode(Node $node); + public function enterNode(Node $node, ExecutionContextInterface $context); - public function leaveNode(Node $node); + public function leaveNode(Node $node, ExecutionContextInterface $context); } diff --git a/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializer.php b/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializer.php index bd000366c5..8d44d39ee0 100644 --- a/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializer.php +++ b/src/Symfony/Component/Validator/NodeVisitor/ObjectInitializer.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\NodeVisitor; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Node\ClassNode; use Symfony\Component\Validator\Node\Node; use Symfony\Component\Validator\ObjectInitializerInterface; @@ -42,7 +43,7 @@ class ObjectInitializer extends AbstractVisitor $this->initializers = $initializers; } - public function enterNode(Node $node) + public function enterNode(Node $node, ExecutionContextInterface $context) { if (!$node instanceof ClassNode) { return; diff --git a/src/Symfony/Component/Validator/Tests/Context/ExecutionContextManagerTest.php b/src/Symfony/Component/Validator/Tests/Context/ExecutionContextManagerTest.php deleted file mode 100644 index 548a11170d..0000000000 --- a/src/Symfony/Component/Validator/Tests/Context/ExecutionContextManagerTest.php +++ /dev/null @@ -1,95 +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\Context; - -use Symfony\Component\Validator\Context\ExecutionContextManager; - -/** - * @since 2.5 - * @author Bernhard Schussek - */ -class ExecutionContextManagerTest extends \PHPUnit_Framework_TestCase -{ - const TRANSLATION_DOMAIN = '__TRANSLATION_DOMAIN__'; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $validator; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $groupManager; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $translator; - - /** - * @var ExecutionContextManager - */ - private $contextManager; - - protected function setUp() - { - $this->validator = $this->getMock('Symfony\Component\Validator\Validator\ValidatorInterface'); - $this->groupManager = $this->getMock('Symfony\Component\Validator\Group\GroupManagerInterface'); - $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); - $this->contextManager = new ExecutionContextManager( - $this->groupManager, - $this->translator, - self::TRANSLATION_DOMAIN - ); - } - - /** - * @expectedException \Symfony\Component\Validator\Exception\RuntimeException - */ - public function testInitializeMustBeCalledBeforeStartContext() - { - $this->contextManager->startContext('root'); - } - - /** - * @expectedException \Symfony\Component\Validator\Exception\RuntimeException - */ - public function testCannotStopContextIfNoneWasStarted() - { - $this->contextManager->stopContext(); - } - - /** - * @expectedException \Symfony\Component\Validator\Exception\RuntimeException - */ - public function testCannotEnterNodeWithoutActiveContext() - { - $node = $this->getMockBuilder('Symfony\Component\Validator\Node\Node') - ->disableOriginalConstructor() - ->getMock(); - - $this->contextManager->enterNode($node); - } - - /** - * @expectedException \Symfony\Component\Validator\Exception\RuntimeException - */ - public function testCannotLeaveNodeWithoutActiveContext() - { - $node = $this->getMockBuilder('Symfony\Component\Validator\Node\Node') - ->disableOriginalConstructor() - ->getMock(); - - $this->contextManager->leaveNode($node); - } -} diff --git a/src/Symfony/Component/Validator/Tests/Context/ExecutionContextTest.php b/src/Symfony/Component/Validator/Tests/Context/ExecutionContextTest.php index 1133f413a7..8019aecea1 100644 --- a/src/Symfony/Component/Validator/Tests/Context/ExecutionContextTest.php +++ b/src/Symfony/Component/Validator/Tests/Context/ExecutionContextTest.php @@ -51,11 +51,7 @@ class ExecutionContextTest extends \PHPUnit_Framework_TestCase $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); $this->context = new ExecutionContext( - self::ROOT, - $this->validator, - $this->groupManager, - $this->translator, - self::TRANSLATION_DOMAIN + $this->validator, self::ROOT, $this->groupManager, $this->translator, self::TRANSLATION_DOMAIN ); } diff --git a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php index 48d8f07141..ad529f3750 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php @@ -87,7 +87,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest 'groups' => array('Group 1', 'Group 2'), ))); - $violations = $this->validateObject($entity, array('Group 1', 'Group 2')); + $violations = $this->validator->validateObject($entity, array('Group 1', 'Group 2')); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); @@ -118,7 +118,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest ))); $sequence = new GroupSequence(array('Group 1', 'Group 2', 'Group 3')); - $violations = $this->validateObject($entity, $sequence); + $violations = $this->validator->validateObject($entity, $sequence); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); @@ -148,7 +148,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest ))); $sequence = new GroupSequence(array('Group 1', 'Entity')); - $violations = $this->validateObject($entity, $sequence); + $violations = $this->validator->validateObject($entity, $sequence); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); @@ -207,7 +207,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest 'groups' => 'Group', ))); - $violations = $this->validateObject($entity, 'Group'); + $violations = $this->validator->validateObject($entity, 'Group'); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); @@ -251,7 +251,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest 'groups' => 'Group', ))); - $violations = $this->validateObject($entity, 'Group'); + $violations = $this->validator->validateObject($entity, 'Group'); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); @@ -302,7 +302,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest 'groups' => 'Group', ))); - $violations = $this->validateObject($entity, 'Group'); + $violations = $this->validator->validateObject($entity, 'Group'); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); @@ -361,7 +361,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest { $entity = new Entity(); - $this->validate($entity, new Traverse()); + $this->validator->validate($entity, new Traverse()); } /** @@ -373,7 +373,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest $this->metadata->addConstraint(new Traverse()); - $this->validateObject($entity); + $this->validator->validateObject($entity); } public function testAddCustomizedViolation() @@ -391,7 +391,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest $this->metadata->addConstraint(new Callback($callback)); - $violations = $this->validateObject($entity); + $violations = $this->validator->validateObject($entity); /** @var ConstraintViolationInterface[] $violations */ $this->assertCount(1, $violations); diff --git a/src/Symfony/Component/Validator/Tests/Validator/LegacyValidator2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/LegacyValidator2Dot5ApiTest.php index e3715731ce..1cecafdedf 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/LegacyValidator2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/LegacyValidator2Dot5ApiTest.php @@ -13,8 +13,9 @@ namespace Symfony\Component\Validator\Tests\Validator; use Symfony\Component\Validator\DefaultTranslator; use Symfony\Component\Validator\ConstraintValidatorFactory; -use Symfony\Component\Validator\Context\LegacyExecutionContextManager; +use Symfony\Component\Validator\Context\LegacyExecutionContextFactory; use Symfony\Component\Validator\MetadataFactoryInterface; +use Symfony\Component\Validator\NodeVisitor\ContextRefresher; use Symfony\Component\Validator\NodeVisitor\GroupSequenceResolver; use Symfony\Component\Validator\NodeVisitor\NodeValidator; use Symfony\Component\Validator\NodeTraverser\NodeTraverser; @@ -26,20 +27,13 @@ class LegacyValidator2Dot5ApiTest extends Abstract2Dot5ApiTest { $nodeTraverser = new NodeTraverser($metadataFactory); $nodeValidator = new NodeValidator($nodeTraverser, new ConstraintValidatorFactory()); - $contextManager = new LegacyExecutionContextManager($nodeValidator, new DefaultTranslator()); - $validator = new LegacyValidator($nodeTraverser, $metadataFactory, $contextManager); + $contextFactory = new LegacyExecutionContextFactory($nodeValidator, new DefaultTranslator()); + $validator = new LegacyValidator($contextFactory, $nodeTraverser, $metadataFactory); $groupSequenceResolver = new GroupSequenceResolver(); - - // The context manager needs the validator for passing it to created - // contexts - $contextManager->initialize($validator); - - // The node validator needs the context manager for passing the current - // context to the constraint validators - $nodeValidator->initialize($contextManager); + $contextRefresher = new ContextRefresher(); $nodeTraverser->addVisitor($groupSequenceResolver); - $nodeTraverser->addVisitor($contextManager); + $nodeTraverser->addVisitor($contextRefresher); $nodeTraverser->addVisitor($nodeValidator); return $validator; diff --git a/src/Symfony/Component/Validator/Tests/Validator/LegacyValidatorLegacyApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/LegacyValidatorLegacyApiTest.php index c106d765c1..94900e4497 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/LegacyValidatorLegacyApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/LegacyValidatorLegacyApiTest.php @@ -13,8 +13,9 @@ namespace Symfony\Component\Validator\Tests\Validator; use Symfony\Component\Validator\DefaultTranslator; use Symfony\Component\Validator\ConstraintValidatorFactory; -use Symfony\Component\Validator\Context\LegacyExecutionContextManager; +use Symfony\Component\Validator\Context\LegacyExecutionContextFactory; use Symfony\Component\Validator\MetadataFactoryInterface; +use Symfony\Component\Validator\NodeVisitor\ContextRefresher; use Symfony\Component\Validator\NodeVisitor\GroupSequenceResolver; use Symfony\Component\Validator\NodeVisitor\NodeValidator; use Symfony\Component\Validator\NodeTraverser\NodeTraverser; @@ -26,20 +27,13 @@ class LegacyValidatorLegacyApiTest extends AbstractLegacyApiTest { $nodeTraverser = new NodeTraverser($metadataFactory); $nodeValidator = new NodeValidator($nodeTraverser, new ConstraintValidatorFactory()); - $contextManager = new LegacyExecutionContextManager($nodeValidator, new DefaultTranslator()); - $validator = new LegacyValidator($nodeTraverser, $metadataFactory, $contextManager); + $contextFactory = new LegacyExecutionContextFactory($nodeValidator, new DefaultTranslator()); + $validator = new LegacyValidator($contextFactory, $nodeTraverser, $metadataFactory); $groupSequenceResolver = new GroupSequenceResolver(); - - // The context manager needs the validator for passing it to created - // contexts - $contextManager->initialize($validator); - - // The node validator needs the context manager for passing the current - // context to the constraint validators - $nodeValidator->initialize($contextManager); + $contextRefresher = new ContextRefresher(); $nodeTraverser->addVisitor($groupSequenceResolver); - $nodeTraverser->addVisitor($contextManager); + $nodeTraverser->addVisitor($contextRefresher); $nodeTraverser->addVisitor($nodeValidator); return $validator; diff --git a/src/Symfony/Component/Validator/Tests/Validator/Validator2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/Validator2Dot5ApiTest.php index 2bf4693dde..4d58f0a13e 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/Validator2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/Validator2Dot5ApiTest.php @@ -13,8 +13,9 @@ namespace Symfony\Component\Validator\Tests\Validator; use Symfony\Component\Validator\DefaultTranslator; use Symfony\Component\Validator\ConstraintValidatorFactory; -use Symfony\Component\Validator\Context\ExecutionContextManager; +use Symfony\Component\Validator\Context\ExecutionContextFactory; use Symfony\Component\Validator\MetadataFactoryInterface; +use Symfony\Component\Validator\NodeVisitor\ContextRefresher; use Symfony\Component\Validator\NodeVisitor\GroupSequenceResolver; use Symfony\Component\Validator\NodeVisitor\NodeValidator; use Symfony\Component\Validator\NodeTraverser\NodeTraverser; @@ -26,20 +27,13 @@ class Validator2Dot5ApiTest extends Abstract2Dot5ApiTest { $nodeTraverser = new NodeTraverser($metadataFactory); $nodeValidator = new NodeValidator($nodeTraverser, new ConstraintValidatorFactory()); - $contextManager = new ExecutionContextManager($nodeValidator, new DefaultTranslator()); - $validator = new Validator($nodeTraverser, $metadataFactory, $contextManager); + $contextFactory = new ExecutionContextFactory($nodeValidator, new DefaultTranslator()); + $validator = new Validator($contextFactory, $nodeTraverser, $metadataFactory); $groupSequenceResolver = new GroupSequenceResolver(); - - // The context manager needs the validator for passing it to created - // contexts - $contextManager->initialize($validator); - - // The node validator needs the context manager for passing the current - // context to the constraint validators - $nodeValidator->initialize($contextManager); + $contextRefresher = new ContextRefresher(); $nodeTraverser->addVisitor($groupSequenceResolver); - $nodeTraverser->addVisitor($contextManager); + $nodeTraverser->addVisitor($contextRefresher); $nodeTraverser->addVisitor($nodeValidator); return $validator; diff --git a/src/Symfony/Component/Validator/Validator/AbstractValidator.php b/src/Symfony/Component/Validator/Validator/AbstractValidator.php deleted file mode 100644 index 8cd2767bd0..0000000000 --- a/src/Symfony/Component/Validator/Validator/AbstractValidator.php +++ /dev/null @@ -1,188 +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\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\PropertyNode; -use Symfony\Component\Validator\Node\GenericNode; -use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; -use Symfony\Component\Validator\Util\PropertyPath; - -/** - * @since %%NextVersion%% - * @author Bernhard Schussek - */ -abstract class AbstractValidator implements ValidatorInterface -{ - /** - * @var NodeTraverserInterface - */ - protected $nodeTraverser; - - /** - * @var MetadataFactoryInterface - */ - protected $metadataFactory; - - /** - * @var string - */ - protected $defaultPropertyPath = ''; - - protected $defaultGroups = array(Constraint::DEFAULT_GROUP); - - public function __construct(NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory) - { - $this->nodeTraverser = $nodeTraverser; - $this->metadataFactory = $metadataFactory; - } - - /** - * @param ExecutionContextInterface $context - * - * @return ContextualValidatorInterface - */ - public function inContext(ExecutionContextInterface $context) - { - return new ContextualValidator($this->nodeTraverser, $this->metadataFactory, $context); - } - - public function getMetadataFor($object) - { - return $this->metadataFactory->getMetadataFor($object); - } - - public function hasMetadataFor($object) - { - return $this->metadataFactory->hasMetadataFor($object); - } - - protected function traverse($value, $constraints, $groups = null) - { - if (!is_array($constraints)) { - $constraints = array($constraints); - } - - $metadata = new GenericMetadata(); - $metadata->addConstraints($constraints); - $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - - $this->nodeTraverser->traverse(array(new GenericNode( - $value, - $metadata, - $this->defaultPropertyPath, - $groups, - $groups - ))); - } - - protected function traverseObject($object, $groups = null) - { - $classMetadata = $this->metadataFactory->getMetadataFor($object); - - if (!$classMetadata instanceof ClassMetadataInterface) { - 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) - )); - } - - $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - - $this->nodeTraverser->traverse(array(new ClassNode( - $object, - $classMetadata, - $this->defaultPropertyPath, - $groups, - $groups - ))); - } - - protected function traverseProperty($object, $propertyName, $groups = null) - { - $classMetadata = $this->metadataFactory->getMetadataFor($object); - - if (!$classMetadata instanceof ClassMetadataInterface) { - 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; - $nodes = array(); - - foreach ($propertyMetadatas as $propertyMetadata) { - $propertyValue = $propertyMetadata->getPropertyValue($object); - - $nodes[] = new PropertyNode( - $propertyValue, - $propertyMetadata, - PropertyPath::append($this->defaultPropertyPath, $propertyName), - $groups, - $groups - ); - } - - $this->nodeTraverser->traverse($nodes); - } - - protected function traversePropertyValue($object, $propertyName, $value, $groups = null) - { - $classMetadata = $this->metadataFactory->getMetadataFor($object); - - if (!$classMetadata instanceof ClassMetadataInterface) { - 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; - $nodes = array(); - - foreach ($propertyMetadatas as $propertyMetadata) { - $nodes[] = new PropertyNode( - $value, - $propertyMetadata, - PropertyPath::append($this->defaultPropertyPath, $propertyName), - $groups, - $groups - ); - } - - $this->nodeTraverser->traverse($nodes); - } - - protected function normalizeGroups($groups) - { - if (is_array($groups)) { - return $groups; - } - - return array($groups); - } -} diff --git a/src/Symfony/Component/Validator/Validator/ContextualValidator.php b/src/Symfony/Component/Validator/Validator/ContextualValidator.php index b975ef4bde..cb9951bf77 100644 --- a/src/Symfony/Component/Validator/Validator/ContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/ContextualValidator.php @@ -13,30 +13,45 @@ namespace Symfony\Component\Validator\Validator; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Traverse; -use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Context\ExecutionContextManagerInterface; +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\GenericNode; +use Symfony\Component\Validator\Node\PropertyNode; use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; +use Symfony\Component\Validator\Util\PropertyPath; /** * @since %%NextVersion%% * @author Bernhard Schussek */ -class ContextualValidator extends AbstractValidator implements ContextualValidatorInterface +class ContextualValidator implements ContextualValidatorInterface { /** - * @var ExecutionContextManagerInterface + * @var ExecutionContextInterface */ private $context; - public function __construct(NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory, ExecutionContextInterface $context) - { - parent::__construct($nodeTraverser, $metadataFactory); + /** + * @var NodeTraverserInterface + */ + private $nodeTraverser; + /** + * @var MetadataFactoryInterface + */ + private $metadataFactory; + + public function __construct(ExecutionContextInterface $context, NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory) + { $this->context = $context; $this->defaultPropertyPath = $context->getPropertyPath(); - $this->defaultGroups = array($context->getGroup()); + $this->defaultGroups = array($context->getGroup() ?: Constraint::DEFAULT_GROUP); + $this->nodeTraverser = $nodeTraverser; + $this->metadataFactory = $metadataFactory; } public function atPath($subPath) @@ -46,40 +61,53 @@ class ContextualValidator extends AbstractValidator implements ContextualValidat return $this; } - /** - * Validates a value against a constraint or a list of constraints. - * - * @param mixed $value The value to validate. - * @param Constraint|Constraint[] $constraints The constraint(s) to validate against. - * @param array|null $groups The validation groups to validate. - * - * @return ConstraintViolationListInterface A list of constraint violations. If the - * list is empty, validation succeeded. - */ public function validate($value, $constraints, $groups = null) { - $this->traverse($value, $constraints, $groups); + if (!is_array($constraints)) { + $constraints = array($constraints); + } - return $this->context->getViolations(); + $metadata = new GenericMetadata(); + $metadata->addConstraints($constraints); + $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; + + $node = new GenericNode( + $value, + $metadata, + $this->defaultPropertyPath, + $groups + ); + + $this->nodeTraverser->traverse(array($node), $this->context); + + return $this; } - /** - * Validates a value. - * - * The accepted values depend on the {@link MetadataFactoryInterface} - * implementation. - * - * @param mixed $object The value to validate - * @param array|null $groups The validation groups to validate. - * - * @return ConstraintViolationListInterface A list of constraint violations. If the - * list is empty, validation succeeded. - */ public function validateObject($object, $groups = null) { - $this->traverseObject($object, $groups); + $classMetadata = $this->metadataFactory->getMetadataFor($object); - return $this->context->getViolations(); + if (!$classMetadata instanceof ClassMetadataInterface) { + 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) + )); + } + + $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; + + $node = new ClassNode( + $object, + $classMetadata, + $this->defaultPropertyPath, + $groups + ); + + $this->nodeTraverser->traverse(array($node), $this->context); + + return $this; } public function validateCollection($collection, $groups = null, $deep = false) @@ -89,50 +117,85 @@ class ContextualValidator extends AbstractValidator implements ContextualValidat 'deep' => $deep, )); - $this->traverse($collection, $constraint, $groups); - - return $this->context->getViolations(); + return $this->validate($collection, $constraint, $groups); } - /** - * Validates a property of a value against its current value. - * - * The accepted values depend on the {@link MetadataFactoryInterface} - * implementation. - * - * @param mixed $object The value containing the property. - * @param string $propertyName The name of the property to validate. - * @param array|null $groups The validation groups to validate. - * - * @return ConstraintViolationListInterface A list of constraint violations. If the - * list is empty, validation succeeded. - */ public function validateProperty($object, $propertyName, $groups = null) { - $this->traverseProperty($object, $propertyName, $groups); + $classMetadata = $this->metadataFactory->getMetadataFor($object); - return $this->context->getViolations(); + if (!$classMetadata instanceof ClassMetadataInterface) { + 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; + $nodes = array(); + + foreach ($propertyMetadatas as $propertyMetadata) { + $propertyValue = $propertyMetadata->getPropertyValue($object); + + $nodes[] = new PropertyNode( + $propertyValue, + $propertyMetadata, + PropertyPath::append($this->defaultPropertyPath, $propertyName), + $groups + ); + } + + $this->nodeTraverser->traverse($nodes, $this->context); + + return $this; } - /** - * Validate a property of a value against a potential value. - * - * The accepted values depend on the {@link MetadataFactoryInterface} - * implementation. - * - * @param string $object The value containing the property. - * @param string $propertyName The name of the property to validate - * @param string $value The value to validate against the - * constraints of the property. - * @param array|null $groups The validation groups to validate. - * - * @return ConstraintViolationListInterface A list of constraint violations. If the - * list is empty, validation succeeded. - */ public function validatePropertyValue($object, $propertyName, $value, $groups = null) { - $this->traversePropertyValue($object, $propertyName, $value, $groups); + $classMetadata = $this->metadataFactory->getMetadataFor($object); + if (!$classMetadata instanceof ClassMetadataInterface) { + 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; + $nodes = array(); + + foreach ($propertyMetadatas as $propertyMetadata) { + $nodes[] = new PropertyNode( + $value, + $propertyMetadata, + PropertyPath::append($this->defaultPropertyPath, $propertyName), + $groups, + $groups + ); + } + + $this->nodeTraverser->traverse($nodes, $this->context); + + return $this; + } + + protected function normalizeGroups($groups) + { + if (is_array($groups)) { + return $groups; + } + + return array($groups); + } + + public function getViolations() + { return $this->context->getViolations(); } } diff --git a/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php b/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php index 61de8900f1..69a224cb19 100644 --- a/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php +++ b/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php @@ -11,16 +11,80 @@ namespace Symfony\Component\Validator\Validator; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintViolationListInterface; + /** * @since %%NextVersion%% * @author Bernhard Schussek */ -interface ContextualValidatorInterface extends ValidatorInterface +interface ContextualValidatorInterface { /** - * @param $subPath + * @param string $subPath * - * @return ContextualValidatorInterface + * @return ContextualValidatorInterface This validator */ public function atPath($subPath); + + /** + * Validates a value against a constraint or a list of constraints. + * + * @param mixed $value The value to validate. + * @param Constraint|Constraint[] $constraints The constraint(s) to validate against. + * @param array|null $groups The validation groups to validate. + * + * @return ContextualValidatorInterface This validator + */ + public function validate($value, $constraints, $groups = null); + + /** + * Validates a value. + * + * The accepted values depend on the {@link MetadataFactoryInterface} + * implementation. + * + * @param mixed $object The value to validate + * @param array|null $groups The validation groups to validate. + * + * @return ContextualValidatorInterface This validator + */ + public function validateObject($object, $groups = null); + + public function validateCollection($collection, $groups = null, $deep = false); + + /** + * Validates a property of a value against its current value. + * + * The accepted values depend on the {@link MetadataFactoryInterface} + * implementation. + * + * @param mixed $object The value containing the property. + * @param string $propertyName The name of the property to validate. + * @param array|null $groups The validation groups to validate. + * + * @return ContextualValidatorInterface This validator + */ + public function validateProperty($object, $propertyName, $groups = null); + + /** + * Validate a property of a value against a potential value. + * + * The accepted values depend on the {@link MetadataFactoryInterface} + * implementation. + * + * @param string $object The value containing the property. + * @param string $propertyName The name of the property to validate + * @param string $value The value to validate against the + * constraints of the property. + * @param array|null $groups The validation groups to validate. + * + * @return ContextualValidatorInterface This validator + */ + public function validatePropertyValue($object, $propertyName, $value, $groups = null); + + /** + * @return ConstraintViolationListInterface + */ + public function getViolations(); } diff --git a/src/Symfony/Component/Validator/Validator/Validator.php b/src/Symfony/Component/Validator/Validator/Validator.php index 94857a3a1b..820b01a149 100644 --- a/src/Symfony/Component/Validator/Validator/Validator.php +++ b/src/Symfony/Component/Validator/Validator/Validator.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Validator\Validator; -use Symfony\Component\Validator\Constraints\Traverse; -use Symfony\Component\Validator\Context\ExecutionContextManagerInterface; +use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; use Symfony\Component\Validator\MetadataFactoryInterface; @@ -20,67 +20,102 @@ use Symfony\Component\Validator\MetadataFactoryInterface; * @since %%NextVersion%% * @author Bernhard Schussek */ -class Validator extends AbstractValidator +class Validator implements ValidatorInterface { /** - * @var ExecutionContextManagerInterface + * @var ExecutionContextFactoryInterface */ - protected $contextManager; + protected $contextFactory; - public function __construct(NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory, ExecutionContextManagerInterface $contextManager) + /** + * @var NodeTraverserInterface + */ + protected $nodeTraverser; + + /** + * @var MetadataFactoryInterface + */ + protected $metadataFactory; + + public function __construct(ExecutionContextFactoryInterface $contextFactory, NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory) { - parent::__construct($nodeTraverser, $metadataFactory); + $this->contextFactory = $contextFactory; + $this->nodeTraverser = $nodeTraverser; + $this->metadataFactory = $metadataFactory; + } - $this->contextManager = $contextManager; + /** + * {@inheritdoc} + */ + public function startContext($root = null) + { + return new ContextualValidator( + $this->contextFactory->createContext($this, $root), + $this->nodeTraverser, + $this->metadataFactory + ); + } + + /** + * {@inheritdoc} + */ + public function inContext(ExecutionContextInterface $context) + { + return new ContextualValidator( + $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); } public function validate($value, $constraints, $groups = null) { - $this->contextManager->startContext($value); - - $this->traverse($value, $constraints, $groups); - - return $this->contextManager->stopContext()->getViolations(); + return $this->startContext($value) + ->validate($value, $constraints, $groups) + ->getViolations(); } public function validateObject($object, $groups = null) { - $this->contextManager->startContext($object); - - $this->traverseObject($object, $groups); - - return $this->contextManager->stopContext()->getViolations(); + return $this->startContext($object) + ->validateObject($object, $groups) + ->getViolations(); } public function validateCollection($collection, $groups = null, $deep = false) { - $this->contextManager->startContext($collection); - - $constraint = new Traverse(array( - 'traverse' => true, - 'deep' => $deep, - )); - - $this->traverse($collection, $constraint, $groups); - - return $this->contextManager->stopContext()->getViolations(); + return $this->startContext($collection) + ->validateCollection($collection, $groups, $deep) + ->getViolations(); } public function validateProperty($object, $propertyName, $groups = null) { - $this->contextManager->startContext($object); - - $this->traverseProperty($object, $propertyName, $groups); - - return $this->contextManager->stopContext()->getViolations(); + return $this->startContext($object) + ->validateProperty($object, $propertyName, $groups) + ->getViolations(); } public function validatePropertyValue($object, $propertyName, $value, $groups = null) { - $this->contextManager->startContext($object); - - $this->traversePropertyValue($object, $propertyName, $value, $groups); - - return $this->contextManager->stopContext()->getViolations(); + return $this->startContext($object) + ->validatePropertyValue($object, $propertyName, $value, $groups) + ->getViolations(); } } diff --git a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php index 8cecaf3dc4..f69395284a 100644 --- a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php +++ b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php @@ -81,6 +81,11 @@ interface ValidatorInterface */ public function validatePropertyValue($object, $propertyName, $value, $groups = null); + /** + * @return ContextualValidatorInterface + */ + public function startContext(); + /** * @param ExecutionContextInterface $context *