From 6b0c24adf7c0ee3a19ef4bf9fbb6f33723b02ed0 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Wed, 24 Sep 2014 11:48:52 +0200 Subject: [PATCH] [Validator] Added ConstraintValidator::buildViolation() helper for BC with 2.4 API --- .../Constraints/UniqueEntityValidatorTest.php | 11 +- .../Constraints/UniqueEntityValidator.php | 14 +- .../Validator/Constraints/FormValidator.php | 38 +--- .../Constraints/UserPasswordValidatorTest.php | 3 +- .../Validator/ConstraintValidator.php | 53 +++++- .../AbstractComparisonValidator.php | 15 +- .../Validator/Constraints/BlankValidator.php | 6 +- .../Validator/Constraints/CardScheme.php | 2 + .../Constraints/CardSchemeValidator.php | 16 +- .../Validator/Constraints/ChoiceValidator.php | 60 ++----- .../Constraints/CollectionValidator.php | 34 ++-- .../Validator/Constraints/CountValidator.php | 60 ++----- .../Constraints/CountryValidator.php | 6 +- .../Constraints/CurrencyValidator.php | 6 +- .../Constraints/DateTimeValidator.php | 24 ++- .../Validator/Constraints/DateValidator.php | 32 +++- .../Validator/Constraints/EmailValidator.php | 54 ++++-- .../Constraints/ExpressionValidator.php | 4 +- .../Validator/Constraints/FalseValidator.php | 6 +- .../Validator/Constraints/FileValidator.php | 73 ++++---- .../Component/Validator/Constraints/Iban.php | 3 + .../Validator/Constraints/IbanValidator.php | 58 +++--- .../Component/Validator/Constraints/Image.php | 3 + .../Validator/Constraints/ImageValidator.php | 77 ++++---- .../Validator/Constraints/IpValidator.php | 6 +- .../Validator/Constraints/IsbnValidator.php | 67 +++++-- .../Validator/Constraints/IssnValidator.php | 77 ++++++-- .../Constraints/LanguageValidator.php | 6 +- .../Validator/Constraints/LengthValidator.php | 60 ++----- .../Validator/Constraints/LocaleValidator.php | 6 +- .../Component/Validator/Constraints/Luhn.php | 3 + .../Validator/Constraints/LuhnValidator.php | 12 +- .../Constraints/NotBlankValidator.php | 6 +- .../Validator/Constraints/NullValidator.php | 6 +- .../Validator/Constraints/RangeValidator.php | 22 +-- .../Validator/Constraints/RegexValidator.php | 6 +- .../Validator/Constraints/TimeValidator.php | 34 +++- .../Validator/Constraints/TrueValidator.php | 6 +- .../Validator/Constraints/TypeValidator.php | 8 +- .../Validator/Constraints/UrlValidator.php | 6 +- .../Validator/Constraints/UuidValidator.php | 41 ++++- .../Constraints/CallbackValidatorTest.php | 54 +++--- .../Tests/Constraints/ChoiceValidatorTest.php | 2 + .../Tests/Constraints/CountValidatorTest.php | 37 ++-- .../Constraints/ExpressionValidatorTest.php | 19 +- .../Tests/Constraints/ImageValidatorTest.php | 40 ++--- .../Tests/Constraints/IsbnValidatorTest.php | 41 +++-- .../Tests/Constraints/IssnValidatorTest.php | 48 +---- .../Tests/Constraints/LengthValidatorTest.php | 37 ++-- .../Tests/Constraints/LuhnValidatorTest.php | 2 + .../Tests/Constraints/RangeValidatorTest.php | 32 ---- .../Tests/Constraints/UuidValidatorTest.php | 18 +- .../LegacyConstraintViolationBuilder.php | 165 ++++++++++++++++++ 53 files changed, 898 insertions(+), 627 deletions(-) create mode 100644 src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index b36c458e4fdf..cfc56b9b4c88 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -262,12 +262,19 @@ public function testValidateUniquenessAfterConsideringMultipleQueryResults() $this->validator->validate($entity1, $constraint); - $this->assertViolation('myMessage', array(), 'property.path.name', 'Foo'); + $this->buildViolation('myMessage') + ->atPath('property.path.name') + ->setInvalidValue('Foo') + ->assertRaised(); + $this->context->getViolations()->remove(0); $this->validator->validate($entity2, $constraint); - $this->assertViolation('myMessage', array(), 'property.path.name', 'Foo'); + $this->buildViolation('myMessage') + ->atPath('property.path.name') + ->setInvalidValue('Foo') + ->assertRaised(); } public function testValidateUniquenessUsingCustomRepositoryMethod() diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 60e11e08c078..48c35c9eff3a 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -13,7 +13,6 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\ConstraintValidator; @@ -137,14 +136,9 @@ public function validate($entity, Constraint $constraint) $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->message) - ->atPath($errorPath) - ->setInvalidValue($criteria[$fields[0]]) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolationAt($errorPath, $constraint->message, array(), $criteria[$fields[0]]); - } + $this->buildViolation($constraint->message) + ->atPath($errorPath) + ->setInvalidValue($criteria[$fields[0]]) + ->addViolation(); } } diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 2a6673cbc586..ab826f9c979f 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -99,40 +99,20 @@ public function validate($form, Constraint $constraint) ? (string) $form->getViewData() : gettype($form->getViewData()); - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($config->getOption('invalid_message')) - ->setParameters(array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters'))) - ->setInvalidValue($form->getViewData()) - ->setCode(Form::ERR_INVALID) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation( - $config->getOption('invalid_message'), - array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters')), - $form->getViewData(), - null, - Form::ERR_INVALID - ); - } + $this->buildViolation($config->getOption('invalid_message')) + ->setParameters(array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters'))) + ->setInvalidValue($form->getViewData()) + ->setCode(Form::ERR_INVALID) + ->addViolation(); } } // Mark the form with an error if it contains extra fields if (count($form->getExtraData()) > 0) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($config->getOption('extra_fields_message')) - ->setParameter('{{ extra_fields }}', implode('", "', array_keys($form->getExtraData()))) - ->setInvalidValue($form->getExtraData()) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation( - $config->getOption('extra_fields_message'), - array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))), - $form->getExtraData() - ); - } + $this->buildViolation($config->getOption('extra_fields_message')) + ->setParameter('{{ extra_fields }}', implode('", "', array_keys($form->getExtraData()))) + ->setInvalidValue($form->getExtraData()) + ->addViolation(); } } diff --git a/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 10f692c6a54d..ef93e25d2dad 100644 --- a/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -92,7 +92,8 @@ public function testPasswordIsNotValid() $this->validator->validate('secret', $constraint); - $this->assertViolation('myMessage'); + $this->buildViolation('myMessage') + ->assertRaised(); } /** diff --git a/src/Symfony/Component/Validator/ConstraintValidator.php b/src/Symfony/Component/Validator/ConstraintValidator.php index 58c0902e131f..111c2b4bacec 100644 --- a/src/Symfony/Component/Validator/ConstraintValidator.php +++ b/src/Symfony/Component/Validator/ConstraintValidator.php @@ -11,6 +11,10 @@ namespace Symfony\Component\Validator; +use Symfony\Component\Validator\Context\ExecutionContextInterface as ExecutionContextInterface2Dot5; +use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface; +use Symfony\Component\Validator\Violation\LegacyConstraintViolationBuilder; + /** * Base class for constraint validators * @@ -24,14 +28,14 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface * Whether to format {@link \DateTime} objects as RFC-3339 dates * ("Y-m-d H:i:s"). * - * @var integer + * @var int */ const PRETTY_DATE = 1; /** * Whether to cast objects with a "__toString()" method to strings. * - * @var integer + * @var int */ const OBJECT_TO_STRING = 2; @@ -48,6 +52,47 @@ public function initialize(ExecutionContextInterface $context) $this->context = $context; } + /** + * Wrapper for {@link ExecutionContextInterface::buildViolation} that + * supports the 2.4 context API. + * + * @param string $message The violation message + * @param array $parameters The message parameters + * + * @return ConstraintViolationBuilderInterface The violation builder + * + * @deprecated This method will be removed in Symfony 3.0. + */ + protected function buildViolation($message, array $parameters = array()) + { + if ($this->context instanceof ExecutionContextInterface2Dot5) { + return $this->context->buildViolation($message, $parameters); + } + + return new LegacyConstraintViolationBuilder($this->context, $message, $parameters); + } + + /** + * Wrapper for {@link ExecutionContextInterface::buildViolation} that + * supports the 2.4 context API. + * + * @param ExecutionContextInterface $context The context to use + * @param string $message The violation message + * @param array $parameters The message parameters + * + * @return ConstraintViolationBuilderInterface The violation builder + * + * @deprecated This method will be removed in Symfony 3.0. + */ + protected function buildViolationInContext(ExecutionContextInterface $context, $message, array $parameters = array()) + { + if ($context instanceof ExecutionContextInterface2Dot5) { + return $context->buildViolation($message, $parameters); + } + + return new LegacyConstraintViolationBuilder($context, $message, $parameters); + } + /** * Returns a string representation of the type of the value. * @@ -82,7 +127,7 @@ protected function formatTypeOf($value) * confused by the violation message. * * @param mixed $value The value to format as string - * @param integer $format A bitwise combination of the format + * @param int $format A bitwise combination of the format * constants in this class * * @return string The string representation of the passed value @@ -142,7 +187,7 @@ protected function formatValue($value, $format = 0) * {@link formatValue()}. The values are then concatenated with commas. * * @param array $values A list of values - * @param integer $format A bitwise combination of the format + * @param int $format A bitwise combination of the format * constants in this class * * @return string The string representation of the value list diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index 5fbcd179a6d2..ce7239d98ddf 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -19,6 +19,7 @@ * Provides a base class for the validation of property comparisons. * * @author Daniel Holmes + * @author Bernhard Schussek */ abstract class AbstractComparisonValidator extends ConstraintValidator { @@ -35,12 +36,14 @@ public function validate($value, Constraint $constraint) return; } - if (!$this->compareValues($value, $constraint->value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE), - '{{ compared_value }}' => $this->formatValue($constraint->value, self::OBJECT_TO_STRING | self::PRETTY_DATE), - '{{ compared_value_type }}' => $this->formatTypeOf($constraint->value), - )); + $comparedValue = $constraint->value; + + if (!$this->compareValues($value, $comparedValue)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE)) + ->setParameter('{{ compared_value }}', $this->formatValue($comparedValue, self::OBJECT_TO_STRING | self::PRETTY_DATE)) + ->setParameter('{{ compared_value_type }}', $this->formatTypeOf($comparedValue)) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/BlankValidator.php b/src/Symfony/Component/Validator/Constraints/BlankValidator.php index e8d444e123ef..031c7a581cd3 100644 --- a/src/Symfony/Component/Validator/Constraints/BlankValidator.php +++ b/src/Symfony/Component/Validator/Constraints/BlankValidator.php @@ -32,9 +32,9 @@ public function validate($value, Constraint $constraint) } if ('' !== $value && null !== $value) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/CardScheme.php b/src/Symfony/Component/Validator/Constraints/CardScheme.php index a8f38a982d9f..26a0e1c2fd46 100644 --- a/src/Symfony/Component/Validator/Constraints/CardScheme.php +++ b/src/Symfony/Component/Validator/Constraints/CardScheme.php @@ -18,6 +18,8 @@ * * @Annotation * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) + * + * @author Tim Nagel */ class CardScheme extends Constraint { diff --git a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php index 0557f690819c..7cf7fa4bfe04 100644 --- a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php @@ -18,9 +18,11 @@ /** * Validates that a card number belongs to a specified scheme. * + * @author Tim Nagel + * @author Bernhard Schussek + * * @see http://en.wikipedia.org/wiki/Bank_card_number * @see http://www.regular-expressions.info/creditcard.html - * @author Tim Nagel */ class CardSchemeValidator extends ConstraintValidator { @@ -113,9 +115,9 @@ public function validate($value, Constraint $constraint) } if (!is_numeric($value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); return; } @@ -131,8 +133,8 @@ public function validate($value, Constraint $constraint) } } - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php index f995af681637..5640c733173a 100644 --- a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -64,63 +63,38 @@ public function validate($value, Constraint $constraint) if ($constraint->multiple) { foreach ($value as $_value) { if (!in_array($_value, $choices, $constraint->strict)) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->multipleMessage) - ->setParameter('{{ value }}', $this->formatValue($_value)) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->multipleMessage, array( - '{{ value }}' => $this->formatValue($_value), - )); - } + $this->buildViolation($constraint->multipleMessage) + ->setParameter('{{ value }}', $this->formatValue($_value)) + ->setInvalidValue($_value) + ->addViolation(); + + return; } } $count = count($value); if ($constraint->min !== null && $count < $constraint->min) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->minMessage) - ->setParameter('{{ limit }}', $constraint->min) - ->setPlural((int) $constraint->min) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->minMessage, array( - '{{ limit }}' => $constraint->min, - ), $value, (int) $constraint->min); - } + $this->buildViolation($constraint->minMessage) + ->setParameter('{{ limit }}', $constraint->min) + ->setPlural((int) $constraint->min) + ->addViolation(); return; } if ($constraint->max !== null && $count > $constraint->max) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->maxMessage) - ->setParameter('{{ limit }}', $constraint->max) - ->setPlural((int) $constraint->max) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->maxMessage, array( - '{{ limit }}' => $constraint->max, - ), $value, (int) $constraint->max); - } + $this->buildViolation($constraint->maxMessage) + ->setParameter('{{ limit }}', $constraint->max) + ->setPlural((int) $constraint->max) + ->addViolation(); return; } } elseif (!in_array($value, $choices, $constraint->strict)) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); - } + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php index 64e8d82526fe..4289169b1606 100644 --- a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php @@ -70,36 +70,22 @@ public function validate($value, Constraint $constraint) } } } elseif (!$fieldConstraint instanceof Optional && !$constraint->allowMissingFields) { - if ($context instanceof ExecutionContextInterface) { - $context->buildViolation($constraint->missingFieldsMessage) - ->atPath('['.$field.']') - ->setParameter('{{ field }}', $this->formatValue($field)) - ->setInvalidValue(null) - ->addViolation(); - } else { - // 2.4 API - $context->addViolationAt('['.$field.']', $constraint->missingFieldsMessage, array( - '{{ field }}' => $this->formatValue($field), - ), null); - } + $this->buildViolationInContext($context, $constraint->missingFieldsMessage) + ->atPath('['.$field.']') + ->setParameter('{{ field }}', $this->formatValue($field)) + ->setInvalidValue(null) + ->addViolation(); } } if (!$constraint->allowExtraFields) { foreach ($value as $field => $fieldValue) { if (!isset($constraint->fields[$field])) { - if ($context instanceof ExecutionContextInterface) { - $context->buildViolation($constraint->extraFieldsMessage) - ->atPath('['.$field.']') - ->setParameter('{{ field }}', $this->formatValue($field)) - ->setInvalidValue($fieldValue) - ->addViolation(); - } else { - // 2.4 API - $context->addViolationAt('['.$field.']', $constraint->extraFieldsMessage, array( - '{{ field }}' => $this->formatValue($field), - ), $fieldValue); - } + $this->buildViolationInContext($context, $constraint->extraFieldsMessage) + ->atPath('['.$field.']') + ->setParameter('{{ field }}', $this->formatValue($field)) + ->setInvalidValue($fieldValue) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/CountValidator.php b/src/Symfony/Component/Validator/Constraints/CountValidator.php index 49d1bfc48f29..0f40a3f1e0de 100644 --- a/src/Symfony/Component/Validator/Constraints/CountValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CountValidator.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** @@ -36,59 +35,24 @@ public function validate($value, Constraint $constraint) $count = count($value); - if ($constraint->min == $constraint->max && $count != $constraint->min) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->exactMessage) - ->setParameter('{{ count }}', $count) - ->setParameter('{{ limit }}', $constraint->min) - ->setInvalidValue($value) - ->setPlural((int) $constraint->min) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->exactMessage, array( - '{{ count }}' => $count, - '{{ limit }}' => $constraint->min, - ), $value, (int) $constraint->min); - } - - return; - } - if (null !== $constraint->max && $count > $constraint->max) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->maxMessage) - ->setParameter('{{ count }}', $count) - ->setParameter('{{ limit }}', $constraint->max) - ->setInvalidValue($value) - ->setPlural((int) $constraint->max) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->maxMessage, array( - '{{ count }}' => $count, - '{{ limit }}' => $constraint->max, - ), $value, (int) $constraint->max); - } + $this->buildViolation($constraint->min == $constraint->max ? $constraint->exactMessage : $constraint->maxMessage) + ->setParameter('{{ count }}', $count) + ->setParameter('{{ limit }}', $constraint->max) + ->setInvalidValue($value) + ->setPlural((int) $constraint->max) + ->addViolation(); return; } if (null !== $constraint->min && $count < $constraint->min) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->minMessage) - ->setParameter('{{ count }}', $count) - ->setParameter('{{ limit }}', $constraint->min) - ->setInvalidValue($value) - ->setPlural((int) $constraint->min) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->minMessage, array( - '{{ count }}' => $count, - '{{ limit }}' => $constraint->min, - ), $value, (int) $constraint->min); - } + $this->buildViolation($constraint->min == $constraint->max ? $constraint->exactMessage : $constraint->minMessage) + ->setParameter('{{ count }}', $count) + ->setParameter('{{ limit }}', $constraint->min) + ->setInvalidValue($value) + ->setPlural((int) $constraint->min) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/CountryValidator.php b/src/Symfony/Component/Validator/Constraints/CountryValidator.php index 92049db54d8f..a3ad1dc83bca 100644 --- a/src/Symfony/Component/Validator/Constraints/CountryValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CountryValidator.php @@ -46,9 +46,9 @@ public function validate($value, Constraint $constraint) $countries = Intl::getRegionBundle()->getCountryNames(); if (!isset($countries[$value])) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php b/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php index 92989ac99e73..ae3046f2eb1d 100644 --- a/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php @@ -46,9 +46,9 @@ public function validate($value, Constraint $constraint) $currencies = Intl::getCurrencyBundle()->getCurrencyNames(); if (!isset($currencies[$value])) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php index 14634b30515e..88c8e2586229 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php @@ -21,7 +21,7 @@ */ class DateTimeValidator extends DateValidator { - const PATTERN = '/^(\d{4})-(\d{2})-(\d{2}) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/'; + const PATTERN = '/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/'; /** * {@inheritdoc} @@ -42,10 +42,24 @@ public function validate($value, Constraint $constraint) $value = (string) $value; - if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + if (!preg_match(static::PATTERN, $value, $matches)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + if (!DateValidator::checkDate($matches[1], $matches[2], $matches[3])) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + } + + if (!TimeValidator::checkTime($matches[4], $matches[5], $matches[6])) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/DateValidator.php b/src/Symfony/Component/Validator/Constraints/DateValidator.php index 7edc59c5c7e0..e1640b13b385 100644 --- a/src/Symfony/Component/Validator/Constraints/DateValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateValidator.php @@ -24,6 +24,22 @@ class DateValidator extends ConstraintValidator { const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/'; + /** + * Checks whether a date is valid. + * + * @param int $year The year + * @param int $month The month + * @param int $day The day + * + * @return bool Whether the date is valid + * + * @internal + */ + public static function checkDate($year, $month, $day) + { + return checkdate($month, $day, $year); + } + /** * {@inheritdoc} */ @@ -43,10 +59,18 @@ public function validate($value, Constraint $constraint) $value = (string) $value; - if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + if (!preg_match(static::PATTERN, $value, $matches)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + if (!self::checkDate($matches[1], $matches[2], $matches[3])) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index 70f3d55c0d3b..1f487ca1c4a4 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -13,8 +13,8 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\RuntimeException; use Symfony\Component\Validator\Exception\UnexpectedTypeException; -use Egulias\EmailValidator\EmailValidator as StrictEmailValidator; /** * @author Bernhard Schussek @@ -53,34 +53,50 @@ public function validate($value, Constraint $constraint) } $value = (string) $value; + if (null === $constraint->strict) { $constraint->strict = $this->isStrict; } - if ($constraint->strict && class_exists('\Egulias\EmailValidator\EmailValidator')) { - $strictValidator = new StrictEmailValidator(); - $valid = $strictValidator->isValid($value, false, true); - } elseif ($constraint->strict === true) { - throw new \RuntimeException('Strict email validation requires egulias/email-validator'); - } else { - $valid = preg_match('/.+\@.+\..+/', $value); + if ($constraint->strict) { + if (!class_exists('\Egulias\EmailValidator\EmailValidator')) { + throw new RuntimeException('Strict email validation requires egulias/email-validator'); + } + + $strictValidator = new \Egulias\EmailValidator\EmailValidator(); + + if (!$strictValidator->isValid($value, false, true)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + } elseif (!preg_match('/.+\@.+\..+/', $value)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; } - if ($valid) { - $host = substr($value, strpos($value, '@') + 1); - // Check for host DNS resource records + $host = substr($value, strpos($value, '@') + 1); - if ($valid && $constraint->checkMX) { - $valid = $this->checkMX($host); - } elseif ($valid && $constraint->checkHost) { - $valid = $this->checkHost($host); + // Check for host DNS resource records + if ($constraint->checkMX) { + if (!$this->checkMX($host)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } + + return; } - if (!$valid) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + if ($constraint->checkHost && !$this->checkHost($host)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index 3df23d434279..a20d1ac2518f 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -89,7 +89,9 @@ public function validate($value, Constraint $constraint) } if (!$this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { - $this->context->addViolation($constraint->message); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/FalseValidator.php b/src/Symfony/Component/Validator/Constraints/FalseValidator.php index d798ebfbb98a..206780cefc73 100644 --- a/src/Symfony/Component/Validator/Constraints/FalseValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FalseValidator.php @@ -35,8 +35,8 @@ public function validate($value, Constraint $constraint) return; } - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 3916ef54931d..acc80c9b0794 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -66,38 +66,46 @@ public function validate($value, Constraint $constraint) $limitInBytes = UploadedFile::getMaxFilesize(); } - $this->context->addViolation($constraint->uploadIniSizeErrorMessage, array( - '{{ limit }}' => $limitInBytes, - '{{ suffix }}' => 'bytes', - )); + $this->buildViolation($constraint->uploadIniSizeErrorMessage) + ->setParameter('{{ limit }}', $limitInBytes) + ->setParameter('{{ suffix }}', 'bytes') + ->addViolation(); return; case UPLOAD_ERR_FORM_SIZE: - $this->context->addViolation($constraint->uploadFormSizeErrorMessage); + $this->buildViolation($constraint->uploadFormSizeErrorMessage) + ->addViolation(); return; case UPLOAD_ERR_PARTIAL: - $this->context->addViolation($constraint->uploadPartialErrorMessage); + $this->buildViolation($constraint->uploadPartialErrorMessage) + ->addViolation(); return; case UPLOAD_ERR_NO_FILE: - $this->context->addViolation($constraint->uploadNoFileErrorMessage); + $this->buildViolation($constraint->uploadNoFileErrorMessage) + ->addViolation(); return; case UPLOAD_ERR_NO_TMP_DIR: - $this->context->addViolation($constraint->uploadNoTmpDirErrorMessage); + $this->buildViolation($constraint->uploadNoTmpDirErrorMessage) + ->addViolation(); return; case UPLOAD_ERR_CANT_WRITE: - $this->context->addViolation($constraint->uploadCantWriteErrorMessage); + $this->buildViolation($constraint->uploadCantWriteErrorMessage) + ->addViolation(); return; case UPLOAD_ERR_EXTENSION: - $this->context->addViolation($constraint->uploadExtensionErrorMessage); + $this->buildViolation($constraint->uploadExtensionErrorMessage) + ->addViolation(); return; default: - $this->context->addViolation($constraint->uploadErrorMessage); + $this->buildViolation($constraint->uploadErrorMessage) + ->setCode($value->getError()) + ->addViolation(); return; } @@ -110,17 +118,17 @@ public function validate($value, Constraint $constraint) $path = $value instanceof FileObject ? $value->getPathname() : (string) $value; if (!is_file($path)) { - $this->context->addViolation($constraint->notFoundMessage, array( - '{{ file }}' => $this->formatValue($path), - )); + $this->buildViolation($constraint->notFoundMessage) + ->setParameter('{{ file }}', $this->formatValue($path)) + ->addViolation(); return; } if (!is_readable($path)) { - $this->context->addViolation($constraint->notReadableMessage, array( - '{{ file }}' => $this->formatValue($path), - )); + $this->buildViolation($constraint->notReadableMessage) + ->setParameter('{{ file }}', $this->formatValue($path)) + ->addViolation(); return; } @@ -161,12 +169,12 @@ public function validate($value, Constraint $constraint) $sizeAsString = (string) round($sizeInBytes / $coef, 2); } - $this->context->addViolation($constraint->maxSizeMessage, array( - '{{ size }}' => $sizeAsString, - '{{ limit }}' => $limitAsString, - '{{ suffix }}' => self::$suffices[$coef], - '{{ file }}' => $this->formatValue($path), - )); + $this->buildViolation($constraint->maxSizeMessage) + ->setParameter('{{ file }}', $this->formatValue($path)) + ->setParameter('{{ size }}', $sizeAsString) + ->setParameter('{{ limit }}', $limitAsString) + ->setParameter('{{ suffix }}', self::$suffices[$coef]) + ->addViolation(); return; } @@ -179,29 +187,24 @@ public function validate($value, Constraint $constraint) $mimeTypes = (array) $constraint->mimeTypes; $mime = $value->getMimeType(); - $valid = false; foreach ($mimeTypes as $mimeType) { if ($mimeType === $mime) { - $valid = true; - break; + return; } if ($discrete = strstr($mimeType, '/*', true)) { if (strstr($mime, '/', true) === $discrete) { - $valid = true; - break; + return; } } } - if (false === $valid) { - $this->context->addViolation($constraint->mimeTypesMessage, array( - '{{ type }}' => $this->formatValue($mime), - '{{ types }}' => $this->formatValues($mimeTypes), - '{{ file }}' => $this->formatValue($path), - )); - } + $this->buildViolation($constraint->mimeTypesMessage) + ->setParameter('{{ file }}', $this->formatValue($path)) + ->setParameter('{{ type }}', $this->formatValue($mime)) + ->setParameter('{{ types }}', $this->formatValues($mimeTypes)) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Iban.php b/src/Symfony/Component/Validator/Constraints/Iban.php index 628d21cacecd..895a93cd5860 100644 --- a/src/Symfony/Component/Validator/Constraints/Iban.php +++ b/src/Symfony/Component/Validator/Constraints/Iban.php @@ -16,6 +16,9 @@ /** * @Annotation * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) + * + * @author Manuel Reinhard + * @author Michael Schummel */ class Iban extends Constraint { diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php index 2276789c319a..b66561dc2ade 100644 --- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php @@ -45,21 +45,38 @@ public function validate($value, Constraint $constraint) // Remove spaces $canonicalized = str_replace(' ', '', $value); + // The IBAN must have at least 4 characters... if (strlen($canonicalized) < 4) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); return; } - // The IBAN must have at least 4 characters, start with a country - // code and contain only digits and (uppercase) characters - if (strlen($canonicalized) < 4 || !ctype_upper($canonicalized{0}) - || !ctype_upper($canonicalized{1}) || !ctype_alnum($canonicalized)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + // ...start with a country code... + if (!ctype_alpha($canonicalized{0}) || !ctype_alpha($canonicalized{1})) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + // ...contain only digits and characters... + if (!ctype_alnum($canonicalized)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + // ...and contain uppercase characters only + if ($canonicalized !== strtoupper($canonicalized)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); return; } @@ -76,23 +93,13 @@ public function validate($value, Constraint $constraint) // -> 0076 2011 6238 5295 7 121893 $checkSum = $this->toBigInt($canonicalized); - if (false === $checkSum) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); - - return; - } - // Do a modulo-97 operation on the large integer // We cannot use PHP's modulo operator, so we calculate the // modulo step-wisely instead if (1 !== $this->bigModulo97($checkSum)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); - - return; + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } @@ -109,11 +116,6 @@ private function toBigInt($string) continue; } - // Disallow lowercase characters - if (ctype_lower($char)) { - return false; - } - // Simply append digits $bigInt .= $char; } diff --git a/src/Symfony/Component/Validator/Constraints/Image.php b/src/Symfony/Component/Validator/Constraints/Image.php index 6401603a752e..707e2af312e0 100644 --- a/src/Symfony/Component/Validator/Constraints/Image.php +++ b/src/Symfony/Component/Validator/Constraints/Image.php @@ -15,6 +15,8 @@ * @Annotation * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * + * @author Benjamin Dulau + * * @api */ class Image extends File @@ -30,6 +32,7 @@ class Image extends File public $allowLandscape = true; public $allowPortrait = true; + // The constant for a wrong MIME type is taken from the parent class. public $mimeTypesMessage = 'This file is not a valid image.'; public $sizeNotDetectedMessage = 'The size of the image could not be detected.'; public $maxWidthMessage = 'The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.'; diff --git a/src/Symfony/Component/Validator/Constraints/ImageValidator.php b/src/Symfony/Component/Validator/Constraints/ImageValidator.php index 43bea3d7c212..4eaed687dc98 100644 --- a/src/Symfony/Component/Validator/Constraints/ImageValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ImageValidator.php @@ -20,6 +20,7 @@ * against minWidth, maxWidth, minHeight and maxHeight constraints * * @author Benjamin Dulau + * @author Bernhard Schussek */ class ImageValidator extends FileValidator { @@ -50,8 +51,10 @@ public function validate($value, Constraint $constraint) } $size = @getimagesize($value); + if (empty($size) || ($size[0] === 0) || ($size[1] === 0)) { - $this->context->addViolation($constraint->sizeNotDetectedMessage); + $this->buildViolation($constraint->sizeNotDetectedMessage) + ->addViolation(); return; } @@ -65,10 +68,10 @@ public function validate($value, Constraint $constraint) } if ($width < $constraint->minWidth) { - $this->context->addViolation($constraint->minWidthMessage, array( - '{{ width }}' => $width, - '{{ min_width }}' => $constraint->minWidth, - )); + $this->buildViolation($constraint->minWidthMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ min_width }}', $constraint->minWidth) + ->addViolation(); return; } @@ -80,10 +83,10 @@ public function validate($value, Constraint $constraint) } if ($width > $constraint->maxWidth) { - $this->context->addViolation($constraint->maxWidthMessage, array( - '{{ width }}' => $width, - '{{ max_width }}' => $constraint->maxWidth, - )); + $this->buildViolation($constraint->maxWidthMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ max_width }}', $constraint->maxWidth) + ->addViolation(); return; } @@ -95,10 +98,10 @@ public function validate($value, Constraint $constraint) } if ($height < $constraint->minHeight) { - $this->context->addViolation($constraint->minHeightMessage, array( - '{{ height }}' => $height, - '{{ min_height }}' => $constraint->minHeight, - )); + $this->buildViolation($constraint->minHeightMessage) + ->setParameter('{{ height }}', $height) + ->setParameter('{{ min_height }}', $constraint->minHeight) + ->addViolation(); return; } @@ -110,10 +113,10 @@ public function validate($value, Constraint $constraint) } if ($height > $constraint->maxHeight) { - $this->context->addViolation($constraint->maxHeightMessage, array( - '{{ height }}' => $height, - '{{ max_height }}' => $constraint->maxHeight, - )); + $this->buildViolation($constraint->maxHeightMessage) + ->setParameter('{{ height }}', $height) + ->setParameter('{{ max_height }}', $constraint->maxHeight) + ->addViolation(); } } @@ -125,10 +128,10 @@ public function validate($value, Constraint $constraint) } if ($ratio < $constraint->minRatio) { - $this->context->addViolation($constraint->minRatioMessage, array( - '{{ ratio }}' => $ratio, - '{{ min_ratio }}' => $constraint->minRatio, - )); + $this->buildViolation($constraint->minRatioMessage) + ->setParameter('{{ ratio }}', $ratio) + ->setParameter('{{ min_ratio }}', $constraint->minRatio) + ->addViolation(); } } @@ -138,32 +141,32 @@ public function validate($value, Constraint $constraint) } if ($ratio > $constraint->maxRatio) { - $this->context->addViolation($constraint->maxRatioMessage, array( - '{{ ratio }}' => $ratio, - '{{ max_ratio }}' => $constraint->maxRatio, - )); + $this->buildViolation($constraint->maxRatioMessage) + ->setParameter('{{ ratio }}', $ratio) + ->setParameter('{{ max_ratio }}', $constraint->maxRatio) + ->addViolation(); } } if (!$constraint->allowSquare && $width == $height) { - $this->context->addViolation($constraint->allowSquareMessage, array( - '{{ width }}' => $width, - '{{ height }}' => $height, - )); + $this->buildViolation($constraint->allowSquareMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ height }}', $height) + ->addViolation(); } if (!$constraint->allowLandscape && $width > $height) { - $this->context->addViolation($constraint->allowLandscapeMessage, array( - '{{ width }}' => $width, - '{{ height }}' => $height, - )); + $this->buildViolation($constraint->allowLandscapeMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ height }}', $height) + ->addViolation(); } if (!$constraint->allowPortrait && $width < $height) { - $this->context->addViolation($constraint->allowPortraitMessage, array( - '{{ width }}' => $width, - '{{ height }}' => $height, - )); + $this->buildViolation($constraint->allowPortraitMessage) + ->setParameter('{{ width }}', $width) + ->setParameter('{{ height }}', $height) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/IpValidator.php b/src/Symfony/Component/Validator/Constraints/IpValidator.php index 69286e90f99a..8db2ddb062b0 100644 --- a/src/Symfony/Component/Validator/Constraints/IpValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IpValidator.php @@ -95,9 +95,9 @@ public function validate($value, Constraint $constraint) } if (!filter_var($value, FILTER_VALIDATE_IP, $flag)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/IsbnValidator.php b/src/Symfony/Component/Validator/Constraints/IsbnValidator.php index 67b1fc44e72b..8972b9161040 100644 --- a/src/Symfony/Component/Validator/Constraints/IsbnValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IsbnValidator.php @@ -54,30 +54,45 @@ public function validate($value, Constraint $constraint) } } - if ('isbn10' === $constraint->type && !$this->validateIsbn10($canonical)) { - $this->context->addViolation($this->getMessage($constraint, 'isbn10'), array( - '{{ value }}' => $this->formatValue($value), - )); - } elseif ('isbn13' === $constraint->type && !$this->validateIsbn13($canonical)) { - $this->context->addViolation($this->getMessage($constraint, 'isbn13'), array( - '{{ value }}' => $this->formatValue($value), - )); - } elseif (!$this->validateIsbn10($canonical) && !$this->validateIsbn13($canonical)) { - $this->context->addViolation($this->getMessage($constraint), array( - '{{ value }}' => $this->formatValue($value), - )); + // Explicitly validate against ISBN-10 + if ('isbn10' === $constraint->type) { + if (!$this->validateIsbn10($canonical)) { + $this->buildViolation($this->getMessage($constraint, $constraint->type)) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + } + + return; + } + + // Explicitly validate against ISBN-13 + if ('isbn13' === $constraint->type) { + if (!$this->validateIsbn13($canonical)) { + $this->buildViolation($this->getMessage($constraint, $constraint->type)) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + } + + return; + } + + // Try both ISBNs + if (!$this->validateIsbn10($canonical) && !$this->validateIsbn13($canonical)) { + $this->buildViolation($this->getMessage($constraint)) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } protected function validateIsbn10($isbn) { - if (10 !== strlen($isbn)) { - return false; - } - $checkSum = 0; for ($i = 0; $i < 10; ++$i) { + if (!isset($isbn{$i})) { + return false; + } + if ('X' === $isbn{$i}) { $digit = 10; } elseif (ctype_digit($isbn{$i})) { @@ -89,12 +104,26 @@ protected function validateIsbn10($isbn) $checkSum += $digit * intval(10 - $i); } + if (isset($isbn{$i})) { + return false; + } + return 0 === $checkSum % 11; } protected function validateIsbn13($isbn) { - if (13 !== strlen($isbn) || !ctype_digit($isbn)) { + if (!ctype_digit($isbn)) { + return false; + } + + $length = strlen($isbn); + + if ($length < 13) { + return false; + } + + if ($length > 13) { return false; } @@ -116,9 +145,9 @@ protected function getMessage($constraint, $type = null) { if (null !== $constraint->message) { return $constraint->message; - } elseif ($type == 'isbn10') { + } elseif ('isbn10' === $type) { return $constraint->isbn10Message; - } elseif ($type == 'isbn13') { + } elseif ('isbn13' === $type) { return $constraint->isbn13Message; } diff --git a/src/Symfony/Component/Validator/Constraints/IssnValidator.php b/src/Symfony/Component/Validator/Constraints/IssnValidator.php index ed9a6fb9f812..2ee8a53dfd7e 100644 --- a/src/Symfony/Component/Validator/Constraints/IssnValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IssnValidator.php @@ -19,6 +19,7 @@ * Validates whether the value is a valid ISSN. * * @author Antonio J. GarcĂ­a Lagar + * @author Bernhard Schussek * * @see https://en.wikipedia.org/wiki/Issn */ @@ -42,25 +43,73 @@ public function validate($value, Constraint $constraint) } $value = (string) $value; + $canonical = $value; - // Compose regex pattern - $digitsPattern = $constraint->requireHyphen ? '\d{4}-\d{3}' : '\d{4}-?\d{3}'; - $checkSumPattern = $constraint->caseSensitive ? '[\d|X]' : '[\d|X|x]'; - $pattern = "/^".$digitsPattern.$checkSumPattern."$/"; + // 1234-567X + // ^ + if (isset($canonical{4}) && '-' === $canonical{4}) { + // remove hyphen + $canonical = substr($canonical, 0, 4).substr($canonical, 5); + } elseif ($constraint->requireHyphen) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); - if (!preg_match($pattern, $value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + return; + } + + $length = strlen($canonical); + + if ($length < 8) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + if ($length > 8) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); return; } - $canonical = strtoupper(str_replace('-', '', $value)); + // 1234567X + // ^^^^^^^ digits only + if (!ctype_digit(substr($canonical, 0, 7))) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + // 1234567X + // ^ digit, x or X + if (!ctype_digit($canonical{7}) && 'x' !== $canonical{7} && 'X' !== $canonical{7}) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + // 1234567X + // ^ case-sensitive? + if ($constraint->caseSensitive && 'x' === $canonical{7}) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } // Calculate a checksum. "X" equals 10. - $checkSum = 'X' === $canonical{7} - ? 10 : $canonical{7}; + $checkSum = 'X' === $canonical{7} || 'x' === $canonical{7} + ? 10 + : $canonical{7}; for ($i = 0; $i < 7; ++$i) { // Multiply the first digit by 8, the second by 7, etc. @@ -68,9 +117,9 @@ public function validate($value, Constraint $constraint) } if (0 !== $checkSum % 11) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/LanguageValidator.php b/src/Symfony/Component/Validator/Constraints/LanguageValidator.php index 3286f26b86bf..1cc2a438197b 100644 --- a/src/Symfony/Component/Validator/Constraints/LanguageValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LanguageValidator.php @@ -46,9 +46,9 @@ public function validate($value, Constraint $constraint) $languages = Intl::getLanguageBundle()->getLanguageNames(); if (!isset($languages[$value])) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/LengthValidator.php b/src/Symfony/Component/Validator/Constraints/LengthValidator.php index eeb9d3a5d1b3..cdc5bea985df 100644 --- a/src/Symfony/Component/Validator/Constraints/LengthValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LengthValidator.php @@ -13,7 +13,6 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** @@ -48,59 +47,24 @@ public function validate($value, Constraint $constraint) $length = strlen($stringValue); } - if ($constraint->min == $constraint->max && $length != $constraint->min) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->exactMessage) - ->setParameter('{{ value }}', $this->formatValue($stringValue)) - ->setParameter('{{ limit }}', $constraint->min) - ->setInvalidValue($value) - ->setPlural((int) $constraint->min) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->exactMessage, array( - '{{ value }}' => $this->formatValue($stringValue), - '{{ limit }}' => $constraint->min, - ), $value, (int) $constraint->min); - } - - return; - } - if (null !== $constraint->max && $length > $constraint->max) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->maxMessage) - ->setParameter('{{ value }}', $this->formatValue($stringValue)) - ->setParameter('{{ limit }}', $constraint->max) - ->setInvalidValue($value) - ->setPlural((int) $constraint->max) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->maxMessage, array( - '{{ value }}' => $this->formatValue($stringValue), - '{{ limit }}' => $constraint->max, - ), $value, (int) $constraint->max); - } + $this->buildViolation($constraint->min == $constraint->max ? $constraint->exactMessage : $constraint->maxMessage) + ->setParameter('{{ value }}', $this->formatValue($stringValue)) + ->setParameter('{{ limit }}', $constraint->max) + ->setInvalidValue($value) + ->setPlural((int) $constraint->max) + ->addViolation(); return; } if (null !== $constraint->min && $length < $constraint->min) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->minMessage) - ->setParameter('{{ value }}', $this->formatValue($stringValue)) - ->setParameter('{{ limit }}', $constraint->min) - ->setInvalidValue($value) - ->setPlural((int) $constraint->min) - ->addViolation(); - } else { - // 2.4 API - $this->context->addViolation($constraint->minMessage, array( - '{{ value }}' => $this->formatValue($stringValue), - '{{ limit }}' => $constraint->min, - ), $value, (int) $constraint->min); - } + $this->buildViolation($constraint->min == $constraint->max ? $constraint->exactMessage : $constraint->minMessage) + ->setParameter('{{ value }}', $this->formatValue($stringValue)) + ->setParameter('{{ limit }}', $constraint->min) + ->setInvalidValue($value) + ->setPlural((int) $constraint->min) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/LocaleValidator.php b/src/Symfony/Component/Validator/Constraints/LocaleValidator.php index 08f1d1d0a52e..dadbebaf4297 100644 --- a/src/Symfony/Component/Validator/Constraints/LocaleValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LocaleValidator.php @@ -46,9 +46,9 @@ public function validate($value, Constraint $constraint) $locales = Intl::getLocaleBundle()->getLocaleNames(); if (!isset($locales[$value])) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/Luhn.php b/src/Symfony/Component/Validator/Constraints/Luhn.php index 1feafe3e5b16..e9b406adede1 100644 --- a/src/Symfony/Component/Validator/Constraints/Luhn.php +++ b/src/Symfony/Component/Validator/Constraints/Luhn.php @@ -18,6 +18,9 @@ * * @Annotation * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) + * + * @author Tim Nagel + * @author Greg Knapp http://gregk.me/2011/php-implementation-of-bank-card-luhn-algorithm/ */ class Luhn extends Constraint { diff --git a/src/Symfony/Component/Validator/Constraints/LuhnValidator.php b/src/Symfony/Component/Validator/Constraints/LuhnValidator.php index a149aa9a8117..c82d6d8917a0 100644 --- a/src/Symfony/Component/Validator/Constraints/LuhnValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LuhnValidator.php @@ -55,9 +55,9 @@ public function validate($value, Constraint $constraint) $value = (string) $value; if (!ctype_digit($value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); return; } @@ -85,9 +85,9 @@ public function validate($value, Constraint $constraint) } if (0 === $checkSum || 0 !== $checkSum % 10) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php b/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php index e76135623a23..9deab1503b02 100644 --- a/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php @@ -32,9 +32,9 @@ public function validate($value, Constraint $constraint) } if (false === $value || (empty($value) && '0' != $value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/NullValidator.php b/src/Symfony/Component/Validator/Constraints/NullValidator.php index 645ffc034b11..1e6c3a53897a 100644 --- a/src/Symfony/Component/Validator/Constraints/NullValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NullValidator.php @@ -32,9 +32,9 @@ public function validate($value, Constraint $constraint) } if (null !== $value) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index c7bf7a93bcb3..0abd0cb44997 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -34,27 +34,27 @@ public function validate($value, Constraint $constraint) } if (!is_numeric($value)) { - $this->context->addViolation($constraint->invalidMessage, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->invalidMessage) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); return; } if (null !== $constraint->max && $value > $constraint->max) { - $this->context->addViolation($constraint->maxMessage, array( - '{{ value }}' => $value, - '{{ limit }}' => $constraint->max, - )); + $this->buildViolation($constraint->maxMessage) + ->setParameter('{{ value }}', $value) + ->setParameter('{{ limit }}', $constraint->max) + ->addViolation(); return; } if (null !== $constraint->min && $value < $constraint->min) { - $this->context->addViolation($constraint->minMessage, array( - '{{ value }}' => $value, - '{{ limit }}' => $constraint->min, - )); + $this->buildViolation($constraint->minMessage) + ->setParameter('{{ value }}', $value) + ->setParameter('{{ limit }}', $constraint->min) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/RegexValidator.php b/src/Symfony/Component/Validator/Constraints/RegexValidator.php index 88b19d023dbb..b18234454f09 100644 --- a/src/Symfony/Component/Validator/Constraints/RegexValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RegexValidator.php @@ -45,9 +45,9 @@ public function validate($value, Constraint $constraint) $value = (string) $value; if ($constraint->match xor preg_match($constraint->pattern, $value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/TimeValidator.php b/src/Symfony/Component/Validator/Constraints/TimeValidator.php index cf252b1d28dd..3d52fbe43585 100644 --- a/src/Symfony/Component/Validator/Constraints/TimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TimeValidator.php @@ -22,7 +22,23 @@ */ class TimeValidator extends ConstraintValidator { - const PATTERN = '/^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/'; + const PATTERN = '/^(\d{2}):(\d{2}):(\d{2})$/'; + + /** + * Checks whether a time is valid. + * + * @param int $hour The hour + * @param int $minute The minute + * @param int $second The second + * + * @return bool Whether the time is valid + * + * @internal + */ + public static function checkTime($hour, $minute, $second) + { + return $hour >= 0 && $hour < 24 && $minute >= 0 && $minute < 60 && $second >= 0 && $second < 60; + } /** * {@inheritdoc} @@ -43,10 +59,18 @@ public function validate($value, Constraint $constraint) $value = (string) $value; - if (!preg_match(static::PATTERN, $value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + if (!preg_match(static::PATTERN, $value, $matches)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + if (!self::checkTime($matches[1], $matches[2], $matches[3])) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/TrueValidator.php b/src/Symfony/Component/Validator/Constraints/TrueValidator.php index ef0e2608ef8e..a01f5bb37ded 100644 --- a/src/Symfony/Component/Validator/Constraints/TrueValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TrueValidator.php @@ -36,9 +36,9 @@ public function validate($value, Constraint $constraint) } if (true !== $value && 1 !== $value && '1' !== $value) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/TypeValidator.php b/src/Symfony/Component/Validator/Constraints/TypeValidator.php index 002c1bd2aa56..ca6e5aa7ca0f 100644 --- a/src/Symfony/Component/Validator/Constraints/TypeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TypeValidator.php @@ -48,9 +48,9 @@ public function validate($value, Constraint $constraint) return; } - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - '{{ type }}' => $constraint->type, - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ type }}', $constraint->type) + ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 70469592f8e4..3d184775acd5 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -59,9 +59,9 @@ public function validate($value, Constraint $constraint) $pattern = sprintf(static::PATTERN, implode('|', $constraint->protocols)); if (!preg_match($pattern, $value)) { - $this->context->addViolation($constraint->message, array( - '{{ value }}' => $this->formatValue($value), - )); + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/UuidValidator.php b/src/Symfony/Component/Validator/Constraints/UuidValidator.php index 9f1e1abb84e8..5361f12de0f0 100644 --- a/src/Symfony/Component/Validator/Constraints/UuidValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UuidValidator.php @@ -19,6 +19,7 @@ * Validates whether the value is a valid UUID per RFC 4122. * * @author Colin O'Dell + * @author Bernhard Schussek * * @see http://tools.ietf.org/html/rfc4122 * @see https://en.wikipedia.org/wiki/Universally_unique_identifier @@ -64,19 +65,43 @@ public function validate($value, Constraint $constraint) $value = (string) $value; if ($constraint->strict) { + $length = strlen($value); + + if ($length < static::STRICT_UUID_LENGTH) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + + if ($length > static::STRICT_UUID_LENGTH) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); + + return; + } + // Insert the allowed versions into the regular expression $pattern = sprintf(static::STRICT_PATTERN, implode('', $constraint->versions)); - if (strlen($value) !== static::STRICT_UUID_LENGTH || !preg_match($pattern, $value)) { - $this->context->addViolation($constraint->message, array('{{ value }}' => $value)); + if (!preg_match($pattern, $value)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } - } else { - // Trim any wrapping characters like [] or {} used by some legacy systems - $value = trim($value, '[]{}'); - if (!preg_match(static::LOOSE_PATTERN, $value)) { - $this->context->addViolation($constraint->message, array('{{ value }}' => $value)); - } + return; + } + + // Trim any wrapping characters like [] or {} used by some legacy systems + $value = trim($value, '[]{}'); + + if (!preg_match(static::LOOSE_PATTERN, $value)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php index f92f23ec6288..9bb12a25361b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php @@ -82,9 +82,9 @@ public function testSingleMethodExplicitName() $this->validator->validate($object, $constraint); - $this->assertViolation('My message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('My message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } public function testSingleStaticMethod() @@ -94,9 +94,9 @@ public function testSingleStaticMethod() $this->validator->validate($object, $constraint); - $this->assertViolation('Static message', array( - '{{ value }}' => 'baz', - )); + $this->buildViolation('Static message') + ->setParameter('{{ value }}', 'baz') + ->assertRaised(); } public function testClosure() @@ -110,9 +110,9 @@ public function testClosure() $this->validator->validate($object, $constraint); - $this->assertViolation('My message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('My message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } public function testClosureNullObject() @@ -125,9 +125,9 @@ public function testClosureNullObject() $this->validator->validate(null, $constraint); - $this->assertViolation('My message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('My message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } public function testClosureExplicitName() @@ -143,9 +143,9 @@ public function testClosureExplicitName() $this->validator->validate($object, $constraint); - $this->assertViolation('My message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('My message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } public function testArrayCallable() @@ -155,9 +155,9 @@ public function testArrayCallable() $this->validator->validate($object, $constraint); - $this->assertViolation('Callback message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('Callback message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } public function testArrayCallableNullObject() @@ -166,9 +166,9 @@ public function testArrayCallableNullObject() $this->validator->validate(null, $constraint); - $this->assertViolation('Callback message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('Callback message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } public function testArrayCallableExplicitName() @@ -180,9 +180,9 @@ public function testArrayCallableExplicitName() $this->validator->validate($object, $constraint); - $this->assertViolation('Callback message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('Callback message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } // BC with Symfony < 2.4 @@ -193,9 +193,9 @@ public function testSingleMethodBc() $this->validator->validate($object, $constraint); - $this->assertViolation('My message', array( - '{{ value }}' => 'foobar', - )); + $this->buildViolation('My message') + ->setParameter('{{ value }}', 'foobar') + ->assertRaised(); } // BC with Symfony < 2.4 diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php index a299dfc8718d..84b5bc3b0d63 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php @@ -161,6 +161,7 @@ public function testInvalidChoiceMultiple() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"baz"') + ->setInvalidValue('baz') ->assertRaised(); } @@ -274,6 +275,7 @@ public function testStrictWithMultipleChoices() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"3"') + ->setInvalidValue('3') ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php index f8ce07601154..31c7c7856288 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php @@ -65,14 +65,6 @@ public function getFourElements() ); } - public function getNotFourElements() - { - return array_merge( - $this->getThreeOrLessElements(), - $this->getFiveOrMoreElements() - ); - } - public function getFiveOrMoreElements() { return array( @@ -118,7 +110,7 @@ public function testValidValuesExact($value) /** * @dataProvider getFiveOrMoreElements */ - public function testInvalidValuesMax($value) + public function testTooManyValues($value) { $constraint = new Count(array( 'max' => 4, @@ -138,7 +130,7 @@ public function testInvalidValuesMax($value) /** * @dataProvider getThreeOrLessElements */ - public function testInvalidValuesMin($value) + public function testTooFewValues($value) { $constraint = new Count(array( 'min' => 4, @@ -156,9 +148,30 @@ public function testInvalidValuesMin($value) } /** - * @dataProvider getNotFourElements + * @dataProvider getFiveOrMoreElements + */ + public function testTooManyValuesExact($value) + { + $constraint = new Count(array( + 'min' => 4, + 'max' => 4, + 'exactMessage' => 'myMessage', + )); + + $this->validator->validate($value, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ count }}', count($value)) + ->setParameter('{{ limit }}', 4) + ->setInvalidValue($value) + ->setPlural(4) + ->assertRaised(); + } + + /** + * @dataProvider getThreeOrLessElements */ - public function testInvalidValuesExact($value) + public function testTooFewValuesExact($value) { $constraint = new Count(array( 'min' => 4, diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 1e90c09e31c3..378f53193637 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -71,7 +71,9 @@ public function testFailingExpressionAtObjectLevel() $this->validator->validate($object, $constraint); - $this->assertViolation('myMessage'); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'object') + ->assertRaised(); } public function testSucceedingExpressionAtPropertyLevel() @@ -106,7 +108,10 @@ public function testFailingExpressionAtPropertyLevel() $this->validator->validate('2', $constraint); - $this->assertViolation('myMessage', array(), 'data'); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"2"') + ->atPath('data') + ->assertRaised(); } public function testSucceedingExpressionAtNestedPropertyLevel() @@ -147,7 +152,10 @@ public function testFailingExpressionAtNestedPropertyLevel() $this->validator->validate('2', $constraint); - $this->assertViolation('myMessage', array(), 'reference.data'); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"2"') + ->atPath('reference.data') + ->assertRaised(); } /** @@ -184,6 +192,9 @@ public function testFailingExpressionAtPropertyLevelWithoutRoot() $this->validator->validate('2', $constraint); - $this->assertViolation('myMessage', array(), ''); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"2"') + ->atPath('') + ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php index dffbae396787..5d6c9a91ff4d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php @@ -202,10 +202,10 @@ public function testRatioTooSmall() $this->validator->validate($this->image, $constraint); - $this->assertViolation('myMessage', array( - '{{ ratio }}' => 1, - '{{ min_ratio }}' => 2, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ ratio }}', 1) + ->setParameter('{{ min_ratio }}', 2) + ->assertRaised(); } public function testRatioTooBig() @@ -217,10 +217,10 @@ public function testRatioTooBig() $this->validator->validate($this->image, $constraint); - $this->assertViolation('myMessage', array( - '{{ ratio }}' => 1, - '{{ max_ratio }}' => 0.5, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ ratio }}', 1) + ->setParameter('{{ max_ratio }}', 0.5) + ->assertRaised(); } public function testMaxRatioUsesTwoDecimalsOnly() @@ -267,10 +267,10 @@ public function testSquareNotAllowed() $this->validator->validate($this->image, $constraint); - $this->assertViolation('myMessage', array( - '{{ width }}' => 2, - '{{ height }}' => 2, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 2) + ->setParameter('{{ height }}', 2) + ->assertRaised(); } public function testLandscapeNotAllowed() @@ -282,10 +282,10 @@ public function testLandscapeNotAllowed() $this->validator->validate($this->imageLandscape, $constraint); - $this->assertViolation('myMessage', array( - '{{ width }}' => 2, - '{{ height }}' => 1, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 2) + ->setParameter('{{ height }}', 1) + ->assertRaised(); } public function testPortraitNotAllowed() @@ -297,9 +297,9 @@ public function testPortraitNotAllowed() $this->validator->validate($this->imagePortrait, $constraint); - $this->assertViolation('myMessage', array( - '{{ width }}' => 1, - '{{ height }}' => 2, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ width }}', 1) + ->setParameter('{{ height }}', 2) + ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php index 6f80e66a45dd..296ab7c94a3a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php @@ -52,10 +52,11 @@ public function getValidIsbn10() public function getInvalidIsbn10() { return array( + array('27234422841'), + array('272344228'), + array('0-4712-9231'), array('1234567890'), - array('987'), array('0987656789'), - array(0), array('7-35622-5444'), array('0-4X19-92611'), array('0_45122_5244'), @@ -89,16 +90,16 @@ public function getValidIsbn13() public function getInvalidIsbn13() { return array( - array('1234567890'), - array('987'), - array('0987656789'), - array(0), - array('0-4X19-9261981'), + array('978-27234422821'), + array('978-272344228'), + array('978-2723442-82'), + array('978-2723442281'), array('978-0321513774'), array('979-0431225385'), array('980-0474292319'), - array('978_0451225245'), - array('978#0471292319'), + array('0-4X19-92619812'), + array('978_2723442282'), + array('978#2723442282'), array('978-272C442282'), // chr(1) evaluates to 0 // 978-2070546817 is valid @@ -213,7 +214,7 @@ public function testInvalidIsbn13($isbn) /** * @dataProvider getValidIsbn */ - public function testValidIsbn($isbn) + public function testValidIsbnAny($isbn) { $constraint = new Isbn(); @@ -223,9 +224,25 @@ public function testValidIsbn($isbn) } /** - * @dataProvider getInvalidIsbn + * @dataProvider getInvalidIsbn10 + */ + public function testInvalidIsbnAnyIsbn10($isbn) + { + $constraint = new Isbn(array( + 'bothIsbnMessage' => 'myMessage', + )); + + $this->validator->validate($isbn, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$isbn.'"') + ->assertRaised(); + } + + /** + * @dataProvider getInvalidIsbn13 */ - public function testInvalidIsbn($isbn) + public function testInvalidIsbnAnyIsbn13($isbn) { $constraint = new Isbn(array( 'bothIsbnMessage' => 'myMessage', diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php index 4d353cd2b819..cb7ae3d8925d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php @@ -79,18 +79,12 @@ public function getValidIssn() ); } - public function getInvalidFormatedIssn() + public function getInvalidIssn() { return array( array(0), array('1539'), array('2156-537A'), - ); - } - - public function getInvalidValueIssn() - { - return array( array('1119-0231'), array('1684-5312'), array('1996-0783'), @@ -99,14 +93,6 @@ public function getInvalidValueIssn() ); } - public function getInvalidIssn() - { - return array_merge( - $this->getInvalidFormatedIssn(), - $this->getInvalidValueIssn() - ); - } - public function testNullIsValid() { $constraint = new Issn(); @@ -180,38 +166,6 @@ public function testValidIssn($issn) $this->assertNoViolation(); } - /** - * @dataProvider getInvalidFormatedIssn - */ - public function testInvalidFormatIssn($issn) - { - $constraint = new Issn(array( - 'message' => 'myMessage', - )); - - $this->validator->validate($issn, $constraint); - - $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"'.$issn.'"') - ->assertRaised(); - } - - /** - * @dataProvider getInvalidValueIssn - */ - public function testInvalidValueIssn($issn) - { - $constraint = new Issn(array( - 'message' => 'myMessage', - )); - - $this->validator->validate($issn, $constraint); - - $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"'.$issn.'"') - ->assertRaised(); - } - /** * @dataProvider getInvalidIssn */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php index ab0d0ff9c9b7..7674ae24272e 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php @@ -73,14 +73,6 @@ public function getFourCharacters() ); } - public function getNotFourCharacters() - { - return array_merge( - $this->getThreeOrLessCharacters(), - $this->getFiveOrMoreCharacters() - ); - } - public function getFiveOrMoreCharacters() { return array( @@ -189,9 +181,34 @@ public function testInvalidValuesMax($value, $mbOnly = false) } /** - * @dataProvider getNotFourCharacters + * @dataProvider getThreeOrLessCharacters + */ + public function testInvalidValuesExactLessThanFour($value, $mbOnly = false) + { + if ($mbOnly && !function_exists('mb_strlen')) { + $this->markTestSkipped('mb_strlen does not exist'); + } + + $constraint = new Length(array( + 'min' => 4, + 'max' => 4, + 'exactMessage' => 'myMessage', + )); + + $this->validator->validate($value, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$value.'"') + ->setParameter('{{ limit }}', 4) + ->setInvalidValue($value) + ->setPlural(4) + ->assertRaised(); + } + + /** + * @dataProvider getFiveOrMoreCharacters */ - public function testInvalidValuesExact($value, $mbOnly = false) + public function testInvalidValuesExactMoreThanFour($value, $mbOnly = false) { if ($mbOnly && !function_exists('mb_strlen')) { $this->markTestSkipped('mb_strlen does not exist'); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php index d395056f0006..4ad3c736eb09 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php @@ -97,6 +97,8 @@ public function getInvalidNumbers() array('1234567812345678'), array('4222222222222222'), array('0000000000000000'), + array('000000!000000000'), + array('42-22222222222222'), ); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index 9ed2b094b277..81f3ce624dfb 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -188,38 +188,6 @@ public function getInvalidValues() ); } - public function testMinMessageIsSet() - { - $constraint = new Range(array( - 'min' => 10, - 'max' => 20, - 'minMessage' => 'myMessage', - )); - - $this->validator->validate(9, $constraint); - - $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 9) - ->setParameter('{{ limit }}', 10) - ->assertRaised(); - } - - public function testMaxMessageIsSet() - { - $constraint = new Range(array( - 'min' => 10, - 'max' => 20, - 'maxMessage' => 'myMessage', - )); - - $this->validator->validate(21, $constraint); - - $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 21) - ->setParameter('{{ limit }}', 20) - ->assertRaised(); - } - public function testNonNumeric() { $this->validator->validate('abcd', new Range(array( diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php index 296464ae379e..b34ce8138b70 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php @@ -83,9 +83,9 @@ public function testInvalidStrictUuids($uuid) $this->validator->validate($uuid, $constraint); - $this->assertViolation('testMessage', array( - '{{ value }}' => $uuid, - )); + $this->buildViolation('testMessage') + ->setParameter('{{ value }}', '"'.$uuid.'"') + ->assertRaised(); } public function getInvalidStrictUuids() @@ -130,9 +130,9 @@ public function testVersionConstraintIsInvalid($uuid) $this->validator->validate($uuid, $constraint); - $this->assertViolation('myMessage', array( - '{{ value }}' => $uuid, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$uuid.'"') + ->assertRaised(); } /** @@ -177,9 +177,9 @@ public function testInvalidNonStrictUuids($uuid) $this->validator->validate($uuid, $constraint); - $this->assertViolation('myMessage', array( - '{{ value }}' => $uuid, - )); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$uuid.'"') + ->assertRaised(); } public function getInvalidNonStrictUuids() diff --git a/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php b/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php new file mode 100644 index 000000000000..06cfdb60fbd5 --- /dev/null +++ b/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Violation; + +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Validator\ExecutionContextInterface; +use Symfony\Component\Validator\Util\PropertyPath; + +/** + * Backwards-compatible implementation of {@link ConstraintViolationBuilderInterface}. + * + * @author Bernhard Schussek + * + * @internal You should not instantiate or use this class. Code against + * {@link ConstraintViolationBuilderInterface} instead. + * + * @deprecated This class will be removed in Symfony 3.0. + */ +class LegacyConstraintViolationBuilder implements ConstraintViolationBuilderInterface +{ + /** + * @var ExecutionContextInterface + */ + private $context; + + /** + * @var string + */ + private $message; + + /** + * @var array + */ + private $parameters; + + /** + * @var mixed + */ + private $root; + + /** + * @var mixed + */ + private $invalidValue; + + /** + * @var string + */ + private $propertyPath; + + /** + * @var int|null + */ + private $plural; + + /** + * @var mixed + */ + private $code; + + public function __construct(ExecutionContextInterface $context, $message, array $parameters) + { + $this->context = $context; + $this->message = $message; + $this->parameters = $parameters; + $this->root = $context->getRoot(); + $this->invalidValue = $context->getValue(); + } + + /** + * {@inheritdoc} + */ + public function atPath($path) + { + $this->propertyPath = $path; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setParameter($key, $value) + { + $this->parameters[$key] = $value; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setTranslationDomain($translationDomain) + { + // can't be set in the old API + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setInvalidValue($invalidValue) + { + $this->invalidValue = $invalidValue; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPlural($number) + { + $this->plural = $number; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setCode($code) + { + $this->code = $code; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addViolation() + { + if ($this->propertyPath) { + $this->context->addViolationAt($this->propertyPath, $this->message, $this->parameters, $this->invalidValue, $this->plural, $this->code); + + return; + } + + $this->context->addViolation($this->message, $this->parameters, $this->invalidValue, $this->plural, $this->code); + } +}