Skip to content

Commit 95f4ee3

Browse files
committed
Add some ValueErrors to ext/date
Closes GH-5613
1 parent f888674 commit 95f4ee3

16 files changed

+259
-95
lines changed

ext/date/php_date.c

+44-26
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ PHPAPI timelib_tzinfo *get_timezone_info(void)
550550
tz = guess_timezone(DATE_TIMEZONEDB);
551551
tzi = php_date_parse_tzfile(tz, DATE_TIMEZONEDB);
552552
if (! tzi) {
553-
php_error_docref(NULL, E_ERROR, "Timezone database is corrupt - this should *never* happen!");
553+
zend_throw_error(NULL, "Timezone database is corrupt. Please file a bug report as this should never happen");
554554
}
555555
return tzi;
556556
}
@@ -961,7 +961,7 @@ PHP_FUNCTION(idate)
961961

962962
ret = php_idate(ZSTR_VAL(format)[0], ts, 0);
963963
if (ret == -1) {
964-
php_error_docref(NULL, E_WARNING, "Unrecognized date format token.");
964+
php_error_docref(NULL, E_WARNING, "Unrecognized date format token");
965965
RETURN_FALSE;
966966
}
967967
RETURN_LONG(ret);
@@ -1009,7 +1009,7 @@ PHPAPI zend_long php_parse_date(const char *string, zend_long *now)
10091009
PHP_FUNCTION(strtotime)
10101010
{
10111011
zend_string *times;
1012-
int error1, error2;
1012+
int parse_error, epoch_does_not_fit_in_zend_long;
10131013
timelib_error_container *error;
10141014
zend_long preset_ts, ts;
10151015
zend_bool preset_ts_is_null = 1;
@@ -1022,6 +1022,11 @@ PHP_FUNCTION(strtotime)
10221022
Z_PARAM_LONG_OR_NULL(preset_ts, preset_ts_is_null)
10231023
ZEND_PARSE_PARAMETERS_END();
10241024

1025+
/* timelib_strtotime() expects the string to not be empty */
1026+
if (ZSTR_LEN(times) == 0) {
1027+
RETURN_FALSE;
1028+
}
1029+
10251030
tzi = get_timezone_info();
10261031

10271032
now = timelib_time_ctor();
@@ -1032,20 +1037,27 @@ PHP_FUNCTION(strtotime)
10321037

10331038
t = timelib_strtotime(ZSTR_VAL(times), ZSTR_LEN(times), &error,
10341039
DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1035-
error1 = error->error_count;
1040+
parse_error = error->error_count;
10361041
timelib_error_container_dtor(error);
1042+
if (parse_error) {
1043+
timelib_time_dtor(now);
1044+
timelib_time_dtor(t);
1045+
RETURN_FALSE;
1046+
}
1047+
10371048
timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
10381049
timelib_update_ts(t, tzi);
1039-
ts = timelib_date_to_int(t, &error2);
1050+
ts = timelib_date_to_int(t, &epoch_does_not_fit_in_zend_long);
10401051

10411052
timelib_time_dtor(now);
10421053
timelib_time_dtor(t);
10431054

1044-
if (error1 || error2) {
1055+
if (epoch_does_not_fit_in_zend_long) {
1056+
php_error_docref(NULL, E_WARNING, "Epoch doesn't fit in a PHP integer");
10451057
RETURN_FALSE;
1046-
} else {
1047-
RETURN_LONG(ts);
10481058
}
1059+
1060+
RETURN_LONG(ts);
10491061
}
10501062
/* }}} */
10511063

@@ -1057,7 +1069,7 @@ PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
10571069
timelib_time *now;
10581070
timelib_tzinfo *tzi = NULL;
10591071
zend_long ts, adjust_seconds = 0;
1060-
int error;
1072+
int epoch_does_not_fit_in_zend_long;
10611073

10621074
ZEND_PARSE_PARAMETERS_START(1, 6)
10631075
Z_PARAM_LONG(hou)
@@ -1115,15 +1127,18 @@ PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
11151127
}
11161128

11171129
/* Clean up and return */
1118-
ts = timelib_date_to_int(now, &error);
1119-
ts += adjust_seconds;
1120-
timelib_time_dtor(now);
1130+
ts = timelib_date_to_int(now, &epoch_does_not_fit_in_zend_long);
11211131

1122-
if (error) {
1132+
if (epoch_does_not_fit_in_zend_long) {
1133+
timelib_time_dtor(now);
1134+
php_error_docref(NULL, E_WARNING, "Epoch doesn't fit in a PHP integer");
11231135
RETURN_FALSE;
1124-
} else {
1125-
RETURN_LONG(ts);
11261136
}
1137+
1138+
ts += adjust_seconds;
1139+
timelib_time_dtor(now);
1140+
1141+
RETURN_LONG(ts);
11271142
}
11281143
/* }}} */
11291144

@@ -2781,7 +2796,7 @@ static int php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{
27812796
dateobj = Z_PHPDATE_P(object);
27822797

27832798
if (!(dateobj->time)) {
2784-
php_error_docref(NULL, E_WARNING, "The DateTime object has not been correctly initialized by its constructor");
2799+
zend_throw_error(NULL, "The DateTime object has not been correctly initialized by its constructor");
27852800
return 0;
27862801
}
27872802

@@ -3309,7 +3324,7 @@ PHP_FUNCTION(date_timestamp_get)
33093324
zval *object;
33103325
php_date_obj *dateobj;
33113326
zend_long timestamp;
3312-
int error;
3327+
int epoch_does_not_fit_in_zend_long;
33133328

33143329
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
33153330
RETURN_THROWS();
@@ -3318,12 +3333,14 @@ PHP_FUNCTION(date_timestamp_get)
33183333
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
33193334
timelib_update_ts(dateobj->time, NULL);
33203335

3321-
timestamp = timelib_date_to_int(dateobj->time, &error);
3322-
if (error) {
3323-
RETURN_FALSE;
3324-
} else {
3325-
RETVAL_LONG(timestamp);
3336+
timestamp = timelib_date_to_int(dateobj->time, &epoch_does_not_fit_in_zend_long);
3337+
3338+
if (epoch_does_not_fit_in_zend_long) {
3339+
zend_value_error("Epoch doesn't fit in a PHP integer");
3340+
RETURN_THROWS();
33263341
}
3342+
3343+
RETURN_LONG(timestamp);
33273344
}
33283345
/* }}} */
33293346

@@ -3387,7 +3404,7 @@ PHP_FUNCTION(timezone_open)
33873404
php_timezone_obj *tzobj;
33883405

33893406
ZEND_PARSE_PARAMETERS_START(1, 1)
3390-
Z_PARAM_STR(tz)
3407+
Z_PARAM_PATH_STR(tz) /* To prevent null bytes */
33913408
ZEND_PARSE_PARAMETERS_END();
33923409

33933410
tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, return_value));
@@ -3406,7 +3423,7 @@ PHP_METHOD(DateTimeZone, __construct)
34063423
zend_error_handling error_handling;
34073424

34083425
ZEND_PARSE_PARAMETERS_START(1, 1)
3409-
Z_PARAM_STR(tz)
3426+
Z_PARAM_PATH_STR(tz) /* To prevent null bytes */
34103427
ZEND_PARSE_PARAMETERS_END();
34113428

34123429
zend_replace_error_handling(EH_THROW, NULL, &error_handling);
@@ -4336,8 +4353,9 @@ PHP_FUNCTION(timezone_identifiers_list)
43364353

43374354
/* Extra validation */
43384355
if (what == PHP_DATE_TIMEZONE_PER_COUNTRY && option_len != 2) {
4339-
php_error_docref(NULL, E_NOTICE, "A two-letter ISO 3166-1 compatible country code is expected");
4340-
RETURN_FALSE;
4356+
zend_argument_value_error(2, "must be a two-letter ISO 3166-1 compatible country code "
4357+
"when argument #1 ($timezoneGroup) is DateTimeZone::PER_COUNTRY");
4358+
RETURN_THROWS();
43414359
}
43424360

43434361
tzdb = DATE_TIMEZONEDB;

ext/date/php_date.stub.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function date_isodate_set(DateTime $object, int $year, int $week, int $day = 1):
7373

7474
function date_timestamp_set(DateTime $object, int $timestamp): DateTime {}
7575

76-
function date_timestamp_get(DateTimeInterface $object): int|false {}
76+
function date_timestamp_get(DateTimeInterface $object): int {}
7777

7878
function timezone_open(string $timezone): DateTimeZone|false {}
7979

@@ -88,7 +88,7 @@ function timezone_transitions_get(
8888

8989
function timezone_location_get(DateTimeZone $object): array|false {}
9090

91-
function timezone_identifiers_list(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array|false {}
91+
function timezone_identifiers_list(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array {}
9292

9393
function timezone_abbreviations_list(): array {}
9494

@@ -230,7 +230,7 @@ public function setISODate(int $year, int $week, int $dayOfWeek = 1) {}
230230
public function setTimestamp(int $timestamp) {}
231231

232232
/**
233-
* @return int|false
233+
* @return int
234234
* @alias date_timestamp_get
235235
*/
236236
public function getTimestamp() {}
@@ -282,7 +282,7 @@ public function getTimezone() {}
282282
public function getOffset() {}
283283

284284
/**
285-
* @return int|false
285+
* @return int
286286
* @alias date_timestamp_get
287287
*/
288288
public function getTimestamp() {}

ext/date/php_date_arginfo.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: fee95924adec03c89fdd677ec26bb6eea34d4b3c */
2+
* Stub hash: cb1532309655d85eb2644cdcfbf23063dfa1ddaf */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
55
ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
@@ -151,9 +151,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_timestamp_set, 0, 2, DateTim
151151
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
152152
ZEND_END_ARG_INFO()
153153

154-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_date_timestamp_get, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
155-
ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0)
156-
ZEND_END_ARG_INFO()
154+
#define arginfo_date_timestamp_get arginfo_date_offset_get
157155

158156
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_timezone_open, 0, 1, DateTimeZone, MAY_BE_FALSE)
159157
ZEND_ARG_TYPE_INFO(0, timezone, IS_STRING, 0)
@@ -184,7 +182,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timezone_location_get, 0, 1, MAY
184182
ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0)
185183
ZEND_END_ARG_INFO()
186184

187-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timezone_identifiers_list, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
185+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timezone_identifiers_list, 0, 0, IS_ARRAY, 0)
188186
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timezoneGroup, IS_LONG, 0, "DateTimeZone::ALL")
189187
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, countryCode, IS_STRING, 1, "null")
190188
ZEND_END_ARG_INFO()

ext/date/tests/005.phpt

+4-5
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,26 @@ $t = mktime(0,0,0, 6, 27, 2006);
99
var_dump(idate(1,1));
1010
var_dump(idate(""));
1111
var_dump(idate(0));
12-
1312
var_dump(idate("B", $t));
1413
var_dump(idate("[", $t));
1514
var_dump(idate("'"));
1615

1716
echo "Done\n";
1817
?>
1918
--EXPECTF--
20-
Warning: idate(): Unrecognized date format token. in %s on line %d
19+
Warning: idate(): Unrecognized date format token in %s on line %d
2120
bool(false)
2221

2322
Warning: idate(): idate format is one char in %s on line %d
2423
bool(false)
2524

26-
Warning: idate(): Unrecognized date format token. in %s on line %d
25+
Warning: idate(): Unrecognized date format token in %s on line %d
2726
bool(false)
2827
int(41)
2928

30-
Warning: idate(): Unrecognized date format token. in %s on line %d
29+
Warning: idate(): Unrecognized date format token in %s on line %d
3130
bool(false)
3231

33-
Warning: idate(): Unrecognized date format token. in %s on line %d
32+
Warning: idate(): Unrecognized date format token in %s on line %d
3433
bool(false)
3534
Done

ext/date/tests/bug41523.phpt

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var_dump( $dt = new DateTime('0000-00-00 00:00:00') );
1212
echo $dt->format( DateTime::ISO8601 ), "\n";
1313

1414
?>
15-
--EXPECT--
15+
--EXPECTF--
1616
array(12) {
1717
["year"]=>
1818
int(0)
@@ -43,6 +43,8 @@ array(12) {
4343
["is_localtime"]=>
4444
bool(false)
4545
}
46+
47+
Warning: strtotime(): Epoch doesn't fit in a PHP integer in %s on line %d
4648
bool(false)
4749
object(DateTime)#1 (3) {
4850
["date"]=>

ext/date/tests/bug52062.phpt

+12-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ date.timezone=UTC
1010
<?php
1111
$d = new DateTime('@100000000000');
1212
var_dump($d->format('Y-m-d H:i:s U'));
13-
var_dump($d->getTimestamp());
13+
try {
14+
var_dump($d->getTimestamp());
15+
} catch (\ValueError $e) {
16+
echo $e->getMessage() . \PHP_EOL;
17+
}
1418
var_dump($d->format('U'));
1519

1620
try {
@@ -19,16 +23,20 @@ try {
1923
echo $e->getMessage(), "\n";
2024
}
2125
var_dump($d->format('Y-m-d H:i:s U'));
22-
var_dump($d->getTimestamp());
26+
try {
27+
var_dump($d->getTimestamp());
28+
} catch (\ValueError $e) {
29+
echo $e->getMessage() . \PHP_EOL;
30+
}
2331

2432
$i = new DateInterval('PT100000000000S');
2533
var_dump($i->format('%s'));
2634
?>
2735
--EXPECT--
2836
string(32) "5138-11-16 09:46:40 100000000000"
29-
bool(false)
37+
Epoch doesn't fit in a PHP integer
3038
string(12) "100000000000"
3139
DateTime::setTimestamp(): Argument #1 ($timestamp) must be of type int, float given
3240
string(32) "5138-11-16 09:46:40 100000000000"
33-
bool(false)
41+
Epoch doesn't fit in a PHP integer
3442
string(10) "1215752192"

ext/date/tests/bug70277.phpt

+13-11
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ Bug #70277 (new DateTimeZone($foo) is ignoring text after null byte)
33
--FILE--
44
<?php
55
$timezone = "Europe/Zurich\0Foo";
6-
var_dump(timezone_open($timezone));
7-
var_dump(new DateTimeZone($timezone));
6+
try {
7+
var_dump(timezone_open($timezone));
8+
} catch (\ValueError $e) {
9+
echo $e->getMessage() . \PHP_EOL;
10+
}
11+
try {
12+
var_dump(new DateTimeZone($timezone));
13+
} catch (\ValueError $e) {
14+
echo $e->getMessage() . \PHP_EOL;
15+
}
816
?>
9-
--EXPECTF--
10-
Warning: timezone_open(): Timezone must not contain null bytes in %sbug70277.php on line %d
11-
bool(false)
12-
13-
Fatal error: Uncaught Exception: DateTimeZone::__construct(): Timezone must not contain null bytes in %sbug70277.php:%d
14-
Stack trace:
15-
#0 %sbug70277.php(%d): DateTimeZone->__construct('Europe/Zurich\x00F...')
16-
#1 {main}
17-
thrown in %sbug70277.php on line %d
17+
--EXPECT--
18+
timezone_open(): Argument #1 ($timezone) must not contain any null bytes
19+
DateTimeZone::__construct(): Argument #1 ($timezone) must not contain any null bytes

0 commit comments

Comments
 (0)