Skip to content

Commit 0d7a0a4

Browse files
authored
ext/intl: Throw TypeError for non-stringable timezone objects (#21990)
1 parent 115eb0f commit 0d7a0a4

10 files changed

Lines changed: 61 additions & 12 deletions

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ PHP NEWS
6262
. Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message
6363
suggests missing constants). (DanielEScherzer)
6464
. Added grapheme_strrev (Yuya Hamada)
65+
. Passing a non-stringable object as a time zone to Intl time zone
66+
argument handling now raises TypeError instead of Error. (Weilin Du)
6567

6668
- JSON:
6769
. Enriched JSON last error / exception message with error location.

UPGRADING

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ PHP 8.6 UPGRADE NOTES
3434
array arguments types/values and raise a TypeError/ValueError
3535
accordingly.
3636

37+
- Intl:
38+
. Passing a non-stringable object as a time zone to Intl APIs that accept
39+
time zone objects or strings now raises a TypeError instead of an Error.
40+
3741
- PCNTL:
3842
. pcntl_alarm() now raises a ValueError if the seconds argument is
3943
lower than zero or greater than platform's UINT_MAX.

ext/intl/calendar/calendar_methods.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ U_CFUNC PHP_FUNCTION(intlcal_create_instance)
8585
Z_PARAM_STRING_OR_NULL(locale_str, locale_len)
8686
ZEND_PARSE_PARAMETERS_END();
8787

88-
TimeZone *timeZone = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr);
88+
TimeZone *timeZone = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr, 1);
8989
if (timeZone == nullptr) {
9090
RETURN_NULL();
9191
}
@@ -316,7 +316,7 @@ U_CFUNC PHP_FUNCTION(intlcal_set_time_zone)
316316
}
317317

318318
TimeZone *timeZone = timezone_process_timezone_argument(
319-
timezone_object, timezone_string, CALENDAR_ERROR_P(co));
319+
timezone_object, timezone_string, CALENDAR_ERROR_P(co), 2);
320320
if (timeZone == nullptr) {
321321
RETURN_FALSE;
322322
}
@@ -345,7 +345,7 @@ U_CFUNC PHP_METHOD(IntlCalendar, setTimeZone)
345345
}
346346

347347
TimeZone *timeZone = timezone_process_timezone_argument(
348-
timezone_object, timezone_string, CALENDAR_ERROR_P(co));
348+
timezone_object, timezone_string, CALENDAR_ERROR_P(co), 1);
349349
if (timeZone == nullptr) {
350350
RETURN_FALSE;
351351
}

ext/intl/calendar/gregoriancalendar_methods.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static void _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS, bool
143143

144144
if (variant <= 2) {
145145
// From timezone and locale (0 to 2 arguments)
146-
TimeZone *tz = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr);
146+
TimeZone *tz = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr, 1);
147147
if (tz == nullptr) {
148148
// TODO: Exception should always occur already?
149149
if (!EG(exception)) {

ext/intl/dateformat/dateformat_attrcpp.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ U_CFUNC PHP_FUNCTION(datefmt_set_timezone)
9494
DATE_FORMAT_METHOD_FETCH_OBJECT;
9595

9696
TimeZone *timezone = timezone_process_timezone_argument(
97-
timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo));
97+
timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 2);
9898
if (timezone == nullptr) {
9999
RETURN_FALSE;
100100
}
@@ -119,7 +119,7 @@ U_CFUNC PHP_METHOD(IntlDateFormatter, setTimeZone)
119119
DATE_FORMAT_METHOD_FETCH_OBJECT;
120120

121121
TimeZone *timezone = timezone_process_timezone_argument(
122-
timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo));
122+
timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 1);
123123
if (timezone == nullptr) {
124124
RETURN_FALSE;
125125
}

ext/intl/dateformat/dateformat_create.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
131131

132132
if (explicit_tz || calendar_owned ) {
133133
//we have an explicit time zone or a non-object calendar
134-
timezone = timezone_process_timezone_argument(timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo));
134+
timezone = timezone_process_timezone_argument(timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 4);
135135
if (timezone == nullptr) {
136136
goto error;
137137
}

ext/intl/msgformat/msgformat_helpers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ static void umsg_set_timezone(MessageFormatter_object *mfo,
341341
}
342342

343343
if (used_tz == NULL) {
344-
used_tz = timezone_process_timezone_argument(nullptr, nullptr, &err);
344+
used_tz = timezone_process_timezone_argument(nullptr, nullptr, &err, 1);
345345
if (used_tz == NULL) {
346346
continue;
347347
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Intl timezone argument APIs reject non-stringable objects with TypeError
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
8+
function dump_exception(callable $cb): void {
9+
try {
10+
$cb();
11+
} catch (Throwable $e) {
12+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
13+
}
14+
}
15+
16+
$std = new stdClass();
17+
$calendar = IntlCalendar::createInstance();
18+
$formatter = new IntlDateFormatter(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
19+
20+
dump_exception(fn() => intlcal_create_instance($std));
21+
dump_exception(fn() => IntlCalendar::createInstance($std));
22+
dump_exception(fn() => intlcal_set_time_zone($calendar, $std));
23+
dump_exception(fn() => $calendar->setTimeZone($std));
24+
dump_exception(fn() => new IntlGregorianCalendar($std));
25+
dump_exception(fn() => datefmt_create(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std));
26+
dump_exception(fn() => IntlDateFormatter::create(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std));
27+
dump_exception(fn() => new IntlDateFormatter(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std));
28+
dump_exception(fn() => datefmt_set_timezone($formatter, $std));
29+
dump_exception(fn() => $formatter->setTimeZone($std));
30+
31+
?>
32+
--EXPECT--
33+
TypeError: intlcal_create_instance(): Argument #1 ($timezone) Object of class stdClass could not be converted to string
34+
TypeError: IntlCalendar::createInstance(): Argument #1 ($timezone) Object of class stdClass could not be converted to string
35+
TypeError: intlcal_set_time_zone(): Argument #2 ($timezone) Object of class stdClass could not be converted to string
36+
TypeError: IntlCalendar::setTimeZone(): Argument #1 ($timezone) Object of class stdClass could not be converted to string
37+
TypeError: IntlGregorianCalendar::__construct(): Argument #1 ($timezoneOrYear) Object of class stdClass could not be converted to string
38+
TypeError: datefmt_create(): Argument #4 ($timezone) Object of class stdClass could not be converted to string
39+
TypeError: IntlDateFormatter::create(): Argument #4 ($timezone) Object of class stdClass could not be converted to string
40+
TypeError: IntlDateFormatter::__construct(): Argument #4 ($timezone) Object of class stdClass could not be converted to string
41+
TypeError: datefmt_set_timezone(): Argument #2 ($timezone) Object of class stdClass could not be converted to string
42+
TypeError: IntlDateFormatter::setTimeZone(): Argument #1 ($timezone) Object of class stdClass could not be converted to string

ext/intl/timezone/timezone_class.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ static void timezone_throw_exception_with_call_location(const char *msg, const c
121121
/* {{{ timezone_process_timezone_argument
122122
* TimeZone argument processor. outside_error may be nullptr (for static functions/constructors) */
123123
U_CFUNC TimeZone *timezone_process_timezone_argument(
124-
zend_object *timezone_object, zend_string *timezone_string, intl_error *outside_error)
124+
zend_object *timezone_object, zend_string *timezone_string, intl_error *outside_error, uint32_t arg_num)
125125
{
126126
std::unique_ptr<TimeZone> timeZone;
127127
bool free_string = false;
@@ -160,8 +160,9 @@ U_CFUNC TimeZone *timezone_process_timezone_argument(
160160
free_string = true;
161161
} else {
162162
if (!EG(exception)) {
163-
// TODO Proper type error
164-
zend_throw_error(nullptr, "Object of class %s could not be converted to string", ZSTR_VAL(timezone_object->ce->name));
163+
zend_argument_type_error(arg_num,
164+
"Object of class %s could not be converted to string",
165+
ZSTR_VAL(timezone_object->ce->name));
165166
}
166167
return nullptr;
167168
}

ext/intl/timezone/timezone_class.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ static inline TimeZone_object *php_intl_timezone_fetch_object(zend_object *obj)
6767
}
6868

6969
zval *timezone_convert_to_datetimezone(const TimeZone *timeZone, intl_error *outside_error, zval *ret);
70-
TimeZone *timezone_process_timezone_argument(zend_object *timezone_object, zend_string *timezone_string, intl_error *error);
70+
TimeZone *timezone_process_timezone_argument(zend_object *timezone_object, zend_string *timezone_string, intl_error *error, uint32_t arg_num);
7171

7272
void timezone_object_construct(const TimeZone *zone, zval *object, int owned);
7373

0 commit comments

Comments
 (0)