29
29
*/
30
30
class NodeValidationVisitor extends AbstractVisitor implements GroupManagerInterface
31
31
{
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
-
42
32
/**
43
33
* @var ConstraintValidatorFactoryInterface
44
34
*/
@@ -49,20 +39,37 @@ class NodeValidationVisitor extends AbstractVisitor implements GroupManagerInter
49
39
*/
50
40
private $ nodeTraverser ;
51
41
42
1241
+ /**
43
+ * The currently validated group.
44
+ *
45
+ * @var string
46
+ */
52
47
private $ currentGroup ;
53
48
49
+ /**
50
+ * Creates a new visitor.
51
+ *
52
+ * @param NodeTraverserInterface $nodeTraverser The node traverser
53
+ * @param ConstraintValidatorFactoryInterface $validatorFactory The validator factory
54
+ */
54
55
public function __construct (NodeTraverserInterface $ nodeTraverser , ConstraintValidatorFactoryInterface $ validatorFactory )
55
56
{
56
57
$ this ->validatorFactory = $ validatorFactory ;
57
58
$ this ->nodeTraverser = $ nodeTraverser ;
58
59
}
59
60
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
+ */
66
73
public function visit (Node $ node , ExecutionContextInterface $ context )
67
74
{
68
75
if ($ node instanceof CollectionNode) {
@@ -84,21 +91,24 @@ public function visit(Node $node, ExecutionContextInterface $context)
84
91
// simply continue traversal (if possible)
85
92
86
93
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.
90
99
if ($ node instanceof ClassNode) {
91
100
// Use the object hash for group sequences
92
101
$ groupHash = is_object ($ group ) ? spl_object_hash ($ group ) : $ group ;
93
102
94
103
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)
96
106
unset($ node ->groups [$ key ]);
97
107
98
108
continue ;
99
109
}
100
110
101
- // $context->markObjectAsValidatedForGroup($objectHash, $groupHash);
111
+ $ context ->markObjectAsValidatedForGroup ($ objectHash , $ groupHash );
102
112
}
103
113
104
114
// Validate normal group
@@ -108,27 +118,34 @@ public function visit(Node $node, ExecutionContextInterface $context)
108
118
continue ;
109
119
}
110
120
111
- // Skip the group sequence when validating properties
112
- unset($ node ->groups [$ key ]);
113
-
114
121
// Traverse group sequence until a violation is generated
115
122
$ this ->traverseGroupSequence ($ node , $ group , $ context );
116
123
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 ]);
122
126
}
123
127
124
128
return true ;
125
129
}
126
130
131
+ /**
132
+ * {@inheritdoc}
133
+ */
127
134
public function getCurrentGroup ()
128
135
{
129
136
return $ this ->currentGroup ;
130
137
}
131
138
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
+ */
132
149
private function traverseGroupSequence (Node $ node , GroupSequence $ groupSequence , ExecutionContextInterface $ context )
133
150
{
134
151
$ violationCount = count ($ context ->getViolations ());
@@ -150,6 +167,17 @@ private function traverseGroupSequence(Node $node, GroupSequence $groupSequence,
150
167
}
151
168
}
152
169
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
+ */
153
181
private function validateNodeForGroup (Node $ node , $ group , ExecutionContextInterface $ context , $ objectHash )
154
182
{
155
183
try {
@@ -176,8 +204,6 @@ private function validateNodeForGroup(Node $node, $group, ExecutionContextInterf
176
204
177
205
$ context ->markPropertyConstraintAsValidated ($ objectHash , $ propertyName , $ constraintHash );
178
206
}
179
-
180
- $ this ->validatedConstraints [$ objectHash ][$ constraintHash ] = true ;
181
207
}
182
208
183
209
$ validator = $ this ->validatorFactory ->getInstance ($ constraint );
@@ -187,6 +213,7 @@ private function validateNodeForGroup(Node $node, $group, ExecutionContextInterf
187
213
188
214
$ this ->currentGroup = null ;
189
215
} catch (\Exception $ e ) {
216
+ // Should be put into a finally block once we switch to PHP 5.5
190
217
$ this ->currentGroup = null ;
191
218
192
219
throw $ e ;
0 commit comments