8000 [Validator] Fixed group handling in composite constraints · symfony/symfony@f6c070d · GitHub
[go: up one dir, main page]

Skip to content

Commit f6c070d

Browse files
committed
[Validator] Fixed group handling in composite constraints
1 parent 27a2280 commit f6c070d

14 files changed

+437
-77
lines changed

src/Symfony/Component/Validator/Constraint.php

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*
2525
* Constraint instances are immutable and serializable.
2626
*
27+
* @property array $groups The groups that the constraint belongs to
28+
*
2729
* @author Bernhard Schussek <bschussek@gmail.com>
2830
*
2931
* @api
@@ -48,11 +50,6 @@ abstract class Constraint
4850
*/
4951
const PROPERTY_CONSTRAINT = 'property';
5052

51-
/**
52-
* @var array
53-
*/
54-
public $groups = array(self::DEFAULT_GROUP);
55-
5653
/**
5754
* Initializes the constraint with options.
5855
*
@@ -86,6 +83,10 @@ public function __construct($options = null)
8683
{
8784
$invalidOptions = array();
8885
$missingOptions = array_flip((array) $this->getRequiredOptions());
86+
$knownOptions = get_object_vars($this);
87+
88+
// The "groups" option is added to the object lazily
89+
$knownOptions['groups'] = true;
8990

9091
if (is_array($options) && count($options) >= 1 && isset($options['value']) && !property_exists($this, 'value')) {
9192
$options[$this->getDefaultOption()] = $options['value'];
@@ -94,14 +95,14 @@ public function __construct($options = null)
9495

9596
if (is_array($options) && count($options) > 0 && is_string(key($options))) {
9697
foreach ($options as $option => $value) {
97-
if (property_exists($this, $option)) {
98+
if (array_key_exists($option, $knownOptions)) {
9899
$this->$option = $value;
99100
unset($missingOptions[$option]);
100101
} else {
101102
$invalidOptions[] = $option;
102103
}
103104
}
104-
} elseif (null !== $options && ! (is_array($options) && count($options) === 0)) {
105+
} elseif (null !== $options && !(is_array($options) && count($options) === 0)) {
105106
$option = $this->getDefaultOption();
106107

107108
if (null === $option) {
@@ -110,7 +111,7 @@ public function __construct($options = null)
110111
);
111112
}
112113

113-
if (property_exists($this, $option)) {
114+
if (array_key_exists($option, $knownOptions)) {
114115
$this->$option = $options;
115116
unset($missingOptions[$option]);
116117
} else {
@@ -131,15 +132,56 @@ public function __construct($options = null)
131132
array_keys($missingOptions)
132133
);
133134
}
134-
135-
$this->groups = (array) $this->groups;
136135
}
137136

138137
/**
139-
* Unsupported operation.
138+
* Sets the value of a lazily initialized option.
139+
*
140+
* Corresponding properties are added to the object on first access. Hence
141+
* this method will be called at most once per constraint instance and
142+
* option name.
143+
*
144+
* @param string $option The option name
145+
* @param mixed $value The value to set
146+
*
147+
* @throws InvalidOptionsException If an invalid option name is given
140148
*/
141149
public function __set($option, $value)
142150
{
151+
if ('groups' === $option) {
152+
$this->groups = (array) $value;
153+
154+
return;
155+
}
156+
157+
throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
158+
}
159+
160+
/**
161+
* Returns the value of a lazily initialized option.
162+
*
163+
* Corresponding properties are added to the object on first access. Hence
164+
* this method will be called at most once per constraint instance and
165+
* option name.
166+
*
167+
* @param string $option The option name
168+
*
169+
* @return mixed The value of the option
170+
*
171+
* @throws InvalidOptionsException If an invalid option name is given
172+
*
173+
* @internal This method should not be used or overwritten in userland code.
174+
*
175+
* @since 2.6
176+
*/
177+
public function __get($option)
178+
{
179+
if ('groups' === $option) {
180+
$this->groups = array(self::DEFAULT_GROUP);
181+
182+
return $this->groups;
183+
}
184+
143185
throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
144186
}
145187

@@ -217,4 +259,23 @@ public function getTargets()
217259
{
218260
return self::PROPERTY_CONSTRAINT;
219261
}
262+
263+
/**
264+
* Optimizes the serialized value to minimize storage space.
265+
*
266+
* @return array The properties to serialize
267+
*
268+
* @internal This method may be replaced by an implementation of
269+
* {@link \Serializable} in the future. Please don't use or
270+
* overwrite it.
271+
*
272+
* @since 2.6
273+
*/
274+
public function __sleep()
275+
{
276+
// Initialize "groups" option if it is not set
277+
$this->__get('groups');
278+
279+
return array_keys(get_object_vars($this));
280+
}
220281
}

src/Symfony/Component/Validator/Constraints/All.php

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14-
use Symfony\Component\Validator\Constraint;
15-
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
16-
1714
/**
1815
* @Annotation
1916
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
@@ -22,32 +19,10 @@
2219
*
2320
* @api
2421
*/
25-
class All extends Constraint
22+
class All extends Composite
2623
{
2724
public $constraints = array();
2825

29-
/**
30-
* {@inheritdoc}
31-
*/
32-
public function __construct($options = null)
33-
{
34-
parent::__construct($options);
35-
36-
if (!is_array($this->constraints)) {
37-
$this->constraints = array($this->constraints);
38-
}
39-
40-
foreach ($this->constraints as $constraint) {
41-
if (!$constraint instanceof Constraint) {
42-
throw new ConstraintDefinitionException(sprintf('The value %s is not an instance of Constraint in constraint %s', $constraint, __CLASS__));
43-
}
44-
45-
if ($constraint instanceof Valid) {
46-
throw new ConstraintDefinitionException(sprintf('The constraint Valid cannot be nested inside constraint %s. You can only declare the Valid constraint directly on a field or method.', __CLASS__));
47-
}
48-
}
49-
}
50-
5126
public function getDefaultOption()
5227
{
5328
return 'constraints';
@@ -57,4 +32,9 @@ public function getRequiredOptions()
5732
{
5833
return array('constraints');
5934
}
35+
36+
protected function getCompositeOption()
37+
{
38+
return 'constraints';
39+
}
6040
}

src/Symfony/Component/Validator/Constraints/AllValidator.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ public function validate($value, Constraint $constraint)
4040
}
4141

4242
$context = $this->context;
43-
$group = $context->getGroup();
4443
$validator = $context->getValidator()->inContext($context);
4544

4645
foreach ($value as $key => $element) {
47-
$validator->atPath('['.$key.']')->validate($element, $constraint->constraints, $group);
46+
$validator->atPath('['.$key.']')->validate($element, $constraint->constraints);
4847
}
4948
}
5049
}

src/Symfony/Component/Validator/Constraints/Collection.php

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*
2323
* @api
2424
*/
25-
class Collection extends Constraint
25+
class Collection extends Composite
2626
{
2727
public $fields = array();
2828
public $allowExtraFields = false;
@@ -42,6 +42,14 @@ public function __construct($options = null)
4242
}
4343

4444
parent::__construct($options);
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
protected function initializeNestedConstraints()
51+
{
52+
parent::initializeNestedConstraints();
4553

4654
if (!is_array($this->fields)) {
4755
throw new ConstraintDefinitionException(sprintf('The option "fields" is expected to be an array in constraint %s', __CLASS__));
@@ -57,25 +65,16 @@ public function __construct($options = null)
5765
if (!$field instanceof Optional && !$field instanceof Required) {
5866
$this->fields[$fieldName] = $field = new Required($field);
5967
}
60-
61-
if (!is_array($field->constraints)) {
62-
$field->constraints = array($field->constraints);
63-
}
64-
65-
foreach ($field->constraints as $constraint) {
66-
if (!$constraint instanceof Constraint) {
67-
throw new ConstraintDefinitionException(sprintf('The value %s of the field %s is not an instance of Constraint in constraint %s', $constraint, $fieldName, __CLASS__));
68-
}
69-
70-
if ($constraint instanceof Valid) {
71-
throw new ConstraintDefinitionException(sprintf('The constraint Valid cannot be nested inside constraint %s. You can only declare the Valid constraint directly on a field or method.', __CLASS__));
72-
}
73-
}
7468
}
7569
}
7670

7771
public function getRequiredOptions()
7872
{
7973
return array('fields');
8074
}
75+
76+
protected function getCompositeOption()
77+
{
78+
return 'fields';
79+
}
8180
}

src/Symfony/Component/Validator/Constraints/CollectionValidator.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ public function validate($value, Constraint $constraint)
4949
// remove the initialize() method and pass the context as last argument
5050
// to validate() instead.
5151
$context = $this->context;
52-
$group = $context->getGroup();
5352
$validator = $context->getValidator()->inContext($context);
5453

5554
foreach ($constraint->fields as $field => $fieldConstraint) {
@@ -60,7 +59,7 @@ public function validate($value, Constraint $constraint)
6059
if ($existsInArray || $existsInArrayAccess) {
6160
if (count($fieldConstraint->constraints) > 0) {
6261
$validator->atPath('['.$field.']')
63-
->validate($value[$field], $fieldConstraint->constraints, $group);
62+
->validate($value[$field], $fieldConstraint->constraints);
6463
}
6564
} elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) {
6665
$context->buildViolation($constraint->missingFieldsMessage)

0 commit comments

Comments
 (0)
0