From 22ba1998501e8a9914b2e990d628fd97393c1509 Mon Sep 17 00:00:00 2001 From: Jack Peterson Date: Thu, 27 Apr 2017 14:50:25 -0700 Subject: [PATCH] Throw an InvalidArgumentException when one attempts to create a DateTime, Date, Month element with 'min' or 'max' string specification(s) that does not conform to the expected \DateTime format for the given element rather than mysteriously fail when given valid data on input. --- src/Element/Date.php | 3 ++ src/Element/DateTime.php | 66 +++++++++++++++++++++++++----- src/Element/DateTimeLocal.php | 3 ++ src/Element/Month.php | 3 ++ src/Element/Time.php | 3 ++ src/Element/Week.php | 34 +++++++++++++++ test/Element/DateTest.php | 30 ++++++++++++++ test/Element/DateTimeLocalTest.php | 38 +++++++++++++++-- test/Element/DateTimeTest.php | 30 ++++++++++++++ test/Element/TimeTest.php | 30 ++++++++++++++ test/Element/WeekTest.php | 14 +++---- 11 files changed, 232 insertions(+), 22 deletions(-) diff --git a/src/Element/Date.php b/src/Element/Date.php index 0ecfc5a9..f3425446 100644 --- a/src/Element/Date.php +++ b/src/Element/Date.php @@ -16,6 +16,9 @@ class Date extends DateTimeElement { + + const DATETIME_FORMAT = 'Y-m-d'; + /** * Seed attributes * diff --git a/src/Element/DateTime.php b/src/Element/DateTime.php index e2805936..c19e1bb3 100644 --- a/src/Element/DateTime.php +++ b/src/Element/DateTime.php @@ -12,6 +12,7 @@ use DateInterval; use DateTime as PhpDateTime; use Zend\Form\Element; +use Zend\Form\Exception\InvalidArgumentException; use Zend\InputFilter\InputProviderInterface; use Zend\Validator\Date as DateValidator; use Zend\Validator\DateStep as DateStepValidator; @@ -120,17 +121,60 @@ protected function getValidators() $validators = []; $validators[] = $this->getDateValidator(); - if (isset($this->attributes['min'])) { - $validators[] = new GreaterThanValidator([ - 'min' => $this->attributes['min'], - 'inclusive' => true, - ]); + if (isset($this->attributes['min']) && + \DateTime::createFromFormat( + static::DATETIME_FORMAT, + $this->attributes['min'] + ) instanceof \DateTimeInterface + ) { + $validators[] = new GreaterThanValidator( + [ + 'min' => $this->attributes['min'], + 'inclusive' => true, + ] + ); + } elseif (isset($this->attributes['min']) && + ! \DateTime::createFromFormat( + static::DATETIME_FORMAT, + $this->attributes['min'] + ) instanceof \DateTimeInterface + ) { + throw new InvalidArgumentException( + sprintf( + '%1$s expects "min" to conform to %2$s; received "%3$s"', + __METHOD__, + static::DATETIME_FORMAT, + $this->attributes['min'] + ) + ); } - if (isset($this->attributes['max'])) { - $validators[] = new LessThanValidator([ - 'max' => $this->attributes['max'], + + if (isset($this->attributes['max']) && + \DateTime::createFromFormat( + static::DATETIME_FORMAT, + $this->attributes['max'] + ) instanceof \DateTimeInterface + ) { + $validators[] = new LessThanValidator( + [ + 'max' => $this->attributes['max'], 'inclusive' => true, - ]); + ] + ); + } elseif (isset($this->attributes['max']) && + ! \DateTime::createFromFormat( + static::DATETIME_FORMAT, + $this->attributes['max'] + ) instanceof \DateTimeInterface + ) { + throw new InvalidArgumentException( + sprintf( + '%1$s expects "max" to conform to %2$s; received "%3$s"', + __METHOD__, + static::DATETIME_FORMAT, + $this->attributes['max'] + ) + ); } if (! isset($this->attributes['step']) || 'any' !== $this->attributes['step'] @@ -145,7 +189,7 @@ protected function getValidators() /** * Retrieves a Date Validator configured for a DateTime Input type * - * @return DateTime + * @return DateValidator */ protected function getDateValidator() { @@ -155,7 +199,7 @@ protected function getDateValidator() /** * Retrieves a DateStep Validator configured for a DateTime Input type * - * @return DateTime + * @return DateStepValidator */ protected function getStepValidator() { diff --git a/src/Element/DateTimeLocal.php b/src/Element/DateTimeLocal.php index 0cc3f58d..f13d0f29 100644 --- a/src/Element/DateTimeLocal.php +++ b/src/Element/DateTimeLocal.php @@ -13,8 +13,11 @@ class DateTimeLocal extends DateTime { + const DATETIME_LOCAL_FORMAT = 'Y-m-d\TH:i'; + const DATETIME_FORMAT = self::DATETIME_LOCAL_FORMAT; + /** * Seed attributes * diff --git a/src/Element/Month.php b/src/Element/Month.php index fbfcf01d..a5e24d2b 100644 --- a/src/Element/Month.php +++ b/src/Element/Month.php @@ -15,6 +15,9 @@ class Month extends DateTime { + + const DATETIME_FORMAT = 'Y-m'; + /** * Seed attributes * diff --git a/src/Element/Time.php b/src/Element/Time.php index ff1e7467..3c1981fd 100644 --- a/src/Element/Time.php +++ b/src/Element/Time.php @@ -14,6 +14,9 @@ class Time extends DateTime { + + const DATETIME_FORMAT = 'H:i:s'; + /** * Seed attributes * diff --git a/src/Element/Week.php b/src/Element/Week.php index d0a5e59a..f4552d7c 100644 --- a/src/Element/Week.php +++ b/src/Element/Week.php @@ -11,6 +11,8 @@ use Zend\Validator\DateStep as DateStepValidator; use Zend\Validator\Regex as RegexValidator; +use Zend\Validator\GreaterThan as GreaterThanValidator; +use Zend\Validator\LessThan as LessThanValidator; class Week extends DateTime { @@ -52,4 +54,36 @@ protected function getStepValidator() 'step' => new \DateInterval("P{$stepValue}W"), ]); } + + /** + * @see https://bugs.php.net/bug.php?id=74511 + * @return array + */ + protected function getValidators() + { + if ($this->validators) { + return $this->validators; + } + $validators = []; + $validators[] = $this->getDateValidator(); + if (isset($this->attributes['min'])) { + $validators[] = new GreaterThanValidator([ + 'min' => $this->attributes['min'], + 'inclusive' => true, + ]); + } + if (isset($this->attributes['max'])) { + $validators[] = new LessThanValidator([ + 'max' => $this->attributes['max'], + 'inclusive' => true, + ]); + } + if (! isset($this->attributes['step']) + || 'any' !== $this->attributes['step'] + ) { + $validators[] = $this->getStepValidator(); + } + $this->validators = $validators; + return $this->validators; + } } diff --git a/test/Element/DateTest.php b/test/Element/DateTest.php index a9d2e219..0c5d49e0 100644 --- a/test/Element/DateTest.php +++ b/test/Element/DateTest.php @@ -12,6 +12,7 @@ use DateTime; use PHPUnit\Framework\TestCase; use Zend\Form\Element\Date as DateElement; +use Zend\Form\Exception\InvalidArgumentException; /** * @covers \Zend\Form\Element\Date @@ -158,4 +159,33 @@ public function testStepValidatorIgnoresDaylightSavings() } } } + + public function testFailsWithInvalidMinSpecification() + { + $element = new DateElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'min' => '2000-01-01T00', + 'step' => '1', + ] + ); + + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } + + public function testFailsWithInvalidMaxSpecification() + { + $element = new DateElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'max' => '2001-01-01T00', + 'step' => '1', + ] + ); + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } } diff --git a/test/Element/DateTimeLocalTest.php b/test/Element/DateTimeLocalTest.php index fb52ab92..060aabb7 100644 --- a/test/Element/DateTimeLocalTest.php +++ b/test/Element/DateTimeLocalTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\TestCase; use Zend\Form\Element\DateTimeLocal as DateTimeLocalElement; +use Zend\Form\Exception\InvalidArgumentException; class DateTimeLocalTest extends TestCase { @@ -19,8 +20,8 @@ public function testProvidesInputSpecificationThatIncludesValidatorsBasedOnAttri $element = new DateTimeLocalElement('foo'); $element->setAttributes([ 'inclusive' => true, - 'min' => '2000-01-01T00:00Z', - 'max' => '2001-01-01T00:00Z', + 'min' => '2000-01-01T00:00', + 'max' => '2001-01-01T00:00', 'step' => '1', ]); @@ -40,11 +41,11 @@ public function testProvidesInputSpecificationThatIncludesValidatorsBasedOnAttri switch ($class) { case 'Zend\Validator\GreaterThan': $this->assertTrue($validator->getInclusive()); - $this->assertEquals('2000-01-01T00:00Z', $validator->getMin()); + $this->assertEquals('2000-01-01T00:00', $validator->getMin()); break; case 'Zend\Validator\LessThan': $this->assertTrue($validator->getInclusive()); - $this->assertEquals('2001-01-01T00:00Z', $validator->getMax()); + $this->assertEquals('2001-01-01T00:00', $validator->getMax()); break; case 'Zend\Validator\DateStep': $dateInterval = new \DateInterval('PT1M'); @@ -55,4 +56,33 @@ public function testProvidesInputSpecificationThatIncludesValidatorsBasedOnAttri } } } + + public function testFailsWithInvalidMinSpecification() + { + $element = new DateTimeLocalElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'min' => '2001-01-01T00:00Z', + 'step' => '1', + ] + ); + + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } + + public function testFailsWithInvalidMaxSpecification() + { + $element = new DateTimeLocalElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'max' => '2001-01-01T00:00Z', + 'step' => '1', + ] + ); + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } } diff --git a/test/Element/DateTimeTest.php b/test/Element/DateTimeTest.php index fba52a1f..76596f4e 100644 --- a/test/Element/DateTimeTest.php +++ b/test/Element/DateTimeTest.php @@ -12,6 +12,7 @@ use DateTime; use PHPUnit\Framework\TestCase; use Zend\Form\Element\DateTime as DateTimeElement; +use Zend\Form\Exception\InvalidArgumentException; class DateTimeTest extends TestCase { @@ -119,4 +120,33 @@ public function testSetFormatWithOptions() $this->assertSame($format, $element->getFormat()); } + + public function testFailsWithInvalidMinSpecification() + { + $element = new DateTimeElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'min' => '2000-01-01T00', + 'step' => '1', + ] + ); + + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } + + public function testFailsWithInvalidMaxSpecification() + { + $element = new DateTimeElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'max' => '2001-01-01T00', + 'step' => '1', + ] + ); + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } } diff --git a/test/Element/TimeTest.php b/test/Element/TimeTest.php index 70b69cc4..b08f7679 100644 --- a/test/Element/TimeTest.php +++ b/test/Element/TimeTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\TestCase; use Zend\Form\Element\Time as TimeElement; +use Zend\Form\Exception\InvalidArgumentException; class TimeTest extends TestCase { @@ -58,4 +59,33 @@ public function testProvidesInputSpecificationThatIncludesValidatorsBasedOnAttri } } } + + public function testFailsWithInvalidMinSpecification() + { + $element = new TimeElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'min' => '00:00', + 'step' => '1', + ] + ); + + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } + + public function testFailsWithInvalidMaxSpecification() + { + $element = new TimeElement('foo'); + $element->setAttributes( + [ + 'inclusive' => true, + 'max' => '00:00', + 'step' => '1', + ] + ); + $this->expectException(InvalidArgumentException::class); + $element->getInputSpecification(); + } } diff --git a/test/Element/WeekTest.php b/test/Element/WeekTest.php index ac09f96c..5e00f199 100644 --- a/test/Element/WeekTest.php +++ b/test/Element/WeekTest.php @@ -59,13 +59,13 @@ public function testProvidesInputSpecificationThatIncludesValidatorsBasedOnAttri public function weekValuesDataProvider() { return [ - // value expected - ['2012-W01', true], - ['2012-W52', true], - ['2012-01', false], - ['W12-2012', false], - ['2012-W1', false], - ['12-W01', false], + // value expected + ['2012-W01', true], + ['2012-W52', true], + ['2012-01', false], + ['W12-2012', false], + ['2012-W1', false], + ['12-W01', false], ]; }