2929 */
3030class NodeValidationVisitor extends AbstractVisitor implements GroupManagerInterface
3131{
32- /**
33- * Stores the hashes of each validated object together with the groups
34- * in which that object was already validated.
35- *
36- * @var array
37- */
38- private $ validatedObjects = array ();
39-
40- private $ validatedConstraints = array ();
41-
4232 /**
4333 * @var ConstraintValidatorFactoryInterface
4434 */
@@ -49,20 +39,37 @@ class NodeValidationVisitor extends AbstractVisitor implements GroupManagerInter
4939 */
5040 private $ nodeTraverser ;
5141
42+ /**
43+ * The currently validated group.
44+ *
45+ * @var string
46+ */
5247 private $ currentGroup ;
5348
49+ /**
50+ * Creates a new visitor.
51+ *
52+ * @param NodeTraverserInterface $nodeTraverser The node traverser
53+ * @param ConstraintValidatorFactoryInterface $validatorFactory The validator factory
54+ */
5455 public function __construct (NodeTraverserInterface $ nodeTraverser , ConstraintValidatorFactoryInterface $ validatorFactory )
5556 {
5657 $ this ->validatorFactory = $ validatorFactory ;
5758 $ this ->nodeTraverser = $ nodeTraverser ;
5859 }
5960
60- public function afterTraversal (array $ nodes , ExecutionContextInterface $ context )
61- {
62- $ this ->validatedObjects = array ();
63- $ this ->validatedConstraints = array ();
64- }
65-
61+ /**
62+ * Validates a node's value against the constraints defined in the node's
63+ * metadata.
64+ *
65+ * Objects and constraints that were validated before in the same context
66+ * will be skipped.
67+ *
68+ * @param Node $node The current node
69+ * @param ExecutionContextInterface $context The execution context
70+ *
71+ * @return Boolean Whether to traverse the successor nodes
72+ */
6673 public function visit (Node $ node , ExecutionContextInterface $ context )
6774 {
6875 if ($ node instanceof CollectionNode) {
@@ -84,21 +91,24 @@ public function visit(Node $node, ExecutionContextInterface $context)
8491 // simply continue traversal (if possible)
8592
8693 foreach ($ node ->groups as $ key => $ group ) {
87- // Remember which object was validated for which group
88- // Skip validation if the object was already validated for this
89- // group
94+ // Even if we remove the following clause, the constraints on an
95+ // object won't be validated again due to the measures taken in
96+ // validateNodeForGroup().
97+ // The following shortcut, however, prevents validatedNodeForGroup()
98+ // from being called at all and enhances performance a bit.
9099 if ($ node instanceof ClassNode) {
91100 // Use the object hash for group sequences
92101 $ groupHash = is_object ($ group ) ? spl_object_hash ($ group ) : $ group ;
93102
94103 if ($ context ->isObjectValidatedForGroup ($ objectHash , $ groupHash )) {
95- // Skip this group when validating properties
104+ // Skip this group when validating the successor nodes
105+ // (property and/or collection nodes)
96106 unset($ node ->groups [$ key ]);
97107
98108 continue ;
99109 }
100110
101- // $context->markObjectAsValidatedForGroup($objectHash, $groupHash);
111+ $ context ->markObjectAsValidatedForGroup ($ objectHash , $ groupHash );
102112 }
103113
104114 // Validate normal group
@@ -108,27 +118,34 @@ public function visit(Node $node, ExecutionContextInterface $context)
108118 continue ;
109119 }
110120
111- // Skip the group sequence when validating properties
112- unset($ node ->groups [$ key ]);
113-
114121 // Traverse group sequence until a violation is generated
115122 $ this ->traverseGroupSequence ($ node , $ group , $ context );
116123
117- // Optimization: If the groups only contain the group sequence,
118- // we can skip the traversal for the properties of the object
119- if (1 === count ($ node ->groups )) {
120- return false ;
121- }
124+ // Skip the group sequence when validating successor nodes
125+ unset($ node ->groups [$ key ]);
122126 }
123127
124128 return true ;
125129 }
126130
131+ /**
132+ * {@inheritdoc}
133+ */
127134 public function getCurrentGroup ()
128135 {
129136 return $ this ->currentGroup ;
130137 }
131138
139+ /**
140+ * Validates a node's value in each group of a group sequence.
141+ *
142+ * If any of the groups' constraints generates a violation, subsequent
143+ * groups are not validated anymore.
144+ *
145+ * @param Node $node The validated node
146+ * @param GroupSequence $groupSequence The group sequence
147+ * @param ExecutionContextInterface $context The execution context
148+ */
132149 private function traverseGroupSequence (Node $ node , GroupSequence $ groupSequence , ExecutionContextInterface $ context )
133150 {
134151 $ violationCount = count ($ context ->getViolations ());
@@ -150,6 +167,17 @@ private function traverseGroupSequence(Node $node, GroupSequence $groupSequence,
150167 }
151168 }
152169
170+ /**
171+ * Validates a node's value against all constraints in the given group.
172+ *
173+ * @param Node $node The validated node
174+ * @param string $group The group to validate
175+ * @param ExecutionContextInterface $context The execution context
176+ * @param string $objectHash The hash of the node's
177+ * object (if any)
178+ *
179+ * @throws \Exception
180+ */
153181 private function validateNodeForGroup (Node $ node , $ group , ExecutionContextInterface $ context , $ objectHash )
154182 {
155183 try {
@@ -176,8 +204,6 @@ private function validateNodeForGroup(Node $node, $group, ExecutionContextInterf
176204
177205 $ context ->markPropertyConstraintAsValidated ($ objectHash , $ propertyName , $ constraintHash );
178206 }
179-
180- $ this ->validatedConstraints [$ objectHash ][$ constraintHash ] = true ;
181207 }
182208
183209 $ validator = $ this ->validatorFactory ->getInstance ($ constraint );
@@ -187,6 +213,7 @@ private function validateNodeForGroup(Node $node, $group, ExecutionContextInterf
187213
188214 $ this ->currentGroup = null ;
189215 } catch (\Exception $ e ) {
216+ // Should be put into a finally block once we switch to PHP 5.5
190217 $ this ->currentGroup = null ;
191218
192219 throw $ e ;
0 commit comments