Skip to content
This repository was archived by the owner on Jan 31, 2020. It is now read-only.

Commit 8ee395c

Browse files
committed
Merge branch 'feature/5458' into develop
Close zendframework/zendframework#5458
150 parents 997a8a7 + 047576f + 1b49389 + d7193b9 + 3fed016 + 082acdb + 3ddc8fa + 274bd7f + c9e433d + 99f6515 + 8d1a271 + 2c2bc35 + 16359e3 + 10b9b3c + d05ac01 + 9628c66 + 66c8763 + 943d333 + 8342f70 + 4eb4184 + 4ea0bc2 + 860b725 + 7d29287 + 49a7844 + 3836aa0 + de1fe83 + 929eb12 + 6e6902f + f823467 + f3b3f76 + 93aff47 + 5f2adec + 7674ec8 + 2200ed2 + f033c86 + d43a2da + 4434323 + 88b21e1 + b02b602 + b692de7 + 7f833d2 + 2eaf4da + a7848de + 5bd9f85 + d7a26e8 + 173f53d + bd77e8e + 45f017e + 118612c + 3a09f79 + 47d92bc + dbf56ad + a753b61 + 6467186 + 5ac4124 + 2bf68ca + e7cd709 + 7a552db + f112e0c + cec42fc + 066eb37 + 3569d8b + 00def10 + ecae47b + 0e552a5 + 4f854c2 + 2b17650 + c1c0447 + 73b1f80 + dd4a335 + a40eb42 + 9262db1 + bdbd950 + 6d50117 + 10b08a7 + f692a0d + ebe63d3 + 528260b + 5f04a7f + c0dcd12 + 224f280 + 8785f25 + 866539b + d711927 + e280213 + 002f0d4 + 5ffcbbe + 8937705 + 5803840 + 3d7cd9a + 6b14f0e + 24bd169 + 0eba870 + 4e4acfe + 9e243bd + e156354 + 2a84563 + 3f1f758 + 4e425f4 + 7030f97 + ea08a0f + 213ebd9 + 5d80740 + 800c29c + be31ff1 + 8f989d6 + 8b02a8b + 4cfd82c + c411f06 + 4229561 + b321e3b + 2e660f9 + 3fff65f + a2eb7bf + 82469ff + 22fa741 + 99819cb + 8c16404 + a7c0820 + 7d277af + da0bec5 + 33f214a + a07f208 + 963d2fe + 1100f88 + 6713bf5 + 7421758 + b64a638 + 772a2a1 + dff3231 + 56bc4ca + 463e3d7 + 14bd316 + f8b9e58 + ef0268c + 3fe91ce + 130da19 + 6d4097d + 02fa5b3 + 927b7df + 2fbc2a0 + c772270 + 1cdc0cc + ecb5260 + 3ec34d8 + 23824d1 + 56e8233 + 7b8e63b + 838bdc9 + 55a8aa1 commit 8ee395c

File tree

4 files changed

+140
-120
lines changed

4 files changed

+140
-120
lines changed

src/Date.php

Lines changed: 97 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,49 @@
1212
use DateTime;
1313
use Traversable;
1414

15+
/**
16+
* Validates that a given value is a DateTime instance or can be converted into one.
17+
*/
1518
class Date extends AbstractValidator
1619
{
20+
/**#@+
21+
* Validity constants
22+
* @var string
23+
*/
1724
const INVALID = 'dateInvalid';
1825
const INVALID_DATE = 'dateInvalidDate';
1926
const FALSEFORMAT = 'dateFalseFormat';
27+
/**#@-*/
28+
29+
/**
30+
* Default format constant
31+
* @var string
32+
*/
33+
const FORMAT_DEFAULT = 'Y-m-d';
2034

2135
/**
2236
* Validation failure message template definitions
2337
*
2438
* @var array
2539
*/
2640
protected $messageTemplates = array(
27-
self::INVALID => "Invalid type given. String, integer, array or DateTime expected",
28-
self::INVALID_DATE => "The input does not appear to be a valid date",
29-
self::FALSEFORMAT => "The input does not fit the date format '%format%'",
41+
self::INVALID => "Invalid type given. String, integer, array or DateTime expected",
42+
self::INVALID_DATE => "The input does not appear to be a valid date",
43+
self::FALSEFORMAT => "The input does not fit the date format '%format%'",
3044
);
3145

3246
/**
3347
* @var array
3448
*/
3549
protected $messageVariables = array(
36-
'format' => 'format'
50+
'format' => 'format',
3751
);
3852

3953
/**
40-
* Optional format
41-
*
42-
* @var string|null
54+
* @var string
4355
*/
44-
protected $format;
56+
protected $format = self::FORMAT_DEFAULT;
57+
4558

4659
/**
4760
* Sets validator options
@@ -58,10 +71,6 @@ public function __construct($options = array())
5871
$options = $temp;
5972
}
6073

61-
if (array_key_exists('format', $options)) {
62-
$this->setFormat($options['format']);
63-
}
64-
6574
parent::__construct($options);
6675
}
6776

@@ -78,79 +87,107 @@ public function getFormat()
7887
/**
7988
* Sets the format option
8089
*
90+
* Format cannot be null. It will always default to 'Y-m-d', even
91+
* if null is provided.
92+
*
8193
* @param string $format
8294
* @return Date provides a fluent interface
95+
* @todo validate the format
8396
*/
84-
public function setFormat($format = null)
97+
public function setFormat($format = self::FORMAT_DEFAULT)
8598
{
86-
$this->format = $format;
99+
$this->format = (empty($format)) ? self::FORMAT_DEFAULT : $format;
87100
return $this;
88101
}
89102

90103
/**
91-
* Returns true if $value is a valid date of the format YYYY-MM-DD
92-
* If optional $format is set the date format is checked
93-
* according to DateTime
104+
* Returns true if $value is a DateTime instance or can be converted into one.
94105
*
95106
* @param string|array|int|DateTime $value
96107
* @return bool
97108
*/
98109
public function isValid($value)
99110
{
100-
if (!is_string($value)
101-
&& !is_array($value)
102-
&& !is_int($value)
103-
&& !($value instanceof DateTime)
104-
) {
105-
$this->error(self::INVALID);
106-
return false;
107-
}
108-
109111
$this->setValue($value);
110112

111-
$format = $this->getFormat();
113+
if (!$this->convertToDateTime($value)) {
114+
$this->error(self::INVALID_DATE);
115+
return false;
116+
}
112117

113-
if ($value instanceof DateTime) {
114-
return true;
115-
} elseif (is_int($value)
116-
|| (is_string($value) && null !== $format)
117-
) {
118-
$date = (is_int($value))
119-
? date_create("@$value") // from timestamp
120-
: DateTime::createFromFormat($format, $value);
118+
return true;
119+
}
121120

122-
// Invalid dates can show up as warnings (ie. "2007-02-99")
123-
// and still return a DateTime object
124-
$errors = DateTime::getLastErrors();
121+
/**
122+
* Attempts to convert an int, string, or array to a DateTime object
123+
*
124+
* @param string|int|array $param
125+
* @param bool $addErrors
126+
* @return bool|DateTime
127+
*/
128+
protected function convertToDateTime($param, $addErrors = true)
129+
{
130+
if ($param instanceof DateTime) {
131+
return $param;
132+
}
125133

126-
if ($errors['warning_count'] > 0) {
127-
$this->error(self::INVALID_DATE);
128-
return false;
129-
}
130-
if ($date === false) {
131-
$this->error(self::INVALID_DATE);
132-
return false;
133-
}
134-
} else {
135-
if (is_array($value)) {
136-
$value = implode('-', $value);
134+
$type = gettype($param);
135+
if (!in_array($type, array('string', 'integer', 'array'))) {
136+
if ($addErrors) {
137+
$this->error(self::INVALID);
137138
}
139+
return false;
140+
}
138141

139-
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
140-
$this->format = 'Y-m-d';
141-
$this->error(self::FALSEFORMAT);
142-
$this->format = null;
143-
return false;
144-
}
142+
$convertMethod = 'convert' . ucfirst($type);
143+
return $this->{$convertMethod}($param, $addErrors);
144+
}
145+
146+
/**
147+
* Attempts to convert an integer into a DateTime object
148+
*
149+
* @param integer $value
150+
* @return bool|DateTime
151+
*/
152+
protected function convertInteger($value)
153+
{
154+
return date_create("@$value");
155+
}
145156

146-
list($year, $month, $day) = sscanf($value, '%d-%d-%d');
157+
/**
158+
* Attempts to convert a string into a DateTime object
159+
*
160+
* @param string $value
161+
* @param bool $addErrors
162+
* @return bool|DateTime
163+
*/
164+
protected function convertString($value, $addErrors = true)
165+
{
166+
$date = DateTime::createFromFormat($this->format, $value);
147167

148-
if (!checkdate($month, $day, $year)) {
149-
$this->error(self::INVALID_DATE);
150-
return false;
168+
// Invalid dates can show up as warnings (ie. "2007-02-99")
169+
// and still return a DateTime object.
170+
$errors = DateTime::getLastErrors();
171+
if ($errors['warning_count'] > 0) {
172+
if ($addErrors) {
173+
$this->error(self::FALSEFORMAT);
151174
}
175+
return false;
152176
}
153177

154-
return true;
178+
return $date;
179+
}
180+
181+
/**
182+
* Implodes the array into a string and proxies to {@link convertString()}.
183+
*
184+
* @param array $value
185+
* @param bool $addErrors
186+
* @return bool|DateTime
187+
* @todo enhance the implosion
188+
*/
189+
protected function convertArray(array $value, $addErrors = true)
190+
{
191+
return $this->convertString(implode('-', $value), $addErrors);
155192
}
156193
}

src/DateStep.php

Lines changed: 33 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
class DateStep extends Date
2020
{
21-
const NOT_STEP = 'dateStepNotStep';
21+
const NOT_STEP = 'dateStepNotStep';
22+
23+
const FORMAT_DEFAULT = DateTime::ISO8601;
2224

2325
/**
2426
* @var array
@@ -42,13 +44,6 @@ class DateStep extends Date
4244
*/
4345
protected $step;
4446

45-
/**
46-
* Format to use for parsing date strings
47-
*
48-
* @var string
49-
*/
50-
protected $format = DateTime::ISO8601;
51-
5247
/**
5348
* Optional timezone to be used when the baseValue
5449
* and validation values do not contain timezone info
@@ -82,21 +77,11 @@ public function __construct($options = array())
8277
$options = $temp;
8378
}
8479

85-
if (isset($options['baseValue'])) {
86-
$this->setBaseValue($options['baseValue']);
80+
if (!isset($options['step'])) {
81+
$options['step'] = new DateInterval('P1D');
8782
}
88-
if (isset($options['step'])) {
89-
$this->setStep($options['step']);
90-
} else {
91-
$this->setStep(new DateInterval('P1D'));
92-
}
93-
if (array_key_exists('format', $options)) {
94-
$this->setFormat($options['format']);
95-
}
96-
if (isset($options['timezone'])) {
97-
$this->setTimezone($options['timezone']);
98-
} else {
99-
$this->setTimezone(new DateTimeZone(date_default_timezone_get()));
83+
if (!isset($options['timezone'])) {
84+
$options['timezone'] = new DateTimeZone(date_default_timezone_get());
10085
}
10186

10287
parent::__construct($options);
@@ -169,36 +154,33 @@ public function setTimezone(DateTimeZone $timezone)
169154
}
170155

171156
/**
172-
* Converts an int or string to a DateTime object
157+
* Supports formats with ISO week (W) definitions
173158
*
174-
* @param string|int|\DateTime $param
175-
* @return \DateTime
176-
* @throws Exception\InvalidArgumentException
159+
* @see Date::convertString()
177160
*/
178-
protected function convertToDateTime($param)
161+
protected function convertString($value, $addErrors = true)
179162
{
180-
$dateObj = $param;
181-
if (is_int($param)) {
182-
// Convert from timestamp
183-
$dateObj = date_create("@$param");
184-
} elseif (is_string($param)) {
185-
// Custom week format support
186-
if (strpos($this->getFormat(), 'Y-\WW') === 0
187-
&& preg_match('/^([0-9]{4})\-W([0-9]{2})/', $param, $matches)
188-
) {
189-
$dateObj = new DateTime();
190-
$dateObj->setISODate($matches[1], $matches[2]);
191-
} else {
192-
$dateObj = DateTime::createFromFormat(
193-
$this->getFormat(), $param, $this->getTimezone()
194-
);
195-
}
163+
// Custom week format support
164+
if (strpos($this->format, 'Y-\WW') === 0
165+
&& preg_match('/^([0-9]{4})\-W([0-9]{2})/', $value, $matches)
166+
) {
167+
$date = new DateTime;
168+
$date->setISODate($matches[1], $matches[2]);
169+
} else {
170+
$date = DateTime::createFromFormat($this->format, $value, $this->timezone);
196171
}
197-
if (!($dateObj instanceof DateTime)) {
198-
throw new Exception\InvalidArgumentException('Invalid date param given');
172+
173+
// Invalid dates can show up as warnings (ie. "2007-02-99")
174+
// and still return a DateTime object.
175+
$errors = DateTime::getLastErrors();
176+
if ($errors['warning_count'] > 0) {
177+
if ($addErrors) {
178+
$this->error(self::FALSE_FORMAT);
179+
}
180+
return false;
199181
}
200182

201-
return $dateObj;
183+
return $date;
202184
}
203185

204186
/**
@@ -210,20 +192,14 @@ protected function convertToDateTime($param)
210192
*/
211193
public function isValid($value)
212194
{
213-
parent::isValid($value);
214-
215-
$this->setValue($value);
216-
217-
$baseDate = $this->convertToDateTime($this->getBaseValue());
218-
$step = $this->getStep();
219-
220-
// Parse the date
221-
try {
222-
$valueDate = $this->convertToDateTime($value);
223-
} catch (Exception\InvalidArgumentException $ex) {
195+
if (!parent::isValid($value)) {
224196
return false;
225197
}
226198

199+
$valueDate = $this->convertToDateTime($value, false); // avoid duplicate errors
200+
$baseDate = $this->convertToDateTime($this->baseValue, false);
201+
$step = $this->getStep();
202+
227203
// Same date?
228204
if ($valueDate == $baseDate) {
229205
return true;

test/DateStepTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,6 @@ public function testStepError()
115115
'step' => new DateInterval("P10D"),
116116
));
117117

118-
$this->assertFalse($validator->isValid('2012-13-13'));
118+
$this->assertFalse($validator->isValid('2012-02-23'));
119119
}
120120
}

test/DateTest.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ public function setUp()
3333
$this->validator = new Validator\Date();
3434
}
3535

36+
public function testSetFormatIgnoresNull()
37+
{
38+
$this->validator->setFormat(null);
39+
$this->assertEquals(Validator\Date::FORMAT_DEFAULT, $this->validator->getFormat());
40+
}
41+
3642
public function datesDataProvider()
3743
{
3844
return array(
@@ -68,7 +74,9 @@ public function datesDataProvider()
6874
// array(999999999999, null, true),
6975
// array
7076
array(array('2012', '06', '25'), null, true),
71-
array(array('12', '06', '25'), null, false),
77+
// 0012-06-25 is a valid date, if you want 2012, use 'y' instead of 'Y'
78+
array(array('12', '06', '25'), null, true),
79+
array(array('2012', '06', '33'), null, false),
7280
array(array(1 => 1), null, false),
7381
// DateTime
7482
array(new DateTime(), null, true),
@@ -86,7 +94,6 @@ public function testBasic($input, $format, $result)
8694
{
8795
$this->validator->setFormat($format);
8896
$this->assertEquals($result, $this->validator->isValid($input));
89-
$this->assertEquals($format, $this->validator->getFormat());
9097
}
9198

9299
/**

0 commit comments

Comments
 (0)