aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/symfony/form/Symfony/Component/Form/Extension/Validator
diff options
context:
space:
mode:
authorNicolas LÅ“uillet <nicolas.loeuillet@gmail.com>2013-08-03 19:26:54 +0200
committerNicolas LÅ“uillet <nicolas.loeuillet@gmail.com>2013-08-03 19:26:54 +0200
commit4f5b44bd3bd490309eb2ba7b44df4769816ba729 (patch)
tree6cefe170dfe0a5a361cb1e2d1fc4d580a3316d02 /vendor/symfony/form/Symfony/Component/Form/Extension/Validator
parent2b840e0cfb63a453bea67a98541f3df9c273c5f5 (diff)
downloadwallabag-4f5b44bd3bd490309eb2ba7b44df4769816ba729.tar.gz
wallabag-4f5b44bd3bd490309eb2ba7b44df4769816ba729.tar.zst
wallabag-4f5b44bd3bd490309eb2ba7b44df4769816ba729.zip
twig implementation
Diffstat (limited to 'vendor/symfony/form/Symfony/Component/Form/Extension/Validator')
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php33
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php236
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php68
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php56
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php84
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php45
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php28
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php64
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php57
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php286
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php106
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php45
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php299
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php33
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php250
-rw-r--r--vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php30
16 files changed, 1720 insertions, 0 deletions
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php
new file mode 100644
index 00000000..87e33297
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php
@@ -0,0 +1,33 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Constraints;
13
14use Symfony\Component\Validator\Constraint;
15
16/**
17 * @author Bernhard Schussek <bschussek@gmail.com>
18 */
19class Form extends Constraint
20{
21 /**
22 * Violation code marking an invalid form.
23 */
24 const ERR_INVALID = 1;
25
26 /**
27 * {@inheritdoc}
28 */
29 public function getTargets()
30 {
31 return self::CLASS_CONSTRAINT;
32 }
33}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
new file mode 100644
index 00000000..bad5a007
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -0,0 +1,236 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Constraints;
13
14use Symfony\Component\Form\ClickableInterface;
15use Symfony\Component\Form\FormInterface;
16use Symfony\Component\Form\Extension\Validator\Util\ServerParams;
17use Symfony\Component\Validator\Constraint;
18use Symfony\Component\Validator\ConstraintValidator;
19
20/**
21 * @author Bernhard Schussek <bschussek@gmail.com>
22 */
23class FormValidator extends ConstraintValidator
24{
25 /**
26 * @var ServerParams
27 */
28 private $serverParams;
29
30 /**
31 * Creates a validator with the given server parameters.
32 *
33 * @param ServerParams $params The server parameters. Default
34 * parameters are created if null.
35 */
36 public function __construct(ServerParams $params = null)
37 {
38 $this->serverParams = $params ?: new ServerParams();
39 }
40
41 /**
42 * {@inheritdoc}
43 */
44 public function validate($form, Constraint $constraint)
45 {
46 if (!$form instanceof FormInterface) {
47 return;
48 }
49
50 /* @var FormInterface $form */
51 $config = $form->getConfig();
52
53 if ($form->isSynchronized()) {
54 // Validate the form data only if transformation succeeded
55 $groups = self::getValidationGroups($form);
56
57 // Validate the data against its own constraints
58 if (self::allowDataWalking($form)) {
59 foreach ($groups as $group) {
60 $this->context->validate($form->getData(), 'data', $group, true);
61 }
62 }
63
64 // Validate the data against the constraints defined
65 // in the form
66 $constraints = $config->getOption('constraints');
67 foreach ($constraints as $constraint) {
68 foreach ($groups as $group) {
69 if (in_array($group, $constraint->groups)) {
70 $this->context->validateValue($form->getData(), $constraint, 'data', $group);
71
72 // Prevent duplicate validation
73 continue 2;
74 }
75 }
76 }
77 } else {
78 $childrenSynchronized = true;
79
80 foreach ($form as $child) {
81 if (!$child->isSynchronized()) {
82 $childrenSynchronized = false;
83 break;
84 }
85 }
86
87 // Mark the form with an error if it is not synchronized BUT all
88 // of its children are synchronized. If any child is not
89 // synchronized, an error is displayed there already and showing
90 // a second error in its parent form is pointless, or worse, may
91 // lead to duplicate errors if error bubbling is enabled on the
92 // child.
93 // See also https://github.com/symfony/symfony/issues/4359
94 if ($childrenSynchronized) {
95 $clientDataAsString = is_scalar($form->getViewData())
96 ? (string) $form->getViewData()
97 : gettype($form->getViewData());
98
99 $this->context->addViolation(
100 $config->getOption('invalid_message'),
101 array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters')),
102 $form->getViewData(),
103 null,
104 Form::ERR_INVALID
105 );
106 }
107 }
108
109 // Mark the form with an error if it contains extra fields
110 if (count($form->getExtraData()) > 0) {
111 $this->context->addViolation(
112 $config->getOption('extra_fields_message'),
113 array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))),
114 $form->getExtraData()
115 );
116 }
117
118 // Mark the form with an error if the uploaded size was too large
119 $length = $this->serverParams->getContentLength();
120
121 if ($form->isRoot() && null !== $length) {
122 $max = $this->serverParams->getPostMaxSize();
123
124 if (!empty($max) && $length > $max) {
125 $this->context->addViolation(
126 $config->getOption('post_max_size_message'),
127 array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()),
128 $length
129 );
130 }
131 }
132 }
133
134 /**
135 * Returns whether the data of a form may be walked.
136 *
137 * @param FormInterface $form The form to test.
138 *
139 * @return Boolean Whether the graph walker may walk the data.
140 */
141 private static function allowDataWalking(FormInterface $form)
142 {
143 $data = $form->getData();
144
145 // Scalar values cannot have mapped constraints
146 if (!is_object($data) && !is_array($data)) {
147 return false;
148 }
149
150 // Root forms are always validated
151 if ($form->isRoot()) {
152 return true;
153 }
154
155 // Non-root forms are validated if validation cascading
156 // is enabled in all ancestor forms
157 while (null !== ($form = $form->getParent())) {
158 if (!$form->getConfig()->getOption('cascade_validation')) {
159 return false;
160 }
161 }
162
163 return true;
164 }
165
166 /**
167 * Returns the validation groups of the given form.
168 *
169 * @param FormInterface $form The form.
170 *
171 * @return array The validation groups.
172 */
173 private static function getValidationGroups(FormInterface $form)
174 {
175 $button = self::findClickedButton($form->getRoot());
176
177 if (null !== $button) {
178 $groups = $button->getConfig()->getOption('validation_groups');
179
180 if (null !== $groups) {
181 return self::resolveValidationGroups($groups, $form);
182 }
183 }
184
185 do {
186 $groups = $form->getConfig()->getOption('validation_groups');
187
188 if (null !== $groups) {
189 return self::resolveValidationGroups($groups, $form);
190 }
191
192 $form = $form->getParent();
193 } while (null !== $form);
194
195 return array(Constraint::DEFAULT_GROUP);
196 }
197
198 /**
199 * Extracts a clicked button from a form tree, if one exists.
200 *
201 * @param FormInterface $form The root form.
202 *
203 * @return ClickableInterface|null The clicked button or null.
204 */
205 private static function findClickedButton(FormInterface $form)
206 {
207 if ($form instanceof ClickableInterface && $form->isClicked()) {
208 return $form;
209 }
210
211 foreach ($form as $child) {
212 if (null !== ($button = self::findClickedButton($child))) {
213 return $button;
214 }
215 }
216
217 return null;
218 }
219
220 /**
221 * Post-processes the validation groups option for a given form.
222 *
223 * @param array|callable $groups The validation groups.
224 * @param FormInterface $form The validated form.
225 *
226 * @return array The validation groups.
227 */
228 private static function resolveValidationGroups($groups, FormInterface $form)
229 {
230 if (!is_string($groups) && is_callable($groups)) {
231 $groups = call_user_func($groups, $form);
232 }
233
234 return (array) $groups;
235 }
236}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
new file mode 100644
index 00000000..14147531
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
@@ -0,0 +1,68 @@
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
12namespace Symfony\Component\Form\Extension\Validator\EventListener;
13
14use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface;
16use Symfony\Component\Validator\ValidatorInterface;
17use Symfony\Component\Form\FormEvents;
18use Symfony\Component\Form\FormEvent;
19use Symfony\Component\Form\Extension\Validator\Constraints\Form;
20
21/**
22 * @author Bernhard Schussek <bschussek@gmail.com>
23 */
24class ValidationListener implements EventSubscriberInterface
25{
26 private $validator;
27
28 private $violationMapper;
29
30 /**
31 * {@inheritdoc}
32 */
33 public static function getSubscribedEvents()
34 {
35 return array(FormEvents::POST_SUBMIT => 'validateForm');
36 }
37
38 public function __construct(ValidatorInterface $validator, ViolationMapperInterface $violationMapper)
39 {
40 $this->validator = $validator;
41 $this->violationMapper = $violationMapper;
42 }
43
44 /**
45 * Validates the form and its domain object.
46 *
47 * @param FormEvent $event The event object
48 */
49 public function validateForm(FormEvent $event)
50 {
51 $form = $event->getForm();
52
53 if ($form->isRoot()) {
54 // Validate the form in group "Default"
55 $violations = $this->validator->validate($form);
56
57 if (count($violations) > 0) {
58 foreach ($violations as $violation) {
59 // Allow the "invalid" constraint to be put onto
60 // non-synchronized forms
61 $allowNonSynchronized = Form::ERR_INVALID === $violation->getCode();
62
63 $this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
64 }
65 }
66 }
67 }
68}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php
new file mode 100644
index 00000000..7c5e6784
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php
@@ -0,0 +1,56 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Type;
13
14use Symfony\Component\Form\AbstractTypeExtension;
15use Symfony\Component\OptionsResolver\Options;
16use Symfony\Component\OptionsResolver\OptionsResolverInterface;
17
18/**
19 * Encapsulates common logic of {@link FormTypeValidatorExtension} and
20 * {@link SubmitTypeValidatorExtension}.
21 *
22 * @author Bernhard Schussek <bschussek@gmail.com>
23 */
24abstract class BaseValidatorExtension extends AbstractTypeExtension
25{
26 /**
27 * {@inheritdoc}
28 */
29 public function setDefaultOptions(OptionsResolverInterface $resolver)
30 {
31 // Make sure that validation groups end up as null, closure or array
32 $validationGroupsNormalizer = function (Options $options, $groups) {
33 if (false === $groups) {
34 return array();
35 }
36
37 if (empty($groups)) {
38 return null;
39 }
40
41 if (is_callable($groups)) {
42 return $groups;
43 }
44
45 return (array) $groups;
46 };
47
48 $resolver->setDefaults(array(
49 'validation_groups' => null,
50 ));
51
52 $resolver->setNormalizers(array(
53 'validation_groups' => $validationGroupsNormalizer,
54 ));
55 }
56}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php
new file mode 100644
index 00000000..344bddad
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php
@@ -0,0 +1,84 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Type;
13
14use Symfony\Component\Form\FormBuilderInterface;
15use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
16use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
17use Symfony\Component\Validator\ValidatorInterface;
18use Symfony\Component\OptionsResolver\Options;
19use Symfony\Component\OptionsResolver\OptionsResolverInterface;
20
21/**
22 * @author Bernhard Schussek <bschussek@gmail.com>
23 */
24class FormTypeValidatorExtension extends BaseValidatorExtension
25{
26 /**
27 * @var ValidatorInterface
28 */
29 private $validator;
30
31 /**
32 * @var ViolationMapper
33 */
34 private $violationMapper;
35
36 public function __construct(ValidatorInterface $validator)
37 {
38 $this->validator = $validator;
39 $this->violationMapper = new ViolationMapper();
40 }
41
42 /**
43 * {@inheritdoc}
44 */
45 public function buildForm(FormBuilderInterface $builder, array $options)
46 {
47 $builder->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper));
48 }
49
50 /**
51 * {@inheritdoc}
52 */
53 public function setDefaultOptions(OptionsResolverInterface $resolver)
54 {
55 parent::setDefaultOptions($resolver);
56
57 // Constraint should always be converted to an array
58 $constraintsNormalizer = function (Options $options, $constraints) {
59 return is_object($constraints) ? array($constraints) : (array) $constraints;
60 };
61
62 $resolver->setDefaults(array(
63 'error_mapping' => array(),
64 'constraints' => array(),
65 'cascade_validation' => false,
66 'invalid_message' => 'This value is not valid.',
67 'invalid_message_parameters' => array(),
68 'extra_fields_message' => 'This form should not contain extra fields.',
69 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
70 ));
71
72 $resolver->setNormalizers(array(
73 'constraints' => $constraintsNormalizer,
74 ));
75 }
76
77 /**
78 * {@inheritdoc}
79 */
80 public function getExtendedType()
81 {
82 return 'form';
83 }
84}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php
new file mode 100644
index 00000000..858ff0fa
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php
@@ -0,0 +1,45 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Type;
13
14use Symfony\Component\Form\AbstractTypeExtension;
15use Symfony\Component\OptionsResolver\Options;
16use Symfony\Component\OptionsResolver\OptionsResolverInterface;
17
18/**
19 * @author Bernhard Schussek <bschussek@gmail.com>
20 */
21class RepeatedTypeValidatorExtension extends AbstractTypeExtension
22{
23 /**
24 * {@inheritdoc}
25 */
26 public function setDefaultOptions(OptionsResolverInterface $resolver)
27 {
28 // Map errors to the first field
29 $errorMapping = function (Options $options) {
30 return array('.' => $options['first_name']);
31 };
32
33 $resolver->setDefaults(array(
34 'error_mapping' => $errorMapping,
35 ));
36 }
37
38 /**
39 * {@inheritdoc}
40 */
41 public function getExtendedType()
42 {
43 return 'repeated';
44 }
45}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php
new file mode 100644
index 00000000..5aad67fb
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php
@@ -0,0 +1,28 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Type;
13
14use Symfony\Component\Form\AbstractTypeExtension;
15
16/**
17 * @author Bernhard Schussek <bschussek@gmail.com>
18 */
19class SubmitTypeValidatorExtension extends AbstractTypeExtension
20{
21 /**
22 * {@inheritdoc}
23 */
24 public function getExtendedType()
25 {
26 return 'submit';
27 }
28}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php
new file mode 100644
index 00000000..fab6ac2a
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php
@@ -0,0 +1,64 @@
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
12namespace Symfony\Component\Form\Extension\Validator\Util;
13
14/**
15 * @author Bernhard Schussek <bschussek@gmail.com>
16 */
17class ServerParams
18{
19 /**
20 * Returns maximum post size in bytes.
21 *
22 * @return null|integer The maximum post size in bytes
23 */
24 public function getPostMaxSize()
25 {
26 $iniMax = $this->getNormalizedIniPostMaxSize();
27
28 if ('' === $iniMax) {
29 return null;
30 }
31
32 if (preg_match('#^\+?(0X?)?(.*?)([KMG]?)$#', $iniMax, $match)) {
33 $shifts = array('' => 0, 'K' => 10, 'M' => 20, 'G' => 30);
34 $bases = array('' => 10, '0' => 8, '0X' => 16);
35
36 return intval($match[2], $bases[$match[1]]) << $shifts[$match[3]];
37 }
38
39 return 0;
40 }
41
42 /**
43 * Returns the normalized "post_max_size" ini setting.
44 *
45 * @return string
46 */
47 public function getNormalizedIniPostMaxSize()
48 {
49 return strtoupper(trim(ini_get('post_max_size')));
50 }
51
52 /**
53 * Returns the content length of the request.
54 *
55 * @return mixed The request content length.
56 */
57 public function getContentLength()
58 {
59 return isset($_SERVER['CONTENT_LENGTH'])
60 ? (int) $_SERVER['CONTENT_LENGTH']
61 : null;
62 }
63
64}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php
new file mode 100644
index 00000000..9cff22a2
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php
@@ -0,0 +1,57 @@
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
12namespace Symfony\Component\Form\Extension\Validator;
13
14use Symfony\Component\Form\Extension\Validator\Type;
15use Symfony\Component\Form\Extension\Validator\Constraints\Form;
16use Symfony\Component\Form\AbstractExtension;
17use Symfony\Component\Validator\ValidatorInterface;
18use Symfony\Component\Validator\Constraints\Valid;
19
20/**
21 * Extension supporting the Symfony2 Validator component in forms.
22 *
23 * @author Bernhard Schussek <bschussek@gmail.com>
24 */
25class ValidatorExtension extends AbstractExtension
26{
27 private $validator;
28
29 public function __construct(ValidatorInterface $validator)
30 {
31 $this->validator = $validator;
32
33 // Register the form constraints in the validator programmatically.
34 // This functionality is required when using the Form component without
35 // the DIC, where the XML file is loaded automatically. Thus the following
36 // code must be kept synchronized with validation.xml
37
38 /** @var \Symfony\Component\Validator\Mapping\ClassMetadata $metadata */
39 $metadata = $this->validator->getMetadataFactory()->getMetadataFor('Symfony\Component\Form\Form');
40 $metadata->addConstraint(new Form());
41 $metadata->addPropertyConstraint('children', new Valid());
42 }
43
44 public function loadTypeGuesser()
45 {
46 return new ValidatorTypeGuesser($this->validator->getMetadataFactory());
47 }
48
49 protected function loadTypeExtensions()
50 {
51 return array(
52 new Type\FormTypeValidatorExtension($this->validator),
53 new Type\RepeatedTypeValidatorExtension(),
54 new Type\SubmitTypeValidatorExtension(),
55 );
56 }
57}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
new file mode 100644
index 00000000..dcd9cc55
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
@@ -0,0 +1,286 @@
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
12namespace Symfony\Component\Form\Extension\Validator;
13
14use Symfony\Component\Form\FormTypeGuesserInterface;
15use Symfony\Component\Form\Guess\Guess;
16use Symfony\Component\Form\Guess\TypeGuess;
17use Symfony\Component\Form\Guess\ValueGuess;
18use Symfony\Component\Validator\MetadataFactoryInterface;
19use Symfony\Component\Validator\Constraint;
20
21class ValidatorTypeGuesser implements FormTypeGuesserInterface
22{
23 private $metadataFactory;
24
25 public function __construct(MetadataFactoryInterface $metadataFactory)
26 {
27 $this->metadataFactory = $metadataFactory;
28 }
29
30 /**
31 * {@inheritDoc}
32 */
33 public function guessType($class, $property)
34 {
35 $guesser = $this;
36
37 return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) {
38 return $guesser->guessTypeForConstraint($constraint);
39 });
40 }
41
42 /**
43 * {@inheritDoc}
44 */
45 public function guessRequired($class, $property)
46 {
47 $guesser = $this;
48
49 return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) {
50 return $guesser->guessRequiredForConstraint($constraint);
51 // If we don't find any constraint telling otherwise, we can assume
52 // that a field is not required (with LOW_CONFIDENCE)
53 }, false);
54 }
55
56 /**
57 * {@inheritDoc}
58 */
59 public function guessMaxLength($class, $property)
60 {
61 $guesser = $this;
62
63 return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) {
64 return $guesser->guessMaxLengthForConstraint($constraint);
65 });
66 }
67
68 /**
69 * {@inheritDoc}
70 */
71 public function guessPattern($class, $property)
72 {
73 $guesser = $this;
74
75 return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) {
76 return $guesser->guessPatternForConstraint($constraint);
77 });
78 }
79
80 /**
81 * Guesses a field class name for a given constraint
82 *
83 * @param Constraint $constraint The constraint to guess for
84 *
85 * @return TypeGuess The guessed field class and options
86 */
87 public function guessTypeForConstraint(Constraint $constraint)
88 {
89 switch (get_class($constraint)) {
90 case 'Symfony\Component\Validator\Constraints\Type':
91 switch ($constraint->type) {
92 case 'array':
93 return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE);
94 case 'boolean':
95 case 'bool':
96 return new TypeGuess('checkbox', array(), Guess::MEDIUM_CONFIDENCE);
97
98 case 'double':
99 case 'float':
100 case 'numeric':
101 case 'real':
102 return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
103
104 case 'integer':
105 case 'int':
106 case 'long':
107 return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
108
109 case '\DateTime':
110 return new TypeGuess('date', array(), Guess::MEDIUM_CONFIDENCE);
111
112 case 'string':
113 return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
114 }
115 break;
116
117 case 'Symfony\Component\Validator\Constraints\Country':
118 return new TypeGuess('country', array(), Guess::HIGH_CONFIDENCE);
119
120 case 'Symfony\Component\Validator\Constraints\Date':
121 return new TypeGuess('date', array('input' => 'string'), Guess::HIGH_CONFIDENCE);
122
123 case 'Symfony\Component\Validator\Constraints\DateTime':
124 return new TypeGuess('datetime', array('input' => 'string'), Guess::HIGH_CONFIDENCE);
125
126 case 'Symfony\Component\Validator\Constraints\Email':
127 return new TypeGuess('email', array(), Guess::HIGH_CONFIDENCE);
128
129 case 'Symfony\Component\Validator\Constraints\File':
130 case 'Symfony\Component\Validator\Constraints\Image':
131 return new TypeGuess('file', array(), Guess::HIGH_CONFIDENCE);
132
133 case 'Symfony\Component\Validator\Constraints\Language':
134 return new TypeGuess('language', array(), Guess::HIGH_CONFIDENCE);
135
136 case 'Symfony\Component\Validator\Constraints\Locale':
137 return new TypeGuess('locale', array(), Guess::HIGH_CONFIDENCE);
138
139 case 'Symfony\Component\Validator\Constraints\Time':
140 return new TypeGuess('time', array('input' => 'string'), Guess::HIGH_CONFIDENCE);
141
142 case 'Symfony\Component\Validator\Constraints\Url':
143 return new TypeGuess('url', array(), Guess::HIGH_CONFIDENCE);
144
145 case 'Symfony\Component\Validator\Constraints\Ip':
146 return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE);
147
148 case 'Symfony\Component\Validator\Constraints\MaxLength':
149 case 'Symfony\Component\Validator\Constraints\MinLength':
150 case 'Symfony\Component\Validator\Constraints\Regex':
151 return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
152
153 case 'Symfony\Component\Validator\Constraints\Min':
154 case 'Symfony\Component\Validator\Constraints\Max':
155 return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE);
156
157 case 'Symfony\Component\Validator\Constraints\MinCount':
158 case 'Symfony\Component\Validator\Constraints\MaxCount':
159 return new TypeGuess('collection', array(), Guess::LOW_CONFIDENCE);
160
161 case 'Symfony\Component\Validator\Constraints\True':
162 case 'Symfony\Component\Validator\Constraints\False':
163 return new TypeGuess('checkbox', array(), Guess::MEDIUM_CONFIDENCE);
164 }
165
166 return null;
167 }
168
169 /**
170 * Guesses whether a field is required based on the given constraint
171 *
172 * @param Constraint $constraint The constraint to guess for
173 *
174 * @return Guess The guess whether the field is required
175 */
176 public function guessRequiredForConstraint(Constraint $constraint)
177 {
178 switch (get_class($constraint)) {
179 case 'Symfony\Component\Validator\Constraints\NotNull':
180 case 'Symfony\Component\Validator\Constraints\NotBlank':
181 case 'Symfony\Component\Validator\Constraints\True':
182 return new ValueGuess(true, Guess::HIGH_CONFIDENCE);
183 }
184
185 return null;
186 }
187
188 /**
189 * Guesses a field's maximum length based on the given constraint
190 *
191 * @param Constraint $constraint The constraint to guess for
192 *
193 * @return Guess The guess for the maximum length
194 */
195 public function guessMaxLengthForConstraint(Constraint $constraint)
196 {
197 switch (get_class($constraint)) {
198 case 'Symfony\Component\Validator\Constraints\MaxLength':
199 return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE);
200
201 case 'Symfony\Component\Validator\Constraints\Type':
202 if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) {
203 return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
204 }
205 break;
206
207 case 'Symfony\Component\Validator\Constraints\Max':
208 return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE);
209 }
210
211 return null;
212 }
213
214 /**
215 * Guesses a field's pattern based on the given constraint
216 *
217 * @param Constraint $constraint The constraint to guess for
218 *
219 * @return Guess The guess for the pattern
220 */
221 public function guessPatternForConstraint(Constraint $constraint)
222 {
223 switch (get_class($constraint)) {
224 case 'Symfony\Component\Validator\Constraints\MinLength':
225 return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE);
226
227 case 'Symfony\Component\Validator\Constraints\Regex':
228 $htmlPattern = $constraint->getHtmlPattern();
229
230 if (null !== $htmlPattern) {
231 return new ValueGuess($htmlPattern, Guess::HIGH_CONFIDENCE);
232 }
233 break;
234
235 case 'Symfony\Component\Validator\Constraints\Min':
236 return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE);
237
238 case 'Symfony\Component\Validator\Constraints\Type':
239 if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) {
240 return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
241 }
242 break;
243 }
244
245 return null;
246 }
247
248 /**
249 * Iterates over the constraints of a property, executes a constraints on
250 * them and returns the best guess
251 *
252 * @param string $class The class to read the constraints from
253 * @param string $property The property for which to find constraints
254 * @param \Closure $closure The closure that returns a guess
255 * for a given constraint
256 * @param mixed $defaultValue The default value assumed if no other value
257 * can be guessed.
258 *
259 * @return Guess The guessed value with the highest confidence
260 */
261 protected function guess($class, $property, \Closure $closure, $defaultValue = null)
262 {
263 $guesses = array();
264 $classMetadata = $this->metadataFactory->getMetadataFor($class);
265
266 if ($classMetadata->hasMemberMetadatas($property)) {
267 $memberMetadatas = $classMetadata->getMemberMetadatas($property);
268
269 foreach ($memberMetadatas as $memberMetadata) {
270 $constraints = $memberMetadata->getConstraints();
271
272 foreach ($constraints as $constraint) {
273 if ($guess = $closure($constraint)) {
274 $guesses[] = $guess;
275 }
276 }
277 }
278
279 if (null !== $defaultValue) {
280 $guesses[] = new ValueGuess($defaultValue, Guess::LOW_CONFIDENCE);
281 }
282 }
283
284 return Guess::getBestGuess($guesses);
285 }
286}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
new file mode 100644
index 00000000..7b96efb4
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
@@ -0,0 +1,106 @@
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
12namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
13
14use Symfony\Component\Form\FormInterface;
15use Symfony\Component\Form\Exception\ErrorMappingException;
16
17/**
18 * @author Bernhard Schussek <bschussek@gmail.com>
19 */
20class MappingRule
21{
22 /**
23 * @var FormInterface
24 */
25 private $origin;
26
27 /**
28 * @var string
29 */
30 private $propertyPath;
31
32 /**
33 * @var string
34 */
35 private $targetPath;
36
37 public function __construct(FormInterface $origin, $propertyPath, $targetPath)
38 {
39 $this->origin = $origin;
40 $this->propertyPath = $propertyPath;
41 $this->targetPath = $targetPath;
42 }
43
44 /**
45 * @return FormInterface
46 */
47 public function getOrigin()
48 {
49 return $this->origin;
50 }
51
52 /**
53 * Matches a property path against the rule path.
54 *
55 * If the rule matches, the form mapped by the rule is returned.
56 * Otherwise this method returns false.
57 *
58 * @param string $propertyPath The property path to match against the rule.
59 *
60 * @return null|FormInterface The mapped form or null.
61 */
62 public function match($propertyPath)
63 {
64 if ($propertyPath === (string) $this->propertyPath) {
65 return $this->getTarget();
66 }
67
68 return null;
69 }
70
71 /**
72 * Matches a property path against a prefix of the rule path.
73 *
74 * @param string $propertyPath The property path to match against the rule.
75 *
76 * @return Boolean Whether the property path is a prefix of the rule or not.
77 */
78 public function isPrefix($propertyPath)
79 {
80 $length = strlen($propertyPath);
81 $prefix = substr($this->propertyPath, 0, $length);
82 $next = isset($this->propertyPath[$length]) ? $this->propertyPath[$length] : null;
83
84 return $prefix === $propertyPath && ('[' === $next || '.' === $next);
85 }
86
87 /**
88 * @return FormInterface
89 *
90 * @throws ErrorMappingException
91 */
92 public function getTarget()
93 {
94 $childNames = explode('.', $this->targetPath);
95 $target = $this->origin;
96
97 foreach ($childNames as $childName) {
98 if (!$target->has($childName)) {
99 throw new ErrorMappingException(sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName()));
100 }
101 $target = $target->get($childName);
102 }
103
104 return $target;
105 }
106}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php
new file mode 100644
index 00000000..ef5c9fad
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php
@@ -0,0 +1,45 @@
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
12namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
13
14use Symfony\Component\Form\FormInterface;
15use Symfony\Component\PropertyAccess\PropertyPath;
16
17/**
18 * @author Bernhard Schussek <bschussek@gmail.com>
19 */
20class RelativePath extends PropertyPath
21{
22 /**
23 * @var FormInterface
24 */
25 private $root;
26
27 /**
28 * @param FormInterface $root
29 * @param string $propertyPath
30 */
31 public function __construct(FormInterface $root, $propertyPath)
32 {
33 parent::__construct($propertyPath);
34
35 $this->root = $root;
36 }
37
38 /**
39 * @return FormInterface
40 */
41 public function getRoot()
42 {
43 return $this->root;
44 }
45}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
new file mode 100644
index 00000000..8a7636c7
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
@@ -0,0 +1,299 @@
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
12namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
13
14use Symfony\Component\Form\FormInterface;
15use Symfony\Component\Form\Util\InheritDataAwareIterator;
16use Symfony\Component\PropertyAccess\PropertyPathIterator;
17use Symfony\Component\PropertyAccess\PropertyPathBuilder;
18use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
19use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPathIterator;
20use Symfony\Component\Form\FormError;
21use Symfony\Component\Validator\ConstraintViolation;
22
23/**
24 * @author Bernhard Schussek <bschussek@gmail.com>
25 */
26class ViolationMapper implements ViolationMapperInterface
27{
28 /**
29 * @var Boolean
30 */
31 private $allowNonSynchronized;
32
33 /**
34 * {@inheritdoc}
35 */
36 public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false)
37 {
38 $this->allowNonSynchronized = $allowNonSynchronized;
39
40 // The scope is the currently found most specific form that
41 // an error should be mapped to. After setting the scope, the
42 // mapper will try to continue to find more specific matches in
43 // the children of scope. If it cannot, the error will be
44 // mapped to this scope.
45 $scope = null;
46
47 $violationPath = null;
48 $relativePath = null;
49 $match = false;
50
51 // Don't create a ViolationPath instance for empty property paths
52 if (strlen($violation->getPropertyPath()) > 0) {
53 $violationPath = new ViolationPath($violation->getPropertyPath());
54 $relativePath = $this->reconstructPath($violationPath, $form);
55 }
56
57 // This case happens if the violation path is empty and thus
58 // the violation should be mapped to the root form
59 if (null === $violationPath) {
60 $scope = $form;
61 }
62
63 // In general, mapping happens from the root form to the leaf forms
64 // First, the rules of the root form are applied to determine
65 // the subsequent descendant. The rules of this descendant are then
66 // applied to find the next and so on, until we have found the
67 // most specific form that matches the violation.
68
69 // If any of the forms found in this process is not synchronized,
70 // mapping is aborted. Non-synchronized forms could not reverse
71 // transform the value entered by the user, thus any further violations
72 // caused by the (invalid) reverse transformed value should be
73 // ignored.
74
75 if (null !== $relativePath) {
76 // Set the scope to the root of the relative path
77 // This root will usually be $form. If the path contains
78 // an unmapped form though, the last unmapped form found
79 // will be the root of the path.
80 $scope = $relativePath->getRoot();
81 $it = new PropertyPathIterator($relativePath);
82
83 while ($this->acceptsErrors($scope) && null !== ($child = $this->matchChild($scope, $it))) {
84 $scope = $child;
85 $it->next();
86 $match = true;
87 }
88 }
89
90 // This case happens if an error happened in the data under a
91 // form inheriting its parent data that does not match any of the
92 // children of that form.
93 if (null !== $violationPath && !$match) {
94 // If we could not map the error to anything more specific
95 // than the root element, map it to the innermost directly
96 // mapped form of the violation path
97 // e.g. "children[foo].children[bar].data.baz"
98 // Here the innermost directly mapped child is "bar"
99
100 $scope = $form;
101 $it = new ViolationPathIterator($violationPath);
102
103 // Note: acceptsErrors() will always return true for forms inheriting
104 // their parent data, because these forms can never be non-synchronized
105 // (they don't do any data transformation on their own)
106 while ($this->acceptsErrors($scope) && $it->valid() && $it->mapsForm()) {
107 if (!$scope->has($it->current())) {
108 // Break if we find a reference to a non-existing child
109 break;
110 }
111
112 $scope = $scope->get($it->current());
113 $it->next();
114 }
115 }
116
117 // Follow dot rules until we have the final target
118 $mapping = $scope->getConfig()->getOption('error_mapping');
119
120 while ($this->acceptsErrors($scope) && isset($mapping['.'])) {
121 $dotRule = new MappingRule($scope, '.', $mapping['.']);
122 $scope = $dotRule->getTarget();
123 $mapping = $scope->getConfig()->getOption('error_mapping');
124 }
125
126 // Only add the error if the form is synchronized
127 if ($this->acceptsErrors($scope)) {
128 $scope->addError(new FormError(
129 $violation->getMessage(),
130 $violation->getMessageTemplate(),
131 $violation->getMessageParameters(),
132 $violation->getMessagePluralization()
133 ));
134 }
135 }
136
137 /**
138 * Tries to match the beginning of the property path at the
139 * current position against the children of the scope.
140 *
141 * If a matching child is found, it is returned. Otherwise
142 * null is returned.
143 *
144 * @param FormInterface $form The form to search.
145 * @param PropertyPathIteratorInterface $it The iterator at its current position.
146 *
147 * @return null|FormInterface The found match or null.
148 */
149 private function matchChild(FormInterface $form, PropertyPathIteratorInterface $it)
150 {
151 // Remember at what property path underneath "data"
152 // we are looking. Check if there is a child with that
153 // path, otherwise increase path by one more piece
154 $chunk = '';
155 $foundChild = null;
156 $foundAtIndex = 0;
157
158 // Construct mapping rules for the given form
159 $rules = array();
160
161 foreach ($form->getConfig()->getOption('error_mapping') as $propertyPath => $targetPath) {
162 // Dot rules are considered at the very end
163 if ('.' !== $propertyPath) {
164 $rules[] = new MappingRule($form, $propertyPath, $targetPath);
165 }
166 }
167
168 // Skip forms inheriting their parent data when iterating the children
169 $childIterator = new \RecursiveIteratorIterator(
170 new InheritDataAwareIterator($form->all())
171 );
172
173 // Make the path longer until we find a matching child
174 while (true) {
175 if (!$it->valid()) {
176 return null;
177 }
178
179 if ($it->isIndex()) {
180 $chunk .= '['.$it->current().']';
181 } else {
182 $chunk .= ('' === $chunk ? '' : '.').$it->current();
183 }
184
185 // Test mapping rules as long as we have any
186 foreach ($rules as $key => $rule) {
187 /* @var MappingRule $rule */
188
189 // Mapping rule matches completely, terminate.
190 if (null !== ($form = $rule->match($chunk))) {
191 return $form;
192 }
193
194 // Keep only rules that have $chunk as prefix
195 if (!$rule->isPrefix($chunk)) {
196 unset($rules[$key]);
197 }
198 }
199
200 // Test children unless we already found one
201 if (null === $foundChild) {
202 foreach ($childIterator as $child) {
203 /* @var FormInterface $child */
204 $childPath = (string) $child->getPropertyPath();
205
206 // Child found, mark as return value
207 if ($chunk === $childPath) {
208 $foundChild = $child;
209 $foundAtIndex = $it->key();
210 }
211 }
212 }
213
214 // Add element to the chunk
215 $it->next();
216
217 // If we reached the end of the path or if there are no
218 // more matching mapping rules, return the found child
219 if (null !== $foundChild && (!$it->valid() || count($rules) === 0)) {
220 // Reset index in case we tried to find mapping
221 // rules further down the path
222 $it->seek($foundAtIndex);
223
224 return $foundChild;
225 }
226 }
227
228 return null;
229 }
230
231 /**
232 * Reconstructs a property path from a violation path and a form tree.
233 *
234 * @param ViolationPath $violationPath The violation path.
235 * @param FormInterface $origin The root form of the tree.
236 *
237 * @return RelativePath The reconstructed path.
238 */
239 private function reconstructPath(ViolationPath $violationPath, FormInterface $origin)
240 {
241 $propertyPathBuilder = new PropertyPathBuilder($violationPath);
242 $it = $violationPath->getIterator();
243 $scope = $origin;
244
245 // Remember the current index in the builder
246 $i = 0;
247
248 // Expand elements that map to a form (like "children[address]")
249 for ($it->rewind(); $it->valid() && $it->mapsForm(); $it->next()) {
250 if (!$scope->has($it->current())) {
251 // Scope relates to a form that does not exist
252 // Bail out
253 break;
254 }
255
256 // Process child form
257 $scope = $scope->get($it->current());
258
259 if ($scope->getConfig()->getInheritData()) {
260 // Form inherits its parent data
261 // Cut the piece out of the property path and proceed
262 $propertyPathBuilder->remove($i);
263 } elseif (!$scope->getConfig()->getMapped()) {
264 // Form is not mapped
265 // Set the form as new origin and strip everything
266 // we have so far in the path
267 $origin = $scope;
268 $propertyPathBuilder->remove(0, $i + 1);
269 $i = 0;
270 } else {
271 /* @var \Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath */
272 $propertyPath = $scope->getPropertyPath();
273
274 if (null === $propertyPath) {
275 // Property path of a mapped form is null
276 // Should not happen, bail out
277 break;
278 }
279
280 $propertyPathBuilder->replace($i, 1, $propertyPath);
281 $i += $propertyPath->getLength();
282 }
283 }
284
285 $finalPath = $propertyPathBuilder->getPropertyPath();
286
287 return null !== $finalPath ? new RelativePath($origin, $finalPath) : null;
288 }
289
290 /**
291 * @param FormInterface $form
292 *
293 * @return Boolean
294 */
295 private function acceptsErrors(FormInterface $form)
296 {
297 return $this->allowNonSynchronized || $form->isSynchronized();
298 }
299}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php
new file mode 100644
index 00000000..eb8907f1
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php
@@ -0,0 +1,33 @@
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
12namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
13
14use Symfony\Component\Form\FormInterface;
15use Symfony\Component\Validator\ConstraintViolation;
16
17/**
18 * @author Bernhard Schussek <bschussek@gmail.com>
19 */
20interface ViolationMapperInterface
21{
22 /**
23 * Maps a constraint violation to a form in the form tree under
24 * the given form.
25 *
26 * @param ConstraintViolation $violation The violation to map.
27 * @param FormInterface $form The root form of the tree
28 * to map it to.
29 * @param Boolean $allowNonSynchronized Whether to allow
30 * mapping to non-synchronized forms.
31 */
32 public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false);
33}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
new file mode 100644
index 00000000..06d09195
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
@@ -0,0 +1,250 @@
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
12namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
13
14use Symfony\Component\Form\Exception\OutOfBoundsException;
15use Symfony\Component\PropertyAccess\PropertyPath;
16use Symfony\Component\PropertyAccess\PropertyPathInterface;
17
18/**
19 * @author Bernhard Schussek <bschussek@gmail.com>
20 */
21class ViolationPath implements \IteratorAggregate, PropertyPathInterface
22{
23 /**
24 * @var array
25 */
26 private $elements = array();
27
28 /**
29 * @var array
30 */
31 private $isIndex = array();
32
33 /**
34 * @var array
35 */
36 private $mapsForm = array();
37
38 /**
39 * @var string
40 */
41 private $pathAsString = '';
42
43 /**
44 * @var integer
45 */
46 private $length = 0;
47
48 /**
49 * Creates a new violation path from a string.
50 *
51 * @param string $violationPath The property path of a {@link ConstraintViolation}
52 * object.
53 */
54 public function __construct($violationPath)
55 {
56 $path = new PropertyPath($violationPath);
57 $elements = $path->getElements();
58 $data = false;
59
60 for ($i = 0, $l = count($elements); $i < $l; ++$i) {
61 if (!$data) {
62 // The element "data" has not yet been passed
63 if ('children' === $elements[$i] && $path->isProperty($i)) {
64 // Skip element "children"
65 ++$i;
66
67 // Next element must exist and must be an index
68 // Otherwise consider this the end of the path
69 if ($i >= $l || !$path->isIndex($i)) {
70 break;
71 }
72
73 $this->elements[] = $elements[$i];
74 $this->isIndex[] = true;
75 $this->mapsForm[] = true;
76 } elseif ('data' === $elements[$i] && $path->isProperty($i)) {
77 // Skip element "data"
78 ++$i;
79
80 // End of path
81 if ($i >= $l) {
82 break;
83 }
84
85 $this->elements[] = $elements[$i];
86 $this->isIndex[] = $path->isIndex($i);
87 $this->mapsForm[] = false;
88 $data = true;
89 } else {
90 // Neither "children" nor "data" property found
91 // Consider this the end of the path
92 break;
93 }
94 } else {
95 // Already after the "data" element
96 // Pick everything as is
97 $this->elements[] = $elements[$i];
98 $this->isIndex[] = $path->isIndex($i);
99 $this->mapsForm[] = false;
100 }
101 }
102
103 $this->length = count($this->elements);
104
105 $this->buildString();
106 }
107
108 /**
109 * {@inheritdoc}
110 */
111 public function __toString()
112 {
113 return $this->pathAsString;
114 }
115
116 /**
117 * {@inheritdoc}
118 */
119 public function getLength()
120 {
121 return $this->length;
122 }
123
124 /**
125 * {@inheritdoc}
126 */
127 public function getParent()
128 {
129 if ($this->length <= 1) {
130 return null;
131 }
132
133 $parent = clone $this;
134
135 --$parent->length;
136 array_pop($parent->elements);
137 array_pop($parent->isIndex);
138 array_pop($parent->mapsForm);
139
140 $parent->buildString();
141
142 return $parent;
143 }
144
145 /**
146 * {@inheritdoc}
147 */
148 public function getElements()
149 {
150 return $this->elements;
151 }
152
153 /**
154 * {@inheritdoc}
155 */
156 public function getElement($index)
157 {
158 if (!isset($this->elements[$index])) {
159 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
160 }
161
162 return $this->elements[$index];
163 }
164
165 /**
166 * {@inheritdoc}
167 */
168 public function isProperty($index)
169 {
170 if (!isset($this->isIndex[$index])) {
171 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
172 }
173
174 return !$this->isIndex[$index];
175 }
176
177 /**
178 * {@inheritdoc}
179 */
180 public function isIndex($index)
181 {
182 if (!isset($this->isIndex[$index])) {
183 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
184 }
185
186 return $this->isIndex[$index];
187 }
188
189 /**
190 * Returns whether an element maps directly to a form.
191 *
192 * Consider the following violation path:
193 *
194 * <code>
195 * children[address].children[office].data.street
196 * </code>
197 *
198 * In this example, "address" and "office" map to forms, while
199 * "street does not.
200 *
201 * @param integer $index The element index.
202 *
203 * @return Boolean Whether the element maps to a form.
204 *
205 * @throws OutOfBoundsException If the offset is invalid.
206 */
207 public function mapsForm($index)
208 {
209 if (!isset($this->mapsForm[$index])) {
210 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
211 }
212
213 return $this->mapsForm[$index];
214 }
215
216 /**
217 * Returns a new iterator for this path
218 *
219 * @return ViolationPathIterator
220 */
221 public function getIterator()
222 {
223 return new ViolationPathIterator($this);
224 }
225
226 /**
227 * Builds the string representation from the elements.
228 */
229 private function buildString()
230 {
231 $this->pathAsString = '';
232 $data = false;
233
234 foreach ($this->elements as $index => $element) {
235 if ($this->mapsForm[$index]) {
236 $this->pathAsString .= ".children[$element]";
237 } elseif (!$data) {
238 $this->pathAsString .= '.data'.($this->isIndex[$index] ? "[$element]" : ".$element");
239 $data = true;
240 } else {
241 $this->pathAsString .= $this->isIndex[$index] ? "[$element]" : ".$element";
242 }
243 }
244
245 if ('' !== $this->pathAsString) {
246 // remove leading dot
247 $this->pathAsString = substr($this->pathAsString, 1);
248 }
249 }
250}
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php
new file mode 100644
index 00000000..50baa453
--- /dev/null
+++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php
@@ -0,0 +1,30 @@
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
12namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
13
14use Symfony\Component\PropertyAccess\PropertyPathIterator;
15
16/**
17 * @author Bernhard Schussek <bschussek@gmail.com>
18 */
19class ViolationPathIterator extends PropertyPathIterator
20{
21 public function __construct(ViolationPath $violationPath)
22 {
23 parent::__construct($violationPath);
24 }
25
26 public function mapsForm()
27 {
28 return $this->path->mapsForm($this->key());
29 }
30}