8000 [Validator] Extracted code for group sequence resolving into GroupSeq… · symfony/symfony@1156bde · GitHub
[go: up one dir, main page]

Skip to content

Commit 1156bde

Browse files
committed
[Validator] Extracted code for group sequence resolving into GroupSequenceResolver
1 parent b1a9477 commit 1156bde

File tree

4 files changed

+101
-39
lines changed

4 files changed

+101
-39
lines changed

src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -116,27 +116,6 @@ private function traverseNode(Node $node)
116116

117117
private function traverseClassNode(ClassNode $node)
118118
{
119-
// Replace "Default" group by the group sequence attached to the class
120-
// (if any)
121-
foreach ($node->groups as $key => $group) {
122-
if (Constraint::DEFAULT_GROUP !== $group) {
123-
continue;
124-
}
125-
126-
if ($node->metadata->hasGroupSequence()) {
127-
$node->groups[$key] = $node->metadata->getGroupSequence();
128-
} elseif ($node->metadata->isGroupSequenceProvider()) {
129-
/** @var \Symfony\Component\Validator\GroupSequenceProviderInterface $value */
130-
$node->groups[$key] = $value->getGroupSequence();
131-
}
132-
133-
// Cascade the "Default" group when validating the sequence
134-
$node->groups[$key]->cascadedGroup = Constraint::DEFAULT_GROUP;
135-
136-
// "Default" group found, abort
137-
break;
138-
}
139-
140119
$stopTraversal = false;
141120

142121
foreach ($this->visitors as $visitor) {
@@ -147,7 +126,7 @@ private function traverseClassNode(ClassNode $node)
147126

148127
// Stop the traversal, but execute the leaveNode() methods anyway to
149128
// perform possible cleanups
150-
if (!$stopTraversal) {
129+
if (!$stopTraversal && count($node->groups) > 0) {
151130
foreach ($node->metadata->getConstrainedProperties() as $propertyName) {
152131
foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
153132
$this->traverseNode(new PropertyNode(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\NodeVisitor;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Node\ClassNode;
16+
use Symfony\Component\Validator\Node\Node;
17+
18+
/**
19+
* @since %%NextVersion%%
20+
* @author Bernhard Schussek <bschussek@gmail.com>
21+
*/
22+
class GroupSequenceResolver extends AbstractVisitor
23+
{
24+
public function enterNode(Node $node)
25+
{
26+
if (!$node instanceof ClassNode) {
27+
return;
28+
}
29+
30+
if ($node->metadata->hasGroupSequence()) {
31+
$groupSequence = $node->metadata->getGroupSequence();
32+
} elseif ($node->metadata->isGroupSequenceProvider()) {
33+
/** @var \Symfony\Component\Validator\GroupSequenceProviderInterface $value */
34+
$groupSequence = $value->getGroupSequence();
35+
} else {
36+
return;
37+
}
38+
39+
$key = array_search(Constraint::DEFAULT_GROUP, $node->groups);
40+
41+
if (false !== $key) {
42+
// Replace the "Default" group by the group sequence
43+
$node->groups[$key] = $groupSequence;
44+
45+
// Cascade the "Default" group when validating the sequence
46+
$node->groups[$key]->cascadedGroup = Constraint::DEFAULT_GROUP;
47+
}
48+
}
49+
}

src/Symfony/Component/Validator/NodeVisitor/NodeValidator.php

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Validator\Group\GroupManagerInterface;
1818
use Symfony\Component\Validator\Node\ClassNode;
1919
use Symfony\Component\Validator\Node\Node;
20+
use Symfony\Component\Validator\Node\PropertyNode;
2021
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
2122

2223
/**
@@ -27,6 +28,8 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface
2728
{
2829
private $validatedObjects = array();
2930

31+
private $validatedConstraints = array();
32+
3033
/**
3134
* @var ConstraintValidatorFactoryInterface
3235
*/
@@ -44,10 +47,15 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface
4447

4548
private $currentGroup;
4649

50+
private $currentObjectHash;
51+
52+
private $objectHashStack;
53+
4754
public function __construct(NodeTraverserInterface $nodeTraverser, ConstraintValidatorFactoryInterface $validatorFactory)
4855
{
4956
$this->validatorFactory = $validatorFactory;
5057
$this->nodeTraverser = $nodeTraverser;
58+
$this->objectHashStack = new \SplStack();
5159
}
5260

5361
public function initialize(ExecutionContextManagerInterface $contextManager)
@@ -58,39 +66,48 @@ public function initialize(ExecutionContextManagerInterface $contextManager)
5866
public function afterTraversal(array $nodes)
5967
{
6068
$this->validatedObjects = array();
69+
$this->validatedConstraints = array();
70+
$this->objectHashStack = new \SplStack();
6171
}
6272

6373
public function enterNode(Node $node)
6474
{
65-
$objectHash = $node instanceof ClassNode
66-
? spl_object_hash($node->value)
67-
: null;
75+
if ($node instanceof ClassNode) {
76+
$objectHash = spl_object_hash($node->value);
77+
$this->objectHashStack->push($objectHash);
78+
} elseif ($node instanceof PropertyNode) {
79+
$objectHash = $this->objectHashStack->top();
80+
} else {
81+
$objectHash = null;
82+
}
6883

6984
// if group (=[<G1,G2>,G3,G4]) contains group sequence (=<G1,G2>)
7085
// then call traverse() with each entry of the group sequence and abort
7186
// if necessary (G1, G2)
7287
// finally call traverse() with remaining entries ([G3,G4]) or
7388
// simply continue traversal (if possible)
7489

75-
foreach ($node->groups as $group) {
76-
// Validate object nodes only once per group
77-
if (null !== $objectHash) {
90+
foreach ($node->groups as $key => $group) {
91+
// Remember which object was validated for which group
92+
// Skip validation if the object was already validated for this
93+
// group
94+
if ($node instanceof ClassNode) {
7895
// Use the object hash for group sequences
7996
$groupHash = is_object($group) ? spl_object_hash($group) : $group;
8097

81-
// Exit, if the object is already validated for the current group
8298
if (isset($this->validatedObjects[$objectHash][$groupHash])) {
83-
return false;
99+
// Skip this group when validating properties
100+
unset($node->groups[$key]);
101+
102+
continue;
84103
}
85104

86-
// Remember validating this object before starting and possibly
87-
// traversing the object graph
88105
$this->validatedObjects[$objectHash][$groupHash] = true;
89106
}
90107

91-
// Validate group sequence until a violation is generated
108+
// Validate normal group
92109
if (!$group instanceof GroupSequence) {
93-
$this->validateNodeForGroup($node, $group);
110+
$this->validateNodeForGroup($objectHash, $node, $group);
94111

95112
continue;
96113
}
@@ -100,6 +117,7 @@ public function enterNode(Node $node)
100117
continue;
101118
}
102119

120+
// Traverse group sequence until a violation is generated
103121
$this->traverseGroupSequence($node, $group);
104122

105123
// Optimization: If the groups only contain the group sequence,
@@ -139,17 +157,31 @@ private function traverseGroupSequence(ClassNode $node, GroupSequence $groupSequ
139157
}
140158

141159
/**
160+
* @param $objectHash
142161
* @param Node $node
143162
* @param $group
144163
*
145164
* @throws \Exception
146165
*/
147-
private function validateNodeForGroup(Node $node, $group)
166+
private function validateNodeForGroup($objectHash, Node $node, $group)
148167
{
149168
try {
150169
$this->currentGroup = $group;
151170

152171
foreach ($node->metadata->findConstraints($group) as $constraint) {
172+
// Remember the validated constraints of each object to prevent
173+
// duplicate validation of constraints that belong to multiple
174+
// validated groups
175+
if (null !== $objectHash) {
176+
$constraintHash = spl_object_hash($constraint);
177+
178+
if (isset($this->validatedConstraints[$objectHash][$constraintHash])) {
179+
continue;
180+
}
181+
182+
$this->validatedConstraints[$objectHash][$constraintHash] = true;
183+
}
184+
153185
$validator = $this->validatorFactory->getInstance($constraint);
154186
$validator->initialize($this->contextManager->getCurrentContext());
155187
$validator->validate($node->value, $constraint);

src/Symfony/Component/Validator/NodeVisitor/ObjectInitializer.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ public function __construct(array $initializers)
3939

4040
public function enterNode(Node $node)
4141
{
42-
if ($node instanceof ClassNode) {
43-
foreach ($this->initializers as $initializer) {
44-
$initializer->initialize($node->value);
45-
}
42+
if (!$node instanceof ClassNode) {
43+
return;
44+
}
45+
46+
foreach ($this->initializers as $initializer) {
47+
$initializer->initialize($node->value);
4648
}
4749
}
4850
}

0 commit comments

Comments
 (0)
0