Skip to content
Browse files

[Form] Allowed native framework errors to be mapped as well

  • Loading branch information...
1 parent 39bb151 commit d8ef71baf1963704d732ac9a2bb7c06ac8c87bea @webmozart webmozart committed
View
2 CHANGELOG.md
@@ -83,3 +83,5 @@ CHANGELOG
* `getErrorBubbling`
* `getNormTransformers`
* `getClientTransformers`
+ * deprecated the option "validation_constraint" in favor of the new
+ option "constraints"
View
68 Extension/Core/EventListener/ValidationListener.php
@@ -1,68 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Extension\Core\EventListener;
-
-use Symfony\Component\Form\FormError;
-use Symfony\Component\Form\FormEvents;
-use Symfony\Component\Form\Event\DataEvent;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * @author Bernhard Schussek <bschussek@gmail.com>
- */
-class ValidationListener implements EventSubscriberInterface
-{
- /**
- * {@inheritdoc}
- */
- static public function getSubscribedEvents()
- {
- return array(FormEvents::POST_BIND => 'validateForm');
- }
-
- public function validateForm(DataEvent $event)
- {
- $form = $event->getForm();
-
- if (!$form->isSynchronized()) {
- $form->addError(new FormError(
- $form->getAttribute('invalid_message'),
- $form->getAttribute('invalid_message_parameters')
- ));
- }
-
- if (count($form->getExtraData()) > 0) {
- $form->addError(new FormError('This form should not contain extra fields.'));
- }
-
- if ($form->isRoot() && isset($_SERVER['CONTENT_LENGTH'])) {
- $length = (int) $_SERVER['CONTENT_LENGTH'];
- $max = trim(ini_get('post_max_size'));
-
- if ('' !== $max) {
- switch (strtolower(substr($max, -1))) {
- // The 'G' modifier is available since PHP 5.1.0
- case 'g':
- $max *= 1024;
- case 'm':
- $max *= 1024;
- case 'k':
- $max *= 1024;
- }
-
- if ($length > $max) {
- $form->addError(new FormError('The uploaded file was too large. Please try to upload a smaller file'));
- }
- }
- }
- }
-}
View
4 Extension/Core/Type/DateTimeType.php
@@ -51,8 +51,6 @@ public function buildForm(FormBuilder $builder, array $options)
'days',
'empty_value',
'required',
- 'invalid_message',
- 'invalid_message_parameters',
'translation_domain',
)));
$timeOptions = array_intersect_key($options, array_flip(array(
@@ -62,8 +60,6 @@ public function buildForm(FormBuilder $builder, array $options)
'with_seconds',
'empty_value',
'required',
- 'invalid_message',
- 'invalid_message_parameters',
'translation_domain',
)));
View
44 Extension/Core/Type/FormType.php
@@ -18,7 +18,6 @@
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
-use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Form\Exception\FormException;
@@ -50,19 +49,15 @@ public function buildForm(FormBuilder $builder, array $options)
->setVirtual($options['virtual'])
->setAttribute('read_only', $options['read_only'])
->setAttribute('by_reference', $options['by_reference'])
- ->setAttribute('error_mapping', $options['error_mapping'])
->setAttribute('max_length', $options['max_length'])
->setAttribute('pattern', $options['pattern'])
->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName()))
->setAttribute('attr', $options['attr'])
->setAttribute('label_attr', $options['label_attr'])
- ->setAttribute('invalid_message', $options['invalid_message'])
- ->setAttribute('invalid_message_parameters', $options['invalid_message_parameters'])
->setAttribute('translation_domain', $options['translation_domain'])
->setAttribute('single_control', $options['single_control'])
->setData($options['data'])
->setDataMapper(new PropertyPathMapper())
- ->addEventSubscriber(new ValidationListener())
;
if ($options['trim']) {
@@ -197,27 +192,24 @@ public function getDefaultOptions()
};
return array(
- 'data' => null,
- 'data_class' => $dataClass,
- 'empty_data' => $emptyData,
- 'trim' => true,
- 'required' => true,
- 'read_only' => false,
- 'disabled' => false,
- 'max_length' => null,
- 'pattern' => null,
- 'property_path' => null,
- 'mapped' => $mapped,
- 'by_reference' => true,
- 'error_bubbling' => $errorBubbling,
- 'error_mapping' => array(),
- 'label' => null,
- 'attr' => array(),
- 'label_attr' => array(),
- 'virtual' => false,
- 'single_control' => false,
- 'invalid_message' => 'This value is not valid.',
- 'invalid_message_parameters' => array(),
+ 'data' => null,
+ 'data_class' => $dataClass,
+ 'empty_data' => $emptyData,
+ 'trim' => true,
+ 'required' => true,
+ 'read_only' => false,
+ 'disabled' => false,
+ 'max_length' => null,
+ 'pattern' => null,
+ 'property_path' => null,
+ 'mapped' => $mapped,
+ 'by_reference' => true,
+ 'error_bubbling' => $errorBubbling,
+ 'label' => null,
+ 'attr' => array(),
+ 'label_attr' => array(),
+ 'virtual' => false,
+ 'single_control' => false,
'translation_domain' => 'messages',
);
}
View
6 Extension/Core/Type/RepeatedType.php
@@ -42,11 +42,6 @@ public function buildForm(FormBuilder $builder, array $options)
*/
public function getDefaultOptions()
{
- // Map errors to the first field
- $errorMapping = function (Options $options) {
- return array('.' => $options['first_name']);
- };
-
return array(
'type' => 'text',
'options' => array(),
@@ -55,7 +50,6 @@ public function getDefaultOptions()
'first_name' => 'first',
'second_name' => 'second',
'error_bubbling' => false,
- 'error_mapping' => $errorMapping,
);
}
View
33 Extension/Validator/Constraints/Form.php
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Constraints;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class Form extends Constraint
+{
+ /**
+ * Violation code marking an invalid form.
+ */
+ const ERR_INVALID = 1;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTargets()
+ {
+ return self::CLASS_CONSTRAINT;
+ }
+}
View
208 Extension/Validator/Constraints/FormValidator.php
@@ -0,0 +1,208 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Constraints;
+
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\Extension\Validator\Util\ServerParams;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FormValidator extends ConstraintValidator
+{
+ /**
+ * @var ServerParams
+ */
+ private $serverParams;
+
+ /**
+ * Creates a validator with the given server parameters.
+ *
+ * @param ServerParams $params The server parameters. Default
+ * parameters are created if null.
+ */
+ public function __construct(ServerParams $params = null)
+ {
+ if (null === $params) {
+ $params = new ServerParams();
+ }
+
+ $this->serverParams = $params;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($form, Constraint $constraint)
+ {
+ if (!$form instanceof FormInterface) {
+ return;
+ }
+
+ /* @var FormInterface $form */
+
+ $path = $this->context->getPropertyPath();
+ $graphWalker = $this->context->getGraphWalker();
+ $groups = $this->getValidationGroups($form);
+
+ if (!empty($path)) {
+ $path .= '.';
+ }
+
+ if ($form->isSynchronized()) {
+ // Validate the form data only if transformation succeeded
+
+ // Validate the data against its own constraints
+ if (self::allowDataWalking($form)) {
+ foreach ($groups as $group) {
+ $graphWalker->walkReference($form->getData(), $group, $path . 'data', true);
+ }
+ }
+
+ // Validate the data against the constraints defined
+ // in the form
+ $constraints = $form->getAttribute('constraints');
+ foreach ($constraints as $constraint) {
+ foreach ($groups as $group) {
+ $graphWalker->walkConstraint($constraint, $form->getData(), $group, $path . 'data');
+ }
+ }
+ } else {
+ // Mark the form with an error if it is not synchronized
+ $this->context->addViolation(
+ $form->getAttribute('invalid_message'),
+ array('{{ value }}' => (string) $form->getClientData()),
+ $form->getClientData(),
+ null,
+ Form::ERR_INVALID
+ );
+ }
+
+ // Mark the form with an error if it contains extra fields
+ if (count($form->getExtraData()) > 0) {
+ $this->context->addViolation(
+ $form->getAttribute('extra_fields_message'),
+ array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))),
+ $form->getExtraData()
+ );
+ }
+
+ // Mark the form with an error if the uploaded size was too large
+ $length = $this->serverParams->getContentLength();
+
+ if ($form->isRoot() && null !== $length) {
+ $max = strtoupper(trim($this->serverParams->getPostMaxSize()));
+
+ if ('' !== $max) {
+ $maxLength = (int) $max;
+
+ switch (substr($max, -1)) {
+ // The 'G' modifier is available since PHP 5.1.0
+ case 'G':
+ $maxLength *= pow(1024, 3);
+ break;
+ case 'M':
+ $maxLength *= pow(1024, 2);
+ break;
+ case 'K':
+ $maxLength *= 1024;
+ break;
+ }
+
+ if ($length > $maxLength) {
+ $this->context->addViolation(
+ $form->getAttribute('post_max_size_message'),
+ array('{{ max }}' => $max),
+ $length
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether the data of a form may be walked.
+ *
+ * @param FormInterface $form The form to test.
+ *
+ * @return Boolean Whether the graph walker may walk the data.
+ */
+ private function allowDataWalking(FormInterface $form)
+ {
+ $data = $form->getData();
+
+ // Scalar values cannot have mapped constraints
+ if (!is_object($data) && !is_array($data)) {
+ return false;
+ }
+
+ // Root forms are always validated
+ if ($form->isRoot()) {
+ return true;
+ }
+
+ // Non-root forms are validated if validation cascading
+ // is enabled in all ancestor forms
+ $parent = $form->getParent();
+
+ while (null !== $parent) {
+ if (!$parent->getAttribute('cascade_validation')) {
+ return false;
+ }
+
+ $parent = $parent->getParent();
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the validation groups of the given form.
+ *
+ * @param FormInterface $form The form.
+ *
+ * @return array The validation groups.
+ */
+ private function getValidationGroups(FormInterface $form)
+ {
+ $groups = null;
+
+ if ($form->hasAttribute('validation_groups')) {
+ $groups = $form->getAttribute('validation_groups');
+
+ if (is_callable($groups)) {
+ $groups = (array) call_user_func($groups, $form);
+ }
+ }
+
+ $currentForm = $form;
+ while (!$groups && $currentForm->hasParent()) {
+ $currentForm = $currentForm->getParent();
+
+ if ($currentForm->hasAttribute('validation_groups')) {
+ $groups = $currentForm->getAttribute('validation_groups');
+
+ if (is_callable($groups)) {
+ $groups = (array) call_user_func($groups, $currentForm);
+ }
+ }
+ }
+
+ if (null === $groups) {
+ $groups = array('Default');
+ }
+
+ return (array) $groups;
+ }
+}
View
154 Extension/Validator/EventListener/DelegatingValidationListener.php
@@ -1,154 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Extension\Validator\EventListener;
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
-use Symfony\Component\Form\FormInterface;
-use Symfony\Component\Form\FormError;
-use Symfony\Component\Form\FormEvents;
-use Symfony\Component\Form\Event\DataEvent;
-use Symfony\Component\Form\Exception\FormException;
-use Symfony\Component\Form\Util\PropertyPath;
-use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ValidatorInterface;
-use Symfony\Component\Validator\ExecutionContext;
-
-/**
- * @author Bernhard Schussek <bschussek@gmail.com>
- */
-class DelegatingValidationListener implements EventSubscriberInterface
-{
- private $validator;
-
- /**
- * {@inheritdoc}
- */
- static public function getSubscribedEvents()
- {
- return array(FormEvents::POST_BIND => 'validateForm');
- }
-
- /**
- * Validates the data of a form
- *
- * This method is called automatically during the validation process.
- *
- * @param FormInterface $form The validated form
- * @param ExecutionContext $context The current validation context
- */
- static public function validateFormData(FormInterface $form, ExecutionContext $context)
- {
- if (is_object($form->getData()) || is_array($form->getData())) {
- $propertyPath = $context->getPropertyPath();
- $graphWalker = $context->getGraphWalker();
-
- // Adjust the property path accordingly
- if (!empty($propertyPath)) {
- $propertyPath .= '.';
- }
-
- $propertyPath .= 'data';
-
- foreach (self::getFormValidationGroups($form) as $group) {
- $graphWalker->walkReference($form->getData(), $group, $propertyPath, true);
- }
- }
- }
-
- static public function validateFormChildren(FormInterface $form, ExecutionContext $context)
- {
- if ($form->getAttribute('cascade_validation')) {
- $propertyPath = $context->getPropertyPath();
- $graphWalker = $context->getGraphWalker();
-
- // Adjust the property path accordingly
- if (!empty($propertyPath)) {
- $propertyPath .= '.';
- }
-
- $propertyPath .= 'children';
-
- $graphWalker->walkReference($form->getChildren(), Constraint::DEFAULT_GROUP, $propertyPath, true);
- }
- }
-
- static protected function getFormValidationGroups(FormInterface $form)
- {
- $groups = null;
-
- if ($form->hasAttribute('validation_groups')) {
- $groups = $form->getAttribute('validation_groups');
-
- if (is_callable($groups)) {
- $groups = (array) call_user_func($groups, $form);
- }
- }
-
- $currentForm = $form;
- while (!$groups && $currentForm->hasParent()) {
- $currentForm = $currentForm->getParent();
-
- if ($currentForm->hasAttribute('validation_groups')) {
- $groups = $currentForm->getAttribute('validation_groups');
-
- if (is_callable($groups)) {
- $groups = (array) call_user_func($groups, $currentForm);
- }
- }
- }
-
- if (null === $groups) {
- $groups = array('Default');
- }
-
- return (array) $groups;
- }
-
- public function __construct(ValidatorInterface $validator)
- {
- $this->validator = $validator;
- }
-
- /**
- * Validates the form and its domain object.
- *
- * @param DataEvent $event The event object
- */
- public function validateForm(DataEvent $event)
- {
- $form = $event->getForm();
-
- if ($form->isRoot()) {
- // Validate the form in group "Default"
- // Validation of the data in the custom group is done by validateData(),
- // which is constrained by the Execute constraint
- if ($form->hasAttribute('validation_constraint')) {
- $violations = $this->validator->validateValue(
- $form->getData(),
- $form->getAttribute('validation_constraint'),
- self::getFormValidationGroups($form)
- );
- } else {
- $violations = $this->validator->validate($form);
- }
-
- if (count($violations) > 0) {
- $mapper = new ViolationMapper();
-
- foreach ($violations as $violation) {
- $mapper->mapViolation($violation, $form);
- }
- }
- }
- }
-}
View
75 Extension/Validator/EventListener/ValidationListener.php
@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\EventListener;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Validator\ConstraintViolation;
+use Symfony\Component\Form\Extension\Validator\Constraints\Form;
+use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormError;
+use Symfony\Component\Form\FormEvents;
+use Symfony\Component\Form\Event\DataEvent;
+use Symfony\Component\Form\Exception\FormException;
+use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ValidatorInterface;
+use Symfony\Component\Validator\ExecutionContext;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class ValidationListener implements EventSubscriberInterface
+{
+ private $validator;
+
+ private $violationMapper;
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function getSubscribedEvents()
+ {
+ return array(FormEvents::POST_BIND => 'validateForm');
+ }
+
+ public function __construct(ValidatorInterface $validator, ViolationMapperInterface $violationMapper)
+ {
+ $this->validator = $validator;
+ $this->violationMapper = $violationMapper;
+ }
+
+ /**
+ * Validates the form and its domain object.
+ *
+ * @param DataEvent $event The event object
+ */
+ public function validateForm(DataEvent $event)
+ {
+ $form = $event->getForm();
+
+ if ($form->isRoot()) {
+ // Validate the form in group "Default"
+ $violations = $this->validator->validate($form);
+
+ if (count($violations) > 0) {
+ foreach ($violations as $violation) {
+ // Allow the "invalid" constraint to be put onto
+ // non-synchzronized forms
+ $allowNonSynchronized = Form::ERR_INVALID === $violation->getCode();
+
+ $this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
+ }
+ }
+ }
+ }
+}
View
50 Extension/Validator/Type/FormTypeValidatorExtension.php
@@ -13,21 +13,35 @@
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilder;
-use Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener;
+use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
+use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
use Symfony\Component\Validator\ValidatorInterface;
+use Symfony\Component\OptionsResolver\Options;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormTypeValidatorExtension extends AbstractTypeExtension
{
+ /**
+ * @var ValidatorInterface
+ */
private $validator;
+ /**
+ * @var ViolationMapper
+ */
+ private $violationMapper;
+
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
+ $this->violationMapper = new ViolationMapper();
}
+ /**
+ * {@inheritdoc}
+ */
public function buildForm(FormBuilder $builder, array $options)
{
if (empty($options['validation_groups'])) {
@@ -38,23 +52,49 @@ public function buildForm(FormBuilder $builder, array $options)
: (array) $options['validation_groups'];
}
+ // Objects, when casted to an array, are split into their properties
+ $constraints = is_object($options['constraints'])
+ ? array($options['constraints'])
+ : (array) $options['constraints'];
+
$builder
+ ->setAttribute('error_mapping', $options['error_mapping'])
->setAttribute('validation_groups', $options['validation_groups'])
- ->setAttribute('validation_constraint', $options['validation_constraint'])
+ ->setAttribute('constraints', $constraints)
->setAttribute('cascade_validation', $options['cascade_validation'])
- ->addEventSubscriber(new DelegatingValidationListener($this->validator))
+ ->setAttribute('invalid_message', $options['invalid_message'])
+ ->setAttribute('extra_fields_message', $options['extra_fields_message'])
+ ->setAttribute('post_max_size_message', $options['post_max_size_message'])
+ ->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper))
;
}
+ /**
+ * {@inheritdoc}
+ */
public function getDefaultOptions()
{
+ // BC clause
+ $constraints = function (Options $options) {
+ return $options['validation_constraint'];
+ };
+
return array(
- 'validation_groups' => null,
+ 'error_mapping' => array(),
+ 'validation_groups' => null,
+ // "validation_constraint" is deprecated. Use "constraints".
'validation_constraint' => null,
- 'cascade_validation' => false,
+ 'constraints' => $constraints,
+ 'cascade_validation' => false,
+ 'invalid_message' => 'This value is not valid.',
+ 'extra_fields_message' => 'This form should not contain extra fields.',
+ 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
);
}
+ /**
+ * {@inheritdoc}
+ */
public function getExtendedType()
{
return 'form';
View
44 Extension/Validator/Type/RepeatedTypeValidatorExtension.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Type;
+
+use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\OptionsResolver\Options;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class RepeatedTypeValidatorExtension extends AbstractTypeExtension
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getDefaultOptions()
+ {
+ // Map errors to the first field
+ $errorMapping = function (Options $options) {
+ return array('.' => $options['first_name']);
+ };
+
+ return array(
+ 'error_mapping' => $errorMapping,
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getExtendedType()
+ {
+ return 'repeated';
+ }
+}
View
41 Extension/Validator/Util/ServerParams.php
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\Util;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class ServerParams
+{
+ /**
+ * Returns the "post_max_size" ini setting.
+ *
+ * @return string The value of the ini setting.
+ */
+ public function getPostMaxSize()
+ {
+ return ini_get('post_max_size');
+ }
+
+ /**
+ * Returns the content length of the request.
+ *
+ * @return mixed The request content length.
+ */
+ public function getContentLength()
+ {
+ return isset($_SERVER['CONTENT_LENGTH'])
+ ? (int) $_SERVER['CONTENT_LENGTH']
+ : null;
+ }
+
+}
View
5 Extension/Validator/ValidatorExtension.php
@@ -12,9 +12,9 @@
namespace Symfony\Component\Form\Extension\Validator;
use Symfony\Component\Form\Extension\Validator\Type;
+use Symfony\Component\Form\Extension\Validator\Constraints\Form;
use Symfony\Component\Form\AbstractExtension;
use Symfony\Component\Validator\ValidatorInterface;
-use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\Valid;
class ValidatorExtension extends AbstractExtension
@@ -26,7 +26,7 @@ public function __construct(ValidatorInterface $validator)
$this->validator = $validator;
$metadata = $this->validator->getMetadataFactory()->getClassMetadata('Symfony\Component\Form\Form');
- $metadata->addConstraint(new Callback(array(array('Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener', 'validateFormData'))));
+ $metadata->addConstraint(new Form());
$metadata->addPropertyConstraint('children', new Valid());
}
@@ -39,6 +39,7 @@ protected function loadTypeExtensions()
{
return array(
new Type\FormTypeValidatorExtension($this->validator),
+ new Type\RepeatedTypeValidatorExtension(),
);
}
}
View
34 Extension/Validator/ViolationMapper/ViolationMapper.php
@@ -23,7 +23,7 @@
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
-class ViolationMapper
+class ViolationMapper implements ViolationMapperInterface
{
/**
* @var FormInterface
@@ -41,15 +41,17 @@ class ViolationMapper
private $rules = array();
/**
- * Maps a constraint violation to a form in the form tree under
- * the given form.
- *
- * @param ConstraintViolation $violation The violation to map.
- * @param FormInterface $form The root form of the tree
- * to map it to.
+ * @var Boolean
*/
- public function mapViolation(ConstraintViolation $violation, FormInterface $form)
+ private $allowNonSynchronized;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false)
{
+ $this->allowNonSynchronized = $allowNonSynchronized;
+
$violationPath = new ViolationPath($violation->getPropertyPath());
$relativePath = $this->reconstructPath($violationPath, $form);
$match = false;
@@ -74,7 +76,7 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form
$this->setScope($relativePath->getRoot());
$it = new PropertyPathIterator($relativePath);
- while ($this->scope->isSynchronized() && null !== ($child = $this->matchChild($it))) {
+ while ($this->isValidScope() && null !== ($child = $this->matchChild($it))) {
$this->setScope($child);
$it->next();
$match = true;
@@ -95,7 +97,7 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form
// The overhead of setScope() is not needed anymore here
$this->scope = $form;
- while ($this->scope->isSynchronized() && $it->valid() && $it->mapsForm()) {
+ while ($this->isValidScope() && $it->valid() && $it->mapsForm()) {
if (!$this->scope->has($it->current())) {
// Break if we find a reference to a non-existing child
break;
@@ -109,14 +111,14 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form
// Follow dot rules until we have the final target
$mapping = $this->scope->getAttribute('error_mapping');
- while ($this->scope->isSynchronized() && isset($mapping['.'])) {
+ while ($this->isValidScope() && isset($mapping['.'])) {
$dotRule = new MappingRule($this->scope, '.', $mapping['.']);
$this->scope = $dotRule->getTarget();
$mapping = $this->scope->getAttribute('error_mapping');
}
// Only add the error if the form is synchronized
- if ($this->scope->isSynchronized()) {
+ if ($this->isValidScope()) {
$this->scope->addError(new FormError(
$violation->getMessageTemplate(),
$violation->getMessageParameters(),
@@ -286,4 +288,12 @@ private function setScope(FormInterface $form)
}
}
}
+
+ /**
+ * @return Boolean
+ */
+ private function isValidScope()
+ {
+ return $this->allowNonSynchronized || $this->scope->isSynchronized();
+ }
}
View
33 Extension/Validator/ViolationMapper/ViolationMapperInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
+
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Validator\ConstraintViolation;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface ViolationMapperInterface
+{
+ /**
+ * Maps a constraint violation to a form in the form tree under
+ * the given form.
+ *
+ * @param ConstraintViolation $violation The violation to map.
+ * @param FormInterface $form The root form of the tree
+ * to map it to.
+ * @param Boolean $allowNonSynchronized Whether to allow
+ * mapping to non-synchronized forms.
+ */
+ function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false);
+}
View
14 Resources/config/validation.xml
@@ -5,15 +5,9 @@
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="Symfony\Component\Form\Form">
- <constraint name="Callback">
- <value>
- <value>Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener</value>
- <value>validateFormData</value>
- </value>
- <value>
- <value>Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener</value>
- <value>validateFormChildren</value>
- </value>
- </constraint>
+ <constraint name="Symfony\Component\Form\Extension\Validator\Constraints\Form" />
+ <property name="children">
+ <constraint name="Valid" />
+ </property>
</class>
</constraint-mapping>
View
26 Tests/Extension/Core/Type/DateTimeTypeTest.php
@@ -226,32 +226,6 @@ public function testSubmit_differentPattern()
$this->assertDateTimeEquals($dateTime, $form->getData());
}
- public function testSubmit_invalidDateTime()
- {
- $form = $this->factory->create('datetime', null, array(
- 'invalid_message' => 'Customized invalid message',
- // Only possible with the "text" widget, because the "choice"
- // widget automatically fields invalid values
- 'widget' => 'text',
- ));
-
- $form->bind(array(
- 'date' => array(
- 'day' => '31',
- 'month' => '9',
- 'year' => '2010',
- ),
- 'time' => array(
- 'hour' => '25',
- 'minute' => '4',
- ),
- ));
-
- $this->assertFalse($form->isValid());
- $this->assertEquals(array(new FormError('Customized invalid message', array())), $form['date']->getErrors());
- $this->assertEquals(array(new FormError('Customized invalid message', array())), $form['time']->getErrors());
- }
-
// Bug fix
public function testInitializeWithDateTime()
{
View
694 Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -0,0 +1,694 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints;
+
+use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\Exception\TransformationFailedException;
+use Symfony\Component\Form\CallbackTransformer;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\Extension\Validator\Constraints\Form;
+use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator;
+use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\GlobalExecutionContext;
+use Symfony\Component\Validator\ExecutionContext;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FormValidatorTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $dispatcher;
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $factory;
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $serverParams;
+
+ /**
+ * @var FormValidator
+ */
+ private $validator;
+
+ protected function setUp()
+ {
+ if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
+ $this->markTestSkipped('The "EventDispatcher" component is not available');
+ }
+
+ $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
+ $this->serverParams = $this->getMock('Symfony\Component\Form\Extension\Validator\Util\ServerParams');
+ $this->validator = new FormValidator($this->serverParams);
+ }
+
+ public function testValidate()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', array('group1', 'group2'))
+ ->setData($object)
+ ->getForm();
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($object, 'group1', 'data', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($object, 'group2', 'data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testValidateConstraints()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
+ $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
+
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', array('group1', 'group2'))
+ ->setAttribute('constraints', array($constraint1, $constraint2))
+ ->setData($object)
+ ->getForm();
+
+ // First default constraints
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($object, 'group1', 'data', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($object, 'group2', 'data', true);
+
+ // Then custom constraints
+ $graphWalker->expects($this->at(2))
+ ->method('walkConstraint')
+ ->with($constraint1, $object, 'group1', 'data');
+ $graphWalker->expects($this->at(3))
+ ->method('walkConstraint')
+ ->with($constraint1, $object, 'group2', 'data');
+ $graphWalker->expects($this->at(4))
+ ->method('walkConstraint')
+ ->with($constraint2, $object, 'group1', 'data');
+ $graphWalker->expects($this->at(5))
+ ->method('walkConstraint')
+ ->with($constraint2, $object, 'group2', 'data');
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testDontValidateIfParentWithoutCascadeValidation()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+
+ $parent = $this->getBuilder()
+ ->setAttribute('cascade_validation', false)
+ ->getForm();
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', array('group1', 'group2'))
+ ->getForm();
+ $parent->add($form);
+
+ $form->setData($object);
+
+ $graphWalker->expects($this->never())
+ ->method('walkReference');
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testValidateConstraintsEvenIfNoCascadeValidation()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
+ $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
+
+ $parent = $this->getBuilder()
+ ->setAttribute('cascade_validation', false)
+ ->getForm();
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', array('group1', 'group2'))
+ ->setAttribute('constraints', array($constraint1, $constraint2))
+ ->setData($object)
+ ->getForm();
+ $parent->add($form);
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkConstraint')
+ ->with($constraint1, $object, 'group1', 'data');
+ $graphWalker->expects($this->at(1))
+ ->method('walkConstraint')
+ ->with($constraint1, $object, 'group2', 'data');
+ $graphWalker->expects($this->at(2))
+ ->method('walkConstraint')
+ ->with($constraint2, $object, 'group1', 'data');
+ $graphWalker->expects($this->at(3))
+ ->method('walkConstraint')
+ ->with($constraint2, $object, 'group2', 'data');
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testDontValidateIfNotSynchronized()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setData($object)
+ ->setAttribute('invalid_message', 'Invalid!')
+ ->appendClientTransformer(new CallbackTransformer(
+ function ($data) { return $data; },
+ function () { throw new TransformationFailedException(); }
+ ))
+ ->getForm();
+
+ // Launch transformer
+ $form->bind(array());
+
+ $graphWalker->expects($this->never())
+ ->method('walkReference');
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Invalid!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testDontValidateConstraintsIfNotSynchronized()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
+ $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
+
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setData($object)
+ ->setAttribute('validation_groups', array('group1', 'group2'))
+ ->setAttribute('constraints', array($constraint1, $constraint2))
+ ->appendClientTransformer(new CallbackTransformer(
+ function ($data) { return $data; },
+ function () { throw new TransformationFailedException(); }
+ ))
+ ->getForm();
+
+ // Launch transformer
+ $form->bind(array());
+
+ $graphWalker->expects($this->never())
+ ->method('walkReference');
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testHandleCallbackValidationGroups()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', array($this, 'getValidationGroups'))
+ ->setData($object)
+ ->getForm();
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($object, 'group1', 'data', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($object, 'group2', 'data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testHandleClosureValidationGroups()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', function(FormInterface $form){
+ return array('group1', 'group2');
+ })
+ ->setData($object)
+ ->getForm();
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($object, 'group1', 'data', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($object, 'group2', 'data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testUseInheritedValidationGroup()
+ {
+ $context = $this->getExecutionContext('foo.bar');
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+
+ $parent = $this->getBuilder()
+ ->setAttribute('validation_groups', 'group')
+ ->setAttribute('cascade_validation', true)
+ ->getForm();
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', null)
+ ->getForm();
+ $parent->add($form);
+
+ $form->setData($object);
+
+ $graphWalker->expects($this->once())
+ ->method('walkReference')
+ ->with($object, 'group', 'foo.bar.data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testUseInheritedCallbackValidationGroup()
+ {
+ $context = $this->getExecutionContext('foo.bar');
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+
+ $parent = $this->getBuilder()
+ ->setAttribute('validation_groups', array($this, 'getValidationGroups'))
+ ->setAttribute('cascade_validation', true)
+ ->getForm();
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', null)
+ ->getForm();
+ $parent->add($form);
+
+ $form->setData($object);
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($object, 'group1', 'foo.bar.data', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($object, 'group2', 'foo.bar.data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testUseInheritedClosureValidationGroup()
+ {
+ $context = $this->getExecutionContext('foo.bar');
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+
+ $parent = $this->getBuilder()
+ ->setAttribute('validation_groups', function(FormInterface $form){
+ return array('group1', 'group2');
+ })
+ ->setAttribute('cascade_validation', true)
+ ->getForm();
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setAttribute('validation_groups', null)
+ ->getForm();
+ $parent->add($form);
+
+ $form->setData($object);
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($object, 'group1', 'foo.bar.data', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($object, 'group2', 'foo.bar.data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testAppendPropertyPath()
+ {
+ $context = $this->getExecutionContext('foo.bar');
+ $graphWalker = $context->getGraphWalker();
+ $object = $this->getMock('\stdClass');
+ $form = $this->getBuilder('name', '\stdClass')
+ ->setData($object)
+ ->getForm();
+
+ $graphWalker->expects($this->once())
+ ->method('walkReference')
+ ->with($object, 'Default', 'foo.bar.data', true);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testDontWalkScalars()
+ {
+ $context = $this->getExecutionContext();
+ $graphWalker = $context->getGraphWalker();
+
+ $form = $this->getBuilder()
+ ->setData('scalar')
+ ->getForm();
+
+ $graphWalker->expects($this->never())
+ ->method('walkReference');
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+ }
+
+ public function testViolationIfExtraData()
+ {
+ $context = $this->getExecutionContext();
+
+ $form = $this->getBuilder()
+ ->add($this->getBuilder('child'))
+ ->setAttribute('extra_fields_message', 'Extra!')
+ ->getForm();
+
+ $form->bind(array('foo' => 'bar'));
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Extra!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testViolationIfPostMaxSizeExceeded_GigaUpper()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(pow(1024, 3) + 1));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1G'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getBuilder()
+ ->setAttribute('post_max_size_message', 'Max {{ max }}!')
+ ->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Max 1G!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testViolationIfPostMaxSizeExceeded_GigaLower()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(pow(1024, 3) + 1));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1g'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getBuilder()
+ ->setAttribute('post_max_size_message', 'Max {{ max }}!')
+ ->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Max 1G!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testNoViolationIfPostMaxSizeNotExceeded_Giga()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(pow(1024, 3)));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1G'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ public function testViolationIfPostMaxSizeExceeded_Mega()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(pow(1024, 2) + 1));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1M'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getBuilder()
+ ->setAttribute('post_max_size_message', 'Max {{ max }}!')
+ ->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Max 1M!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testNoViolationIfPostMaxSizeNotExceeded_Mega()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(pow(1024, 2)));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1M'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ public function testViolationIfPostMaxSizeExceeded_Kilo()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(1025));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1K'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getBuilder()
+ ->setAttribute('post_max_size_message', 'Max {{ max }}!')
+ ->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Max 1K!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testNoViolationIfPostMaxSizeNotExceeded_Kilo()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(1024));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1K'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ public function testNoViolationIfNotRoot()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(1025));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1K'));
+
+ $context = $this->getExecutionContext();
+ $parent = $this->getForm();
+ $form = $this->getForm();
+ $parent->add($form);
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ public function testNoViolationIfContentLengthNull()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(null));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue('1K'));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ public function testTrimPostMaxSize()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(1025));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue(' 1K '));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getBuilder()
+ ->setAttribute('post_max_size_message', 'Max {{ max }}!')
+ ->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertEquals('Max 1K!', $context->getViolations()->get(0)->getMessage());
+ }
+
+ public function testNoViolationIfPostMaxSizeEmpty()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(1025));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue(' '));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ public function testNoViolationIfPostMaxSizeNull()
+ {
+ $this->serverParams->expects($this->any())
+ ->method('getContentLength')
+ ->will($this->returnValue(1025));
+ $this->serverParams->expects($this->any())
+ ->method('getPostMaxSize')
+ ->will($this->returnValue(null));
+
+ $context = $this->getExecutionContext();
+ $form = $this->getForm();
+
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(0, $context->getViolations());
+ }
+
+ /**
+ * Access has to be public, as this method is called via callback array
+ * in {@link testValidateFormDataCanHandleCallbackValidationGroups()}
+ * and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()}
+ */
+ public function getValidationGroups(FormInterface $form)
+ {
+ return array('group1', 'group2');
+ }
+
+ private function getMockGraphWalker()
+ {
+ return $this->getMockBuilder('Symfony\Component\Validator\GraphWalker')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ private function getMockMetadataFactory()
+ {
+ return $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
+ }
+
+ private function getExecutionContext($propertyPath = null)
+ {
+ $graphWalker = $this->getMockGraphWalker();
+ $metadataFactory = $this->getMockMetadataFactory();
+ $globalContext = new GlobalExecutionContext('Root', $graphWalker, $metadataFactory);
+
+ return new ExecutionContext($globalContext, null, $propertyPath, null, null, null);
+ }
+
+ /**
+ * @return FormBuilder
+ */
+ private function getBuilder($name = 'name', $dataClass = null)
+ {
+ $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory);
+ $builder->setAttribute('constraints', array());
+
+ return $builder;
+ }
+
+ private function getForm($name = 'name', $dataClass = null)
+ {
+ return $this->getBuilder($name, $dataClass)->getForm();
+ }
+}
View
417 Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php
@@ -1,417 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Tests\Extension\Validator\EventListener;
-
-use Symfony\Component\Form\Event\DataEvent;
-use Symfony\Component\Form\FormInterface;
-use Symfony\Component\Form\FormBuilder;
-use Symfony\Component\Form\FormError;
-use Symfony\Component\Form\Util\PropertyPath;
-use Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener;
-use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintViolation;
-use Symfony\Component\Validator\GlobalExecutionContext;
-use Symfony\Component\Validator\ExecutionContext;
-
-class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase
-{
- private $dispatcher;
-
- private $factory;
-
- private $builder;
-
- private $delegate;
-
- private $listener;
-
- private $message;
-
- private $params;
-
- protected function setUp()
- {
- if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
- $this->markTestSkipped('The "EventDispatcher" component is not available');
- }
-
- $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
- $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
- $this->delegate = $this->getMock('Symfony\Component\Validator\ValidatorInterface');
- $this->listener = new DelegatingValidationListener($this->delegate);
- $this->message = 'Message';
- $this->params = array('foo' => 'bar');
- }
-
- protected function getMockGraphWalker()
- {
- return $this->getMockBuilder('Symfony\Component\Validator\GraphWalker')
- ->disableOriginalConstructor()
- ->getMock();
- }
-
- protected function getMockMetadataFactory()
- {
- return $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
- }
-
- protected function getMockTransformer()
- {
- return $this->getMock('Symfony\Component\Form\DataTransformerInterface', array(), array(), '', false, false);
- }
-
- protected function getExecutionContext($propertyPath = null)
- {
- $graphWalker = $this->getMockGraphWalker();
- $metadataFactory = $this->getMockMetadataFactory();
- $globalContext = new GlobalExecutionContext('Root', $graphWalker, $metadataFactory);
-
- return new ExecutionContext($globalContext, null, $propertyPath, null, null, null);
- }
-
- protected function getConstraintViolation($propertyPath)
- {
- return new ConstraintViolation($this->message, $this->params, null, $propertyPath, null);
- }
-
- protected function getFormError()
- {
- return new FormError($this->message, $this->params);
- }
-
- protected function getBuilder($name = 'name', $propertyPath = null, $dataClass = null)
- {
- $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory);
- $builder->setPropertyPath(new PropertyPath($propertyPath ?: $name));
- $builder->setAttribute('error_mapping', array());
- $builder->setErrorBubbling(false);
- $builder->setMapped(true);
-
- return $builder;
- }
-
- protected function getForm($name = 'name', $propertyPath = null, $dataClass = null)
- {
- return $this->getBuilder($name, $propertyPath, $dataClass)->getForm();
- }
-
- protected function getMockForm()
- {
- return $this->getMock('Symfony\Component\Form\Tests\FormInterface');
- }
-
- /**
- * Access has to be public, as this method is called via callback array
- * in {@link testValidateFormDataCanHandleCallbackValidationGroups()}
- * and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()}
- */
- public function getValidationGroups(FormInterface $form)
- {
- return array('group1', 'group2');
- }
-
- public function testUseValidateValueWhenValidationConstraintExist()
- {
- $constraint = $this->getMockForAbstractClass('Symfony\Component\Validator\Constraint');
- $form = $this
- ->getBuilder('name')
- ->setAttribute('validation_constraint', $constraint)
- ->getForm();
-
- $this->delegate->expects($this->once())->method('validateValue');
-
- $this->listener->validateForm(new DataEvent($form, null));
- }
-
- // More specific mapping tests can be found in ViolationMapperTest
- public function testFormErrorMapping()
- {
- $parent = $this->getForm();
- $child = $this->getForm('street');
-
- $parent->add($child);
-
- $this->delegate->expects($this->once())
- ->method('validate')
- ->will($this->returnValue(array(
- $this->getConstraintViolation('children[street].data.constrainedProp')
- )));
-
- $this->listener->validateForm(new DataEvent($parent, null));
-
- $this->assertFalse($parent->hasErrors());
- $this->assertEquals(array($this->getFormError()), $child->getErrors());
- }
-
- // More specific mapping tests can be found in ViolationMapperTest
- public function testDataErrorMapping()
- {
- $parent = $this->getForm();
- $child = $this->getForm('firstName');
-
- $parent->add($child);
-
- $this->delegate->expects($this->once())
- ->method('validate')
- ->will($this->returnValue(array(
- $this->getConstraintViolation('data.firstName.constrainedProp')
- )));
-
- $this->listener->validateForm(new DataEvent($parent, null));
-
- $this->assertFalse($parent->hasErrors());
- $this->assertEquals(array($this->getFormError()), $child->getErrors());
- }
-
- public function testValidateFormData()
- {
- $context = $this->getExecutionContext();
- $graphWalker = $context->getGraphWalker();
- $object = $this->getMock('\stdClass');
- $form = $this->getBuilder('name', null, '\stdClass')
- ->setAttribute('validation_groups', array('group1', 'group2'))
- ->getForm();
-
- $graphWalker->expects($this->at(0))
- ->method('walkReference')
- ->with($object, 'group1', 'data', true);
- $graphWalker->expects($this->at(1))
- ->method('walkReference')
- ->with($object, 'group2', 'data', true);
-
- $form->setData($object);
-
- DelegatingValidationListener::validateFormData($form, $context);
- }
-
- public function testValidateFormDataCanHandleCallbackValidationGroups()
- {
- $context = $this->getExecutionContext();
- $graphWalker = $context->getGraphWalker();
- $object = $this->getMock('\stdClass');
- $form = $this->getBuilder('name', null, '\stdClass')
- ->setAttribute('validation_groups', array($this, 'getValidationGroups'))
- ->getForm();
-
- $graphWalker->expects($this->at(0))
- ->method('walkReference')
- ->with($object, 'group1', 'data', true);
- $graphWalker->expects($this->at(1))
- ->method('walkReference')
- ->with($object, 'group2', 'data', true);
-
- $form->setData($object);
-
- DelegatingValidationListener::validateFormData($form, $context);
- }
-
- public function testValidateFormDataCanHandleClosureValidationGroups()
- {
- $context = $this->getExecutionContext();
- $graphWalker = $context->getGraphWalker();
- $object = $this->getMock('\stdClass');
- $form = $this->getBuilder('name', null, '\stdClass')
- ->setAttribute('validation_groups', function(FormInterface $form){
- return array('group1', 'group2');
- })
- ->getForm();
-
- $graphWalker->expects($this->at(0))
- ->method('walkReference')
- ->with($object, 'group1', 'data', true);
- $graphWalker->expects($this->at(1))
- ->method('walkReference')
- ->with($object, 'group2', 'data', true);
-
- $form->setData($object);
-
- DelegatingValidationListener::validateFormData($form, $context);
- }
-
- public function testValidateFormDataUsesInheritedValidationGroup()
- {
- $context = $this->getExecutionContext('foo.bar');
- $graphWalker = $context->getGraphWalker();
- $object = $this->getMock('\stdClass');
-
- $parent = $this->getBuilder()
- ->setAttribute('validation_groups', 'group')
- ->getForm();
- $child = $this->getBuilder('name', null, '\stdClass')
- ->setAttribute('validation_groups', null)
- ->getForm();
- $parent->add($child);
-
- $child->setData($object);
-
- $graphWalker->expects($this->once())
- ->method('walkReference')
- ->with($object, 'group', 'foo.bar.data', true);
-
- DelegatingValidationListener::validateFormData($child, $context);
- }
-
- public function testValidateFormDataUsesInheritedCallbackValidationGroup()
- {
- $context = $this->getExecutionContext('foo.bar');
- $graphWalker = $context->getGraphWalker();
- $object = $this->getMock('\stdClass');
-
- $parent = $this->getBuilder()
- ->setAttribute('validation_groups', array($this, 'getValidationGroups'))
- ->getForm();
- $child = $this->getBuilder('name', null, '\stdClass')
- ->setAttribute('validation_groups', null)
- ->getForm();
- $parent->add($child);
-
- $child->setData($object);
-
- $graphWalker->expects($this->at(0))
- ->method('walkReference')
- ->with($object, 'group1', 'foo.bar.data', true);
- $graphWalker->expects($this->at(1))
-