Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 69b6c90

Browse filesBrowse files
Merge branch '5.0'
* 5.0: [HttpFoundation] workaround PHP bug in the session module [SecurityBundle] fix accepting env vars in remember-me configurations [Form] Fixed handling groups sequence validation [Mime] Ensure proper line-ending for SMIME [Cache] Avoid memory leak in TraceableAdapter::reset()
2 parents 23f5070 + efbe752 commit 69b6c90
Copy full SHA for 69b6c90

File tree

10 files changed

+237
-38
lines changed
Filter options

10 files changed

+237
-38
lines changed

‎src/Symfony/Component/Cache/Adapter/TraceableAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
+3-7Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,11 @@ public function prune()
244244
*/
245245
public function reset()
246246
{
247-
if (!$this->pool instanceof ResetInterface) {
248-
return;
249-
}
250-
$event = $this->start(__FUNCTION__);
251-
try {
247+
if ($this->pool instanceof ResetInterface) {
252248
$this->pool->reset();
253-
} finally {
254-
$event->end = microtime(true);
255249
}
250+
251+
$this->clearCalls();
256252
}
257253

258254
/**

‎src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+59-20Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
class FormValidator extends ConstraintValidator
2626
{
27+
private $resolvedGroups;
28+
2729
/**
2830
* {@inheritdoc}
2931
*/
@@ -44,44 +46,70 @@ public function validate($form, Constraint $formConstraint)
4446

4547
if ($form->isSubmitted() && $form->isSynchronized()) {
4648
// Validate the form data only if transformation succeeded
47-
$groups = self::getValidationGroups($form);
49+
$groups = $this->getValidationGroups($form);
4850

4951
if (!$groups) {
5052
return;
5153
}
5254

5355
$data = $form->getData();
54-
5556
// Validate the data against its own constraints
56-
if ($form->isRoot() && (\is_object($data) || \is_array($data))) {
57-
if (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups)) {
58-
$validator->atPath('data')->validate($form->getData(), null, $groups);
59-
}
60-
}
57+
$validateDataGraph = $form->isRoot()
58+
&& (\is_object($data) || \is_array($data))
59+
&& (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups))
60+
;
6161

62-
// Validate the data against the constraints defined
63-
// in the form
62+
// Validate the data against the constraints defined in the form
63+
/** @var Constraint[] $constraints */
6464
$constraints = $config->getOption('constraints', []);
6565

6666
if ($groups instanceof GroupSequence) {
67-
$validator->atPath('data')->validate($form->getData(), $constraints, $groups);
68-
// Otherwise validate a constraint only once for the first
69-
// matching group
70-
foreach ($groups as $group) {
71-
if (\in_array($group, $formConstraint->groups)) {
72-
$validator->atPath('data')->validate($form->getData(), $formConstraint, $group);
73-
if (\count($this->context->getViolations()) > 0) {
74-
break;
67+
// Validate the data, the form AND nested fields in sequence
68+
$violationsCount = $this->context->getViolations()->count();
69+
$fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
70+
$hasChildren = $form->count() > 0;
71+
$this->resolvedGroups = $hasChildren ? new \SplObjectStorage() : null;
72+
73+
foreach ($groups->groups as $group) {
74+
if ($validateDataGraph) {
75+
$validator->atPath('data')->validate($data, null, $group);
76+
}
77+
78+
if ($groupedConstraints = self::getConstraintsInGroups($constraints, $group)) {
79+
$validator->atPath('data')->validate($data, $groupedConstraints, $group);
80+
}
81+
82+
foreach ($form->all() as $field) {
83+
if ($field->isSubmitted()) {
84+
// remember to validate this field is one group only
85+
// otherwise resolving the groups would reuse the same
86+
// sequence recursively, thus some fields could fail
87+
// in different steps without breaking early enough
88+
$this->resolvedGroups[$field] = (array) $group;
89+
$validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $formConstraint);
7590
}
7691
}
92+
93+
if ($violationsCount < $this->context->getViolations()->count()) {
94+
break;
95+
}
96+
}
97+
98+
if ($hasChildren) {
99+
// destroy storage at the end of the sequence to avoid memory leaks
100+
$this->resolvedGroups = null;
77101
}
78102
} else {
103+
if ($validateDataGraph) {
104+
$validator->atPath('data')->validate($data, null, $groups);
105+
}
106+
79107
$groupedConstraints = [];
80108

81109
foreach ($constraints as $constraint) {
82110
// For the "Valid" constraint, validate the data in all groups
83111
if ($constraint instanceof Valid) {
84-
$validator->atPath('data')->validate($form->getData(), $constraint, $groups);
112+
$validator->atPath('data')->validate($data, $constraint, $groups);
85113

86114
continue;
87115
}
@@ -101,7 +129,7 @@ public function validate($form, Constraint $formConstraint)
101129
}
102130

103131
foreach ($groupedConstraints as $group => $constraint) {
104-
$validator->atPath('data')->validate($form->getData(), $constraint, $group);
132+
$validator->atPath('data')->validate($data, $constraint, $group);
105133
}
106134
}
107135
} elseif (!$form->isSynchronized()) {
@@ -160,7 +188,7 @@ public function validate($form, Constraint $formConstraint)
160188
*
161189
* @return string|GroupSequence|(string|GroupSequence)[] The validation groups
162190
*/
163-
private static function getValidationGroups(FormInterface $form)
191+
private function getValidationGroups(FormInterface $form)
164192
{
165193
// Determine the clicked button of the complete form tree
166194
$clickedButton = null;
@@ -184,6 +212,10 @@ private static function getValidationGroups(FormInterface $form)
184212
return self::resolveValidationGroups($groups, $form);
185213
}
186214

215+
if (isset($this->resolvedGroups[$form])) {
216+
return $this->resolvedGroups[$form];
217+
}
218+
187219
$form = $form->getParent();
188220
} while (null !== $form);
189221

@@ -209,4 +241,11 @@ private static function resolveValidationGroups($groups, FormInterface $form)
209241

210242
return (array) $groups;
211243
}
244+
245+
private static function getConstraintsInGroups($constraints, $group)
246+
{
247+
return array_filter($constraints, static function (Constraint $constraint) use ($group) {
248+
return \in_array($group, $constraint->groups, true);
249+
});
250+
}
212251
}

‎src/Symfony/Component/Form/Resources/config/validation.xml

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Resources/config/validation.xml
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<class name="Symfony\Component\Form\Form">
88
<constraint name="Symfony\Component\Form\Extension\Validator\Constraints\Form" />
99
<property name="children">
10-
<constraint name="Valid" />
10+
<constraint name="Valid" />
1111
</property>
1212
</class>
1313
</constraint-mapping>

‎src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+36-3Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,8 @@ public function testHandleGroupSequenceValidationGroups()
444444
$form = $this->getCompoundForm($object, $options);
445445
$form->submit([]);
446446

447-
$this->expectValidateAt(0, 'data', $object, new GroupSequence(['group1', 'group2']));
448-
$this->expectValidateAt(1, 'data', $object, new GroupSequence(['group1', 'group2']));
447+
$this->expectValidateAt(0, 'data', $object, 'group1');
448+
$this->expectValidateAt(1, 'data', $object, 'group2');
449449

450450
$this->validator->validate($form, new Form());
451451

@@ -801,6 +801,39 @@ public function testCompositeConstraintValidatedInEachGroup()
801801
$this->assertSame('data[field2]', $context->getViolations()[1]->getPropertyPath());
802802
}
803803

804+
public function testCompositeConstraintValidatedInSequence()
805+
{
806+
$form = $this->getCompoundForm([], [
807+
'constraints' => [
808+
new Collection([
809+
'field1' => new NotBlank([
810+
'groups' => ['field1'],
811+
]),
812+
'field2' => new NotBlank([
813+
'groups' => ['field2'],
814+
]),
815+
]),
816+
],
817+
'validation_groups' => new GroupSequence(['field1', 'field2']),
818+
])
819+
->add($this->getForm('field1'))
820+
->add($this->getForm('field2'))
821+
;
822+
823+
$form->submit([
824+
'field1' => '',
825+
'field2' => '',
826+
]);
827+
828+
$context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
829+
$this->validator->initialize($context);
830+
$this->validator->validate($form, new Form());
831+
832+
$this->assertCount(1, $context->getViolations());
833+
$this->assertSame('This value should not be blank.', $context->getViolations()[0]->getMessage());
834+
$this->assertSame('data[field1]', $context->getViolations()[0]->getPropertyPath());
835+
}
836+
804837
protected function createValidator()
805838
{
806839
return new FormValidator();
@@ -823,7 +856,7 @@ private function getForm($name = 'name', $dataClass = null, array $options = [])
823856

824857
private function getCompoundForm($data, array $options = [])
825858
{
826-
return $this->getBuilder('name', \get_class($data), $options)
859+
return $this->getBuilder('name', \is_object($data) ? \get_class($data) : null, $options)
827860
->setData($data)
828861
->setCompound(true)
829862
->setDataMapper(new PropertyPathMapper())

‎src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
+64-3Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
1313

1414
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
15+
use Symfony\Component\Form\Form;
1516
use Symfony\Component\Form\Forms;
1617
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
1718
use Symfony\Component\Form\Tests\Extension\Core\Type\FormTypeTest;
1819
use Symfony\Component\Form\Tests\Extension\Core\Type\TextTypeTest;
19-
use Symfony\Component\Validator\Constraints\Email;
20+
use Symfony\Component\Form\Tests\Fixtures\Author;
2021
use Symfony\Component\Validator\Constraints\GroupSequence;
2122
use Symfony\Component\Validator\Constraints\Length;
23+
use Symfony\Component\Validator\Constraints\NotBlank;
2224
use Symfony\Component\Validator\Constraints\Valid;
2325
use Symfony\Component\Validator\ConstraintViolationList;
26+
use Symfony\Component\Validator\Mapping\ClassMetadata;
27+
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
2428
use Symfony\Component\Validator\Validation;
2529

2630
class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest
@@ -64,14 +68,71 @@ public function testGroupSequenceWithConstraintsOption()
6468
->add('field', TextTypeTest::TESTED_TYPE, [
6569
'constraints' => [
6670
new Length(['min' => 10, 'allowEmptyString' => true, 'groups' => ['First']]),
67-
new Email(['groups' => ['Second']]),
71+
new NotBlank(['groups' => ['Second']]),
6872
],
6973
])
7074
;
7175

7276
$form->submit(['field' => 'wrong']);
7377

74-
$this->assertCount(1, $form->getErrors(true));
78+
$errors = $form->getErrors(true);
79+
80+
$this->assertCount(1, $errors);
81+
$this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
82+
}
83+
84+
public function testManyFieldsGroupSequenceWithConstraintsOption()
85+
{
86+
$allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
87+
88+
$formMetadata = new ClassMetadata(Form::class);
89+
$authorMetadata = (new ClassMetadata(Author::class))
90+
->addPropertyConstraint('firstName', new NotBlank(['groups' => 'Second']))
91+
;
92+
$metadataFactory = $this->createMock(MetadataFactoryInterface::class);
93+
$metadataFactory->expects($this->any())
94+
->method('getMetadataFor')
95+
->willReturnCallback(static function ($classOrObject) use ($formMetadata, $authorMetadata) {
96+
if (Author::class === $classOrObject || $classOrObject instanceof Author) {
97+
return $authorMetadata;
98+
}
99+
100+
if (Form::class === $classOrObject || $classOrObject instanceof Form) {
101+
return $formMetadata;
102+
}
103+
104+
return new ClassMetadata(\is_string($classOrObject) ? $classOrObject : \get_class($classOrObject));
105+
})
106+
;
107+
108+
$validator = Validation::createValidatorBuilder()
109+
->setMetadataFactory($metadataFactory)
110+
->getValidator()
111+
;
112+
$form = Forms::createFormFactoryBuilder()
113+
->addExtension(new ValidatorExtension($validator))
114+
->getFormFactory()
115+
->create(FormTypeTest::TESTED_TYPE, new Author(), (['validation_groups' => new GroupSequence(['First', 'Second'])]))
116+
->add('firstName', TextTypeTest::TESTED_TYPE)
117+
->add('lastName', TextTypeTest::TESTED_TYPE, [
118+
'constraints' => [
119+
new Length(['min' => 10, 'groups' => ['First']] + $allowEmptyString),
120+
],
121+
])
122+
->add('australian', TextTypeTest::TESTED_TYPE, [
123+
'constraints' => [
124+
new NotBlank(['groups' => ['Second']]),
125+
],
126+
])
127+
;
128+
129+
$form->submit(['firstName' => '', 'lastName' => 'wrong_1', 'australian' => '']);
130+
131+
$errors = $form->getErrors(true);
132+
133+
$this->assertCount(1, $errors);
134+
$this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
135+
$this->assertSame('children[lastName].data', $errors[0]->getCause()->getPropertyPath());
75136
}
76137

77138
protected function createForm(array $options = [])

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.