diff --git a/NEWS b/NEWS index 4ace7d3dc462..4f8fe29d53e7 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,8 @@ PHP NEWS . Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message suggests missing constants). (DanielEScherzer) . Added grapheme_strrev (Yuya Hamada) + . Passing a non-stringable object as a time zone to Intl time zone + argument handling now raises TypeError instead of Error. (Weilin Du) - JSON: . Enriched JSON last error / exception message with error location. diff --git a/UPGRADING b/UPGRADING index d271eb47f7d1..f4853af485fe 100644 --- a/UPGRADING +++ b/UPGRADING @@ -34,6 +34,10 @@ PHP 8.6 UPGRADE NOTES array arguments types/values and raise a TypeError/ValueError accordingly. +- Intl: + . Passing a non-stringable object as a time zone to Intl APIs that accept + time zone objects or strings now raises a TypeError instead of an Error. + - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is lower than zero or greater than platform's UINT_MAX. diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index 4828c6417300..08e446806930 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -85,7 +85,7 @@ U_CFUNC PHP_FUNCTION(intlcal_create_instance) Z_PARAM_STRING_OR_NULL(locale_str, locale_len) ZEND_PARSE_PARAMETERS_END(); - TimeZone *timeZone = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr); + TimeZone *timeZone = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr, 1); if (timeZone == nullptr) { RETURN_NULL(); } @@ -316,7 +316,7 @@ U_CFUNC PHP_FUNCTION(intlcal_set_time_zone) } TimeZone *timeZone = timezone_process_timezone_argument( - timezone_object, timezone_string, CALENDAR_ERROR_P(co)); + timezone_object, timezone_string, CALENDAR_ERROR_P(co), 2); if (timeZone == nullptr) { RETURN_FALSE; } @@ -345,7 +345,7 @@ U_CFUNC PHP_METHOD(IntlCalendar, setTimeZone) } TimeZone *timeZone = timezone_process_timezone_argument( - timezone_object, timezone_string, CALENDAR_ERROR_P(co)); + timezone_object, timezone_string, CALENDAR_ERROR_P(co), 1); if (timeZone == nullptr) { RETURN_FALSE; } diff --git a/ext/intl/calendar/gregoriancalendar_methods.cpp b/ext/intl/calendar/gregoriancalendar_methods.cpp index b94548b54a65..f79c6bf14c8f 100644 --- a/ext/intl/calendar/gregoriancalendar_methods.cpp +++ b/ext/intl/calendar/gregoriancalendar_methods.cpp @@ -143,7 +143,7 @@ static void _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS, bool if (variant <= 2) { // From timezone and locale (0 to 2 arguments) - TimeZone *tz = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr); + TimeZone *tz = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr, 1); if (tz == nullptr) { // TODO: Exception should always occur already? if (!EG(exception)) { diff --git a/ext/intl/dateformat/dateformat_attrcpp.cpp b/ext/intl/dateformat/dateformat_attrcpp.cpp index 1391823ff0fe..1c8fb88a3d51 100644 --- a/ext/intl/dateformat/dateformat_attrcpp.cpp +++ b/ext/intl/dateformat/dateformat_attrcpp.cpp @@ -94,7 +94,7 @@ U_CFUNC PHP_FUNCTION(datefmt_set_timezone) DATE_FORMAT_METHOD_FETCH_OBJECT; TimeZone *timezone = timezone_process_timezone_argument( - timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo)); + timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 2); if (timezone == nullptr) { RETURN_FALSE; } @@ -119,7 +119,7 @@ U_CFUNC PHP_METHOD(IntlDateFormatter, setTimeZone) DATE_FORMAT_METHOD_FETCH_OBJECT; TimeZone *timezone = timezone_process_timezone_argument( - timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo)); + timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 1); if (timezone == nullptr) { RETURN_FALSE; } diff --git a/ext/intl/dateformat/dateformat_create.cpp b/ext/intl/dateformat/dateformat_create.cpp index 4b055fc88eba..574e00099d43 100644 --- a/ext/intl/dateformat/dateformat_create.cpp +++ b/ext/intl/dateformat/dateformat_create.cpp @@ -131,7 +131,7 @@ static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS) if (explicit_tz || calendar_owned ) { //we have an explicit time zone or a non-object calendar - timezone = timezone_process_timezone_argument(timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo)); + timezone = timezone_process_timezone_argument(timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 4); if (timezone == nullptr) { goto error; } diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index 399db1e8c0b0..f504ee50abcb 100644 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -341,7 +341,7 @@ static void umsg_set_timezone(MessageFormatter_object *mfo, } if (used_tz == NULL) { - used_tz = timezone_process_timezone_argument(nullptr, nullptr, &err); + used_tz = timezone_process_timezone_argument(nullptr, nullptr, &err, 1); if (used_tz == NULL) { continue; } diff --git a/ext/intl/tests/timezone_argument_type_errors.phpt b/ext/intl/tests/timezone_argument_type_errors.phpt new file mode 100644 index 000000000000..02e1c58492c9 --- /dev/null +++ b/ext/intl/tests/timezone_argument_type_errors.phpt @@ -0,0 +1,42 @@ +--TEST-- +Intl timezone argument APIs reject non-stringable objects with TypeError +--EXTENSIONS-- +intl +--FILE-- +getMessage(), PHP_EOL; + } +} + +$std = new stdClass(); +$calendar = IntlCalendar::createInstance(); +$formatter = new IntlDateFormatter(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + +dump_exception(fn() => intlcal_create_instance($std)); +dump_exception(fn() => IntlCalendar::createInstance($std)); +dump_exception(fn() => intlcal_set_time_zone($calendar, $std)); +dump_exception(fn() => $calendar->setTimeZone($std)); +dump_exception(fn() => new IntlGregorianCalendar($std)); +dump_exception(fn() => datefmt_create(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std)); +dump_exception(fn() => IntlDateFormatter::create(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std)); +dump_exception(fn() => new IntlDateFormatter(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std)); +dump_exception(fn() => datefmt_set_timezone($formatter, $std)); +dump_exception(fn() => $formatter->setTimeZone($std)); + +?> +--EXPECT-- +TypeError: intlcal_create_instance(): Argument #1 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlCalendar::createInstance(): Argument #1 ($timezone) Object of class stdClass could not be converted to string +TypeError: intlcal_set_time_zone(): Argument #2 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlCalendar::setTimeZone(): Argument #1 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlGregorianCalendar::__construct(): Argument #1 ($timezoneOrYear) Object of class stdClass could not be converted to string +TypeError: datefmt_create(): Argument #4 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlDateFormatter::create(): Argument #4 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlDateFormatter::__construct(): Argument #4 ($timezone) Object of class stdClass could not be converted to string +TypeError: datefmt_set_timezone(): Argument #2 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlDateFormatter::setTimeZone(): Argument #1 ($timezone) Object of class stdClass could not be converted to string diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp index 8aad4fb2b6bd..0d29a47c186d 100644 --- a/ext/intl/timezone/timezone_class.cpp +++ b/ext/intl/timezone/timezone_class.cpp @@ -121,7 +121,7 @@ static void timezone_throw_exception_with_call_location(const char *msg, const c /* {{{ timezone_process_timezone_argument * TimeZone argument processor. outside_error may be nullptr (for static functions/constructors) */ U_CFUNC TimeZone *timezone_process_timezone_argument( - zend_object *timezone_object, zend_string *timezone_string, intl_error *outside_error) + zend_object *timezone_object, zend_string *timezone_string, intl_error *outside_error, uint32_t arg_num) { std::unique_ptr timeZone; bool free_string = false; @@ -160,8 +160,9 @@ U_CFUNC TimeZone *timezone_process_timezone_argument( free_string = true; } else { if (!EG(exception)) { - // TODO Proper type error - zend_throw_error(nullptr, "Object of class %s could not be converted to string", ZSTR_VAL(timezone_object->ce->name)); + zend_argument_type_error(arg_num, + "Object of class %s could not be converted to string", + ZSTR_VAL(timezone_object->ce->name)); } return nullptr; } diff --git a/ext/intl/timezone/timezone_class.h b/ext/intl/timezone/timezone_class.h index b383db558fcb..70b3b5637f2e 100644 --- a/ext/intl/timezone/timezone_class.h +++ b/ext/intl/timezone/timezone_class.h @@ -67,7 +67,7 @@ static inline TimeZone_object *php_intl_timezone_fetch_object(zend_object *obj) } zval *timezone_convert_to_datetimezone(const TimeZone *timeZone, intl_error *outside_error, zval *ret); -TimeZone *timezone_process_timezone_argument(zend_object *timezone_object, zend_string *timezone_string, intl_error *error); +TimeZone *timezone_process_timezone_argument(zend_object *timezone_object, zend_string *timezone_string, intl_error *error, uint32_t arg_num); void timezone_object_construct(const TimeZone *zone, zval *object, int owned);