diff --git a/NEWS b/NEWS index 821dc09ad3a1..65b1f85ba378 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ PHP NEWS initialization). (Arnaud) . Enabled the TAILCALL VM on Windows when compiling with Clang >= 19 x86_64. (henderkes) + . Deprecate specifying a nullable return type for __debugInfo(). (timwolla) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/UPGRADING b/UPGRADING index 37abc121a070..de086c600f56 100644 --- a/UPGRADING +++ b/UPGRADING @@ -189,6 +189,10 @@ PHP 8.6 UPGRADE NOTES 4. Deprecated Functionality ======================================== +- Core: + . Specifying a return type of array|null / ?array for __debugInfo() is now + deprecated. Specify array instead. + - GMP . The shift (<<, >>) and exponentiation (**) operators on GMP objects now emit a deprecation warning when converting a float right operand to int diff --git a/Zend/tests/debug_info/debug_info.phpt b/Zend/tests/debug_info/debug_info.phpt index bff5876777ba..e7bb4d48aec6 100644 --- a/Zend/tests/debug_info/debug_info.phpt +++ b/Zend/tests/debug_info/debug_info.phpt @@ -21,6 +21,12 @@ class Bar { } } +class Baz { + public function __debugInfo(): ?array { + return null; + } +} + $f = new Foo; var_dump($f); @@ -28,6 +34,7 @@ $b = new Bar; var_dump($b); ?> --EXPECTF-- +Deprecated: Returning null from Baz::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d object(Foo)#%d (3) { ["a"]=> int(1) diff --git a/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt index 6bdcafb1bfb5..a4076ee00ce2 100644 --- a/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt +++ b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt @@ -66,5 +66,6 @@ class WidenedArgumentType extends NarrowedReturnType { echo 'No problems!'; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Returning null from ValidMagicMethods::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d No problems! diff --git a/Zend/tests/return_types/042.phpt b/Zend/tests/return_types/042.phpt index 069e0228ff99..21483047d4f2 100644 --- a/Zend/tests/return_types/042.phpt +++ b/Zend/tests/return_types/042.phpt @@ -20,5 +20,10 @@ class JustAnArray { echo 'No problems!'; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Returning null from UnionType::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d + +Deprecated: Returning null from UnionType2::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d + +Deprecated: Returning null from UnionTypeOldStyle::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d No problems! diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 004b4fd81e98..65834adbafff 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2825,6 +2825,10 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, (MAY_BE_ARRAY | MAY_BE_NULL)); + if ((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && ZEND_TYPE_PURE_MASK(fptr->common.arg_info[-1].type) & MAY_BE_NULL) { + zend_error(E_DEPRECATED, "Returning null from %s::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead", + ZSTR_VAL(ce->name)); + } } else if (zend_string_equals_literal(lcname, "__serialize")) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index 08e446806930..8e85b695db9b 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -419,8 +419,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; @@ -455,9 +455,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; @@ -478,18 +479,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 f79c6bf14c8f..aeb728e29de1 100644 --- a/ext/intl/calendar/gregoriancalendar_methods.cpp +++ b/ext/intl/calendar/gregoriancalendar_methods.cpp @@ -176,7 +176,7 @@ static void _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS, bool // From date/time (3, 5 or 6 arguments) GregorianCalendar *tmp; 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 000000000000..db9d18275ae8 --- /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-- + +--FILE-- +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 000000000000..798ab1ebb01f --- /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-- + +--FILE-- +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 000000000000..1ca407d3f713 --- /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-- + +--FILE-- +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 000000000000..5b084b25d869 --- /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-- + +--FILE-- +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 diff --git a/ext/simplexml/simplexml.stub.php b/ext/simplexml/simplexml.stub.php index 935af16621e9..6faf873c885f 100644 --- a/ext/simplexml/simplexml.stub.php +++ b/ext/simplexml/simplexml.stub.php @@ -51,7 +51,7 @@ public function getName(): string {} public function __toString(): string {} - public function __debugInfo(): ?array {} + public function __debugInfo(): array {} /** @tentative-return-type */ public function count(): int {} diff --git a/ext/simplexml/simplexml_arginfo.h b/ext/simplexml/simplexml_arginfo.h index 419c7874fbc5..5a62efbc7be5 100644 --- a/ext/simplexml/simplexml_arginfo.h +++ b/ext/simplexml/simplexml_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit simplexml.stub.php instead. - * Stub hash: cee51320f0f09f14962fb72125ef8ff6073a642a */ + * Stub hash: 83f1bd204b309a4558c1b09128fe29f972f54252 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_simplexml_load_file, 0, 1, SimpleXMLElement, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -79,7 +79,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___debugInfo, 0, 0, IS_ARRAY, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___debugInfo, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement_count, 0, 0, IS_LONG, 0) diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 06c7e2c196d4..1c08aef0e469 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -829,16 +829,15 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) zval *myval; /* check if the current tag already has a value - if yes append to that! */ if ((myval = zend_hash_find(Z_ARRVAL_P(ctag), ZSTR_KNOWN(ZEND_STR_VALUE))) && Z_TYPE_P(myval) == IS_STRING) { - size_t newlen = Z_STRLEN_P(myval) + ZSTR_LEN(decoded_value); - Z_STR_P(myval) = zend_string_extend(Z_STR_P(myval), newlen, 0); + Z_STR_P(myval) = zend_string_safe_realloc(Z_STR_P(myval), 1, Z_STRLEN_P(myval), ZSTR_LEN(decoded_value), false); strncpy(Z_STRVAL_P(myval) + Z_STRLEN_P(myval) - ZSTR_LEN(decoded_value), ZSTR_VAL(decoded_value), ZSTR_LEN(decoded_value) + 1); - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); } else { if (doprint || (! parser->skipwhite)) { add_assoc_str(ctag, "value", decoded_value); } else { - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); } } } else { @@ -856,11 +855,10 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) if (EXPECTED(Z_TYPE_P(mytype) == IS_STRING) && zend_string_equals_literal(Z_STR_P(mytype), "cdata")) { SEPARATE_ARRAY(curtag); if ((myval = zend_hash_find(Z_ARRVAL_P(curtag), ZSTR_KNOWN(ZEND_STR_VALUE)))) { - size_t newlen = Z_STRLEN_P(myval) + ZSTR_LEN(decoded_value); - Z_STR_P(myval) = zend_string_extend(Z_STR_P(myval), newlen, 0); + Z_STR_P(myval) = zend_string_safe_realloc(Z_STR_P(myval), 1, Z_STRLEN_P(myval), ZSTR_LEN(decoded_value), false); strncpy(Z_STRVAL_P(myval) + Z_STRLEN_P(myval) - ZSTR_LEN(decoded_value), ZSTR_VAL(decoded_value), ZSTR_LEN(decoded_value) + 1); - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); return; } } @@ -879,7 +877,7 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) } else if (parser->level == (XML_MAXLEVEL + 1)) { php_error_docref(NULL, E_WARNING, "Maximum depth exceeded - Results truncated"); } else { - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); } } }