diff --git a/src/Symfony/Component/Validator/Constraints/Date.php b/src/Symfony/Component/Validator/Constraints/Date.php index f9923052e0cf1..6f1d728e50cf9 100644 --- a/src/Symfony/Component/Validator/Constraints/Date.php +++ b/src/Symfony/Component/Validator/Constraints/Date.php @@ -22,5 +22,50 @@ */ class Date extends Constraint { - public $message = 'This value is not a valid date.'; -} + public $message = 'This value is not a valid date.'; + public $messageBeforeDate = 'The date should be before {{ before }}.'; + public $messageAfterDate = 'The date should be after {{ after }}.'; + public $dateFormatMessages = 'yyyy-MM-dd'; // see http://userguide.icu-project.org/formatparse/datetime + public $dateFormatter; + + public $before; + public $after; + + public function __construct($options = null) + { + if (isset($options['after']) && !$options['after'] instanceof \DateTime) { + $options['after'] = new \DateTime($options['after']); + } + + if (isset($options['before']) && !$options['before'] instanceof \DateTime) { + $options['before'] = new \DateTime($options['before']); + } + + if (isset($options['before']) && isset($options['after'])) { + if ($options['before'] == $options['after']) { + throw new InvalidOptionsException('The options "after" and "before" may not have the same value. ' . __CLASS__, array('after', 'before')); + } + if ($options['before'] < $options['after']) { + throw new InvalidOptionsException('The value of "before" must be a date later than the value of "after". ' . __CLASS__, array('after', 'before')); + } + } + + if (isset($options['dateFormatter'])) { + if (!$options['dateFormatter'] instanceof \IntlDateFormatter) { + throw new InvalidOptionsException('The option "dateFormatter" must be an instance of \IntlDateFormatter.' . __CLASS__, array('dateFormatter')); + } + } else { + $options['dateFormatter'] = new \IntlDateFormatter('en_US', \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE); + } + + if (isset($options['dateFormatMessages'])) { + if (!$options['dateFormatter']->setPattern($options['dateFormatMessages'])) { + throw new InvalidOptionsException('The value of the option "dateFormatMessages" is invalid. ' . __CLASS__, array('dateFormatMessages')); + } + } else { + $options['dateFormatter']->setPattern($this->dateFormatMessages); + } + + parent::__construct($options); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/Validator/Constraints/DateValidator.php b/src/Symfony/Component/Validator/Constraints/DateValidator.php index f891f9d621a9b..b43420d0c880c 100644 --- a/src/Symfony/Component/Validator/Constraints/DateValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateValidator.php @@ -29,18 +29,41 @@ class DateValidator extends ConstraintValidator */ public function validate($value, Constraint $constraint) { - if (null === $value || '' === $value || $value instanceof \DateTime) { + if (null === $value || '' === $value) { return; } - if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) { - throw new UnexpectedTypeException($value, 'string'); + if($value instanceof \DateTime) { + $dateTime = $value; + } else { + + if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) { + throw new UnexpectedTypeException($value, 'string'); + } + + $value = (string) $value; + + if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) { + $this->context->addViolation($constraint->message, array('{{ value }}' => $value)); + + return; + } + + $dateTime = new \DateTime($matches[1].'-'.$matches[2].'-'.$matches[3]); } - $value = (string) $value; + if (null !== $constraint->before && $dateTime >= $constraint->before) { + $formattedBeforeDate = $constraint->dateFormatter->format($constraint->before); + $this->context->addViolation($constraint->messageBeforeDate, array('{{ value }}' => $value, '{{ before }}' => $formattedBeforeDate)); - if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) { - $this->context->addViolation($constraint->message, array('{{ value }}' => $value)); + return; + } + + if (null !== $constraint->after && $dateTime <= $constraint->after) { + $formattedAfterDate = $constraint->dateFormatter->format($constraint->after); + $this->context->addViolation($constraint->messageAfterDate, array('{{ value }}' => $value, '{{ after }}' => $formattedAfterDate)); + + return; } } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php index b9cf8816b76a5..c57c4ff1692b8 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php @@ -78,6 +78,7 @@ public function testValidDates($date) public function getValidDates() { return array( + array(''), array('2010-01-01'), array('1955-12-12'), array('2030-05-31'), @@ -113,4 +114,228 @@ public function getInvalidDates() array('2010-02-29'), ); } + + /** + * @dataProvider getValidBeforeDates + */ + public function testValidBeforeDates($date) + { + $constraint = new Date(array( + 'before' => '2015-01-01' + )); + + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($date, $constraint); + } + + public function getValidBeforeDates() + { + return array( + array('2014-12-31'), + array('2000-01-01'), + array('1980-01-01'), + ); + } + + /** + * @dataProvider getInvalidBeforeDates + */ + public function testInvalidBeforeDates($date) + { + $constraint = new Date(array( + 'messageBeforeDate' => 'myMessage', + 'before' => '2015-01-01' + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => $date, + '{{ before }}' => '2015-01-01', + )); + + $this->validator->validate($date, $constraint); + } + + public function getInvalidBeforeDates() + { + return array( + array('2015-01-01'), + array('2018-12-20'), + array('2016-02-29'), + ); + } + + /** + * @dataProvider getValidAfterDates + */ + public function testValidAfterDates($date) + { + $constraint = new Date(array( + 'after' => '2015-01-01' + )); + + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($date, $constraint); + } + + public function getValidAfterDates() + { + return array( + array('2015-01-02'), + array('2016-02-29'), + array('2100-12-31'), + ); + } + + /** + * @dataProvider getInvalidAfterDates + */ + public function testInvalidAfterDates($date) + { + $constraint = new Date(array( + 'messageAfterDate' => 'myMessage', + 'after' => '2015-01-01' + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => $date, + '{{ after }}' => '2015-01-01', + )); + + $this->validator->validate($date, $constraint); + } + + public function getInvalidAfterDates() + { + return array( + array('2015-01-01'), + array('2014-12-31'), + array('1980-01-01'), + ); + } + + + /** + * @dataProvider getValidInBetweenDates + */ + public function testValidInBetweenDates($date) + { + $constraint = new Date(array( + 'after' => '2014-12-31', + 'before' => '2017-01-01' + )); + + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($date, $constraint); + } + + public function getValidInBetweenDates() + { + return array( + array('2015-01-01'), + array('2015-12-31'), + array('2016-12-31'), + ); + } + + /** + * @dataProvider getTooLateInBetweenDates + */ + public function testTooLateInBetweenDates($date) + { + $constraint = new Date(array( + 'messageAfterDate' => 'myMessage', + 'messageBeforeDate' => 'myMessage', + 'after' => '2014-12-31', + 'before' => '2017-01-01' + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => $date, + '{{ before }}' => '2017-01-01', + )); + + $this->validator->validate($date, $constraint); + } + + public function getTooLateInBetweenDates() + { + return array( + array('2017-01-01'), + array('2020-06-06'), + ); + } + + /** + * @dataProvider getTooEarlyInBetweenDates + */ + public function testTooEarlyInBetweenDates($date) + { + $constraint = new Date(array( + 'messageAfterDate' => 'myMessage', + 'messageBeforeDate' => 'myMessage', + 'after' => '2014-12-31', + 'before' => '2017-01-01' + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => $date, + '{{ after }}' => '2014-12-31', + )); + + $this->validator->validate($date, $constraint); + } + + public function getTooEarlyInBetweenDates() + { + return array( + array('2014-12-31'), + array('2000-06-06'), + ); + } + + /** + * @dataProvider getDateFormatMessages + */ + public function testDateFormatMessagesFormatter($format, $expected) + { + $constraint = new Date(array( + 'messageAfterDate' => 'myMessage', + 'after' => '2014-12-31', + 'dateFormatMessages' => $format, + )); + + $date = '2000-01-01'; + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => $date, + '{{ after }}' => $expected, + )); + + $this->validator->validate($date, $constraint); + } + + public function getDateFormatMessages() + { + return array( + array('dd/MM/yy', '31/12/14'), + array('yyyy-MM-dd', '2014-12-31'), + array('yyyyMMdd HH:mm:ss', '20141231 00:00:00'), + ); + } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: