Commit 10704f079f6 for php.net
commit 10704f079f6087a51e3b579faca00d833141d889
Author: “LamentXU123” <108666168+LamentXU123@users.noreply.github.com>
Date: Fri May 15 16:18:51 2026 +0800
ext/intl: Fix out-of-bounds argument positions in calendar date/time APIs.
close GH-22044
diff --git a/NEWS b/NEWS
index 8dea2348ff3..e9392cadf7d 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,11 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.4.22
+- Intl:
+ . Fix incorrect argument positions in out-of-bounds errors for
+ IntlCalendar::set(), IntlCalendar::setDate(), IntlCalendar::setDateTime(),
+ and IntlGregorianCalendar date/time construction. (Weilin Du)
+
- MySQLnd:
. Fix persistent free of non-persistent connect_attr key (David Carlier).
diff --git a/UPGRADING b/UPGRADING
index a67fc1b1d88..466b445c2d1 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -82,6 +82,9 @@ PHP 8.4 UPGRADE NOTES
. bind_textdomain_codeset, textdomain and d(*)gettext functions now throw an exception
if the domain argument is empty.
. Intl:
+ . IntlCalendar::set(), IntlCalendar::setDate(), IntlCalendar::setDateTime(),
+ and IntlGregorianCalendar date/time construction now report the correct
+ argument position for out-of-bounds integer values.
. resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
ResourceBundle object now throw:
- TypeError for invalid offset types
diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp
index 9b7a37c0df5..61408375c86 100644
--- a/ext/intl/calendar/calendar_methods.cpp
+++ b/ext/intl/calendar/calendar_methods.cpp
@@ -391,8 +391,8 @@ U_CFUNC PHP_FUNCTION(intlcal_set)
}
for (int i = 0; i < arg_num; i++) {
- /* Arguments start at 1 */
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(args[i], i + 1);
+ /* Count from intlcal_set($calendar, ...), so date/time arguments start at #2. */
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(args[i], i + 2);
}
CALENDAR_METHOD_FETCH_OBJECT;
@@ -427,9 +427,10 @@ U_CFUNC PHP_METHOD(IntlCalendar, setDate)
RETURN_THROWS();
}
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
+ /* These method-only APIs parse the object first, so the API argument positions are offset by +1. */
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 2);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 3);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 4);
CALENDAR_METHOD_FETCH_OBJECT;
@@ -450,18 +451,19 @@ U_CFUNC PHP_METHOD(IntlCalendar, setDateTime)
RETURN_THROWS();
}
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 4);
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 5);
+ /* These method-only APIs parse the object first, so the API argument positions are offset by +1. */
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 2);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 3);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 4);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 5);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 6);
CALENDAR_METHOD_FETCH_OBJECT;
if (second_is_null) {
co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute);
} else {
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 6);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 7);
co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second);
}
}
diff --git a/ext/intl/calendar/gregoriancalendar_methods.cpp b/ext/intl/calendar/gregoriancalendar_methods.cpp
index d6b8f0602dd..0b36e621ef7 100644
--- a/ext/intl/calendar/gregoriancalendar_methods.cpp
+++ b/ext/intl/calendar/gregoriancalendar_methods.cpp
@@ -178,7 +178,7 @@ static void _php_intlgregcal_constructor_body(
} else {
// From date/time (3, 5 or 6 arguments)
for (int i = 0; i < variant; i++) {
- ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(largs[i], hasThis() ? (i-1) : i);
+ ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(largs[i], i + 1);
}
if (variant == 3) {
diff --git a/ext/intl/tests/calendar_set_date_out_of_bounds.phpt b/ext/intl/tests/calendar_set_date_out_of_bounds.phpt
new file mode 100644
index 00000000000..db9d18275ae
--- /dev/null
+++ b/ext/intl/tests/calendar_set_date_out_of_bounds.phpt
@@ -0,0 +1,32 @@
+--TEST--
+IntlCalendar::setDate(): out-of-bounds arguments report correct positions
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
+--FILE--
+<?php
+$cal = IntlCalendar::createInstance();
+
+try {
+ $cal->setDate(99999999999, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDate(1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDate(1, 1, 99999999999);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+IntlCalendar::setDate(): Argument #1 ($year) must be between -2147483648 and 2147483647
+IntlCalendar::setDate(): Argument #2 ($month) must be between -2147483648 and 2147483647
+IntlCalendar::setDate(): Argument #3 ($dayOfMonth) must be between -2147483648 and 2147483647
diff --git a/ext/intl/tests/calendar_set_date_time_out_of_bounds.phpt b/ext/intl/tests/calendar_set_date_time_out_of_bounds.phpt
new file mode 100644
index 00000000000..798ab1ebb01
--- /dev/null
+++ b/ext/intl/tests/calendar_set_date_time_out_of_bounds.phpt
@@ -0,0 +1,53 @@
+--TEST--
+IntlCalendar::setDateTime(): out-of-bounds arguments report correct positions
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
+--FILE--
+<?php
+$cal = IntlCalendar::createInstance();
+
+try {
+ $cal->setDateTime(99999999999, 1, 1, 1, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDateTime(1, 99999999999, 1, 1, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDateTime(1, 1, 99999999999, 1, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDateTime(1, 1, 1, 99999999999, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDateTime(1, 1, 1, 1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->setDateTime(1, 1, 1, 1, 1, 99999999999);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+IntlCalendar::setDateTime(): Argument #1 ($year) must be between -2147483648 and 2147483647
+IntlCalendar::setDateTime(): Argument #2 ($month) must be between -2147483648 and 2147483647
+IntlCalendar::setDateTime(): Argument #3 ($dayOfMonth) must be between -2147483648 and 2147483647
+IntlCalendar::setDateTime(): Argument #4 ($hour) must be between -2147483648 and 2147483647
+IntlCalendar::setDateTime(): Argument #5 ($minute) must be between -2147483648 and 2147483647
+IntlCalendar::setDateTime(): Argument #6 ($second) must be between -2147483648 and 2147483647
diff --git a/ext/intl/tests/calendar_set_out_of_bounds.phpt b/ext/intl/tests/calendar_set_out_of_bounds.phpt
new file mode 100644
index 00000000000..1ca407d3f71
--- /dev/null
+++ b/ext/intl/tests/calendar_set_out_of_bounds.phpt
@@ -0,0 +1,55 @@
+--TEST--
+IntlCalendar::set(): out-of-bounds date/time arguments report correct positions
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
+--FILE--
+<?php
+$cal = IntlCalendar::createInstance();
+
+try {
+ $cal->set(99999999999, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ intlcal_set($cal, 1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->set(1, 1, 1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ $cal->set(1, 1, 1, 1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ intlcal_set($cal, 1, 1, 1, 1, 1, 99999999999);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECTF--
+Deprecated: Calling IntlCalendar::set() with more than 2 arguments is deprecated, use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead in %s on line %d
+IntlCalendar::set(): Argument #1 ($year) must be between -2147483648 and 2147483647
+
+Deprecated: Function intlcal_set() is deprecated since 8.4, use IntlCalendar::set(), IntlCalendar::setDate(), or IntlCalendar::setDateTime() instead in %s on line %d
+intlcal_set(): Argument #3 ($month) must be between -2147483648 and 2147483647
+
+Deprecated: Calling IntlCalendar::set() with more than 2 arguments is deprecated, use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead in %s on line %d
+IntlCalendar::set(): Argument #4 ($hour) must be between -2147483648 and 2147483647
+
+Deprecated: Calling IntlCalendar::set() with more than 2 arguments is deprecated, use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead in %s on line %d
+IntlCalendar::set(): Argument #5 ($minute) must be between -2147483648 and 2147483647
+
+Deprecated: Function intlcal_set() is deprecated since 8.4, use IntlCalendar::set(), IntlCalendar::setDate(), or IntlCalendar::setDateTime() instead in %s on line %d
+intlcal_set(): Argument #7 ($second) must be between -2147483648 and 2147483647
diff --git a/ext/intl/tests/gregoriancalendar___construct_out_of_bounds.phpt b/ext/intl/tests/gregoriancalendar___construct_out_of_bounds.phpt
new file mode 100644
index 00000000000..5b084b25d86
--- /dev/null
+++ b/ext/intl/tests/gregoriancalendar___construct_out_of_bounds.phpt
@@ -0,0 +1,53 @@
+--TEST--
+IntlGregorianCalendar::__construct(): out-of-bounds date/time arguments report correct positions
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
+--FILE--
+<?php
+try {
+ new IntlGregorianCalendar(99999999999, 1, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ intlgregcal_create_instance(1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ new IntlGregorianCalendar(1, 1, 1, 99999999999, 1);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ new IntlGregorianCalendar(1, 1, 1, 1, 99999999999);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ intlgregcal_create_instance(1, 1, 1, 1, 1, 99999999999);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECTF--
+Deprecated: Calling IntlGregorianCalendar::__construct() with more than 2 arguments is deprecated, use either IntlGregorianCalendar::createFromDate() or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d
+IntlGregorianCalendar::__construct(): Argument #1 ($timezoneOrYear) must be between -2147483648 and 2147483647
+
+Deprecated: Function intlgregcal_create_instance() is deprecated since 8.4, use IntlGregorianCalendar::__construct(), IntlGregorianCalendar::createFromDate(), or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d
+intlgregcal_create_instance(): Argument #2 ($localeOrMonth) must be between -2147483648 and 2147483647
+
+Deprecated: Calling IntlGregorianCalendar::__construct() with more than 2 arguments is deprecated, use either IntlGregorianCalendar::createFromDate() or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d
+IntlGregorianCalendar::__construct(): Argument #4 ($hour) must be between -2147483648 and 2147483647
+
+Deprecated: Calling IntlGregorianCalendar::__construct() with more than 2 arguments is deprecated, use either IntlGregorianCalendar::createFromDate() or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d
+IntlGregorianCalendar::__construct(): Argument #5 ($minute) must be between -2147483648 and 2147483647
+
+Deprecated: Function intlgregcal_create_instance() is deprecated since 8.4, use IntlGregorianCalendar::__construct(), IntlGregorianCalendar::createFromDate(), or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d
+intlgregcal_create_instance(): Argument #6 ($second) must be between -2147483648 and 2147483647