diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index 3542458f762c6..4ca4dc3810ae0 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -844,7 +844,7 @@ ZEND_API uint32_t zend_get_func_info( #endif ret = zend_get_return_info_from_signature_only( - callee_func, /* script */ NULL, ce, ce_is_instanceof); + callee_func, /* script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype); #if ZEND_DEBUG if (internal_ret) { @@ -884,7 +884,7 @@ ZEND_API uint32_t zend_get_func_info( } if (!ret) { ret = zend_get_return_info_from_signature_only( - callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof); + callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype); } } return ret; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 2326b8a21f03d..c582a8763ad98 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -4001,9 +4001,11 @@ static int is_recursive_tail_call(const zend_op_array *op_array, uint32_t zend_get_return_info_from_signature_only( const zend_function *func, const zend_script *script, - zend_class_entry **ce, bool *ce_is_instanceof) { + zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info) { uint32_t type; - if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && + (use_tentative_return_info || !ZEND_ARG_TYPE_IS_TENTATIVE(func->common.arg_info - 1)) + ) { zend_arg_info *ret_info = func->common.arg_info - 1; type = zend_fetch_arg_info_type(script, ret_info, ce); *ce_is_instanceof = ce != NULL; @@ -4025,15 +4027,15 @@ uint32_t zend_get_return_info_from_signature_only( ZEND_API void zend_init_func_return_info( const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret) { - if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_ssa_range tmp_range = {0, 0, 0, 0}; - bool is_instanceof = false; - ret->type = zend_get_return_info_from_signature_only( - (zend_function *) op_array, script, &ret->ce, &is_instanceof); - ret->is_instanceof = is_instanceof; - ret->range = tmp_range; - ret->has_range = 0; - } + ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)); + + zend_ssa_range tmp_range = {0, 0, 0, 0}; + bool is_instanceof = false; + ret->type = zend_get_return_info_from_signature_only( + (zend_function *) op_array, script, &ret->ce, &is_instanceof, /* use_tentative_return_info */ 1); + ret->is_instanceof = is_instanceof; + ret->range = tmp_range; + ret->has_range = 0; } void zend_func_return_info(const zend_op_array *op_array, diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index 861262968111b..f9dbb405cc23c 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -271,7 +271,7 @@ ZEND_API void zend_init_func_return_info( const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret); uint32_t zend_get_return_info_from_signature_only( const zend_function *func, const zend_script *script, - zend_class_entry **ce, bool *ce_is_instanceof); + zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info); void zend_func_return_info(const zend_op_array *op_array, const zend_script *script, int recursive, diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt index 69e7607bb350c..fc354a3855715 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt @@ -4,10 +4,10 @@ The default value is a class constant in the parent class method's signature. --EXPECTF-- -Fatal error: Declaration of MyDateTimeZone::listIdentifiers() must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) in %s on line %d +Fatal error: Declaration of MyDateTimeZone::listIdentifiers(): array must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt index 9335abd5d6a91..5b0a9bf05f0f0 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt @@ -4,10 +4,10 @@ The default value is a constant in the parent class method's signature. --EXPECTF-- -Fatal error: Declaration of MyDateTimeZone::getTransitions() must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX) in %s on line %d +Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false in %s on line %d diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt index 80c98a405253a..c01d7d256a66d 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt @@ -5,8 +5,8 @@ The default value is false in the parent class method's signature. interface MyDateTimeInterface extends DateTimeInterface { - public function diff(); + public function diff(): DateInterval; } ?> --EXPECTF-- -Fatal error: Declaration of MyDateTimeInterface::diff() must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false) in %s on line %d +Fatal error: Declaration of MyDateTimeInterface::diff(): DateInterval must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval in %s on line %d diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt index c32cc9e41fbc1..edf71c0263fea 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt @@ -4,10 +4,10 @@ The default value is an integer in the parent class method's signature. --EXPECTF-- -Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false) must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) in %s on line %d +Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime in %s on line %d diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt index 3804c2a6e1557..ebe5e142155ac 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt @@ -4,10 +4,10 @@ The default value is null in the parent class method's signature. --EXPECTF-- -Fatal error: Declaration of MyDateTime::createFromFormat() must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) in %s on line %d +Fatal error: Declaration of MyDateTime::createFromFormat(): DateTime|false must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false in %s on line %d diff --git a/Zend/tests/type_declarations/variance/internal_parent.phpt b/Zend/tests/type_declarations/variance/internal_parent.phpt deleted file mode 100644 index c82e10ffc34d5..0000000000000 --- a/Zend/tests/type_declarations/variance/internal_parent.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -Internal class as parent ---FILE-- - ---EXPECTF-- -Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, ?Wrong $timezone = null) and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null), because class Wrong is not available in %s on line %d diff --git a/Zend/tests/type_declarations/variance/internal_parent/compatible_return_type.phpt b/Zend/tests/type_declarations/variance/internal_parent/compatible_return_type.phpt new file mode 100644 index 0000000000000..a45e3f4eb942f --- /dev/null +++ b/Zend/tests/type_declarations/variance/internal_parent/compatible_return_type.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that no notice is emitted when the return type/value of the overriding method is compatible with the tentative return type/value of the overridden method +--FILE-- + +--EXPECT-- +array(0) { +} diff --git a/Zend/tests/type_declarations/variance/internal_parent/incompatible_return_type.phpt b/Zend/tests/type_declarations/variance/internal_parent/incompatible_return_type.phpt new file mode 100644 index 0000000000000..8c40a46a86608 --- /dev/null +++ b/Zend/tests/type_declarations/variance/internal_parent/incompatible_return_type.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a notice is emitted when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method +--FILE-- + +--EXPECTF-- +Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d +string(0) "" diff --git a/Zend/tests/type_declarations/variance/internal_parent/missing_return_type.phpt b/Zend/tests/type_declarations/variance/internal_parent/missing_return_type.phpt new file mode 100644 index 0000000000000..c20255df15c56 --- /dev/null +++ b/Zend/tests/type_declarations/variance/internal_parent/missing_return_type.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test that a notice is emitted when the tentative return type of the overridden method is omitted +--FILE-- + +--EXPECTF-- +Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d diff --git a/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_param.phpt b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_param.phpt new file mode 100644 index 0000000000000..d1a1ad78ce30a --- /dev/null +++ b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_param.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test unresolvable inheritance check due to unavailable parameter type when the parent has a tentative return type. +--FILE-- + +--EXPECTF-- +Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, ?Wrong $timezone = null): DateTime|false and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d diff --git a/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_return.phpt b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_return.phpt new file mode 100644 index 0000000000000..5cc1730741920 --- /dev/null +++ b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_return.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test unresolvable inheritance check due to unavailable return type when the parent has a tentative return type. +--FILE-- + +--EXPECTF-- +Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, $timezone = null): Wrong and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d diff --git a/Zend/tests/type_declarations/variance/return_type_will_change_class_error.phpt b/Zend/tests/type_declarations/variance/return_type_will_change_class_error.phpt new file mode 100644 index 0000000000000..f2698bb7a9563 --- /dev/null +++ b/Zend/tests/type_declarations/variance/return_type_will_change_class_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test that the ReturnTypeWillChange attribute cannot target classes +--FILE-- + +--EXPECTF-- +Fatal error: Attribute "ReturnTypeWillChange" cannot target class (allowed targets: method) in %s on line %d diff --git a/Zend/tests/type_declarations/variance/return_type_will_change_function_error.phpt b/Zend/tests/type_declarations/variance/return_type_will_change_function_error.phpt new file mode 100644 index 0000000000000..e539275c3e862 --- /dev/null +++ b/Zend/tests/type_declarations/variance/return_type_will_change_function_error.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test that the ReturnTypeWillChange attribute cannot target functions +--FILE-- + +--EXPECTF-- +Fatal error: Attribute "ReturnTypeWillChange" cannot target function (allowed targets: method) in %s on line %d diff --git a/Zend/tests/type_declarations/variance/return_type_will_change_property_error.phpt b/Zend/tests/type_declarations/variance/return_type_will_change_property_error.phpt new file mode 100644 index 0000000000000..ebb2b49c2f8c3 --- /dev/null +++ b/Zend/tests/type_declarations/variance/return_type_will_change_property_error.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test that the ReturnTypeWillChange attribute cannot be used with functions +--FILE-- + +--EXPECTF-- +Fatal error: Attribute "ReturnTypeWillChange" cannot target property (allowed targets: method) in %s on line %d diff --git a/Zend/tests/type_declarations/variance/suppressed_incompatible_return_type.phpt b/Zend/tests/type_declarations/variance/suppressed_incompatible_return_type.phpt new file mode 100644 index 0000000000000..5da5f8d513032 --- /dev/null +++ b/Zend/tests/type_declarations/variance/suppressed_incompatible_return_type.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method +--FILE-- +modify("+1 sec")); +?> +--EXPECT-- +bool(false) diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 50764faeca367..133791f300ecd 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -107,66 +107,91 @@ typedef struct _zend_fcall_info_cache { #define ZEND_FE_END { NULL, NULL, NULL, 0, 0 } -#define _ZEND_ARG_INFO_FLAGS(pass_by_ref, is_variadic) \ - (((pass_by_ref) << _ZEND_SEND_MODE_SHIFT) | ((is_variadic) ? _ZEND_IS_VARIADIC_BIT : 0)) +#define _ZEND_ARG_INFO_FLAGS(pass_by_ref, is_variadic, is_tentative) \ + (((pass_by_ref) << _ZEND_SEND_MODE_SHIFT) | ((is_variadic) ? _ZEND_IS_VARIADIC_BIT : 0) | ((is_tentative) ? _ZEND_IS_TENTATIVE_BIT : 0)) /* Arginfo structures without type information */ #define ZEND_ARG_INFO(pass_by_ref, name) \ - { #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL }, + { #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL }, #define ZEND_ARG_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, default_value) \ - { #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value }, + { #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value }, #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) \ - { #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL }, + { #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), NULL }, /* Arginfo structures with simple type information */ #define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \ - { #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL }, + { #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL }, #define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \ - { #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value }, + { #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value }, #define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \ - { #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL }, + { #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), NULL }, /* Arginfo structures with complex type information */ #define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask, default_value) \ - { #name, ZEND_TYPE_INIT_MASK(type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value }, + { #name, ZEND_TYPE_INIT_MASK(type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value }, #define ZEND_ARG_OBJ_TYPE_MASK(pass_by_ref, name, class_name, type_mask, default_value) \ - { #name, ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value }, + { #name, ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value }, /* Arginfo structures with object type information */ #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) \ - { #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL }, + { #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL }, #define ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, classname, allow_null, default_value) \ - { #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value }, + { #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value }, #define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) \ - { #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL }, + { #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), NULL }, /* Legacy arginfo structures */ #define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) \ - { #name, ZEND_TYPE_INIT_CODE(IS_ARRAY, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL }, + { #name, ZEND_TYPE_INIT_CODE(IS_ARRAY, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL }, #define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) \ - { #name, ZEND_TYPE_INIT_CODE(IS_CALLABLE, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL }, + { #name, ZEND_TYPE_INIT_CODE(IS_CALLABLE, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL }, -#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \ +#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, return_reference, required_num_args, class_name, allow_null, is_tentative_return_type) \ static const zend_internal_arg_info name[] = { \ { (const char*)(zend_uintptr_t)(required_num_args), \ - ZEND_TYPE_INIT_CLASS_CONST(#class_name, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL }, + ZEND_TYPE_INIT_CLASS_CONST(#class_name, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL }, + +#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \ + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, return_reference, required_num_args, class_name, allow_null, 0) + +#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \ + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, return_reference, required_num_args, class_name, allow_null, 1) #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(name, class_name, allow_null) \ - ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, 0, -1, class_name, allow_null) + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, 0, -1, class_name, allow_null, 0) + +#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX2(name, return_reference, required_num_args, type, is_tentative_return_type) \ + static const zend_internal_arg_info name[] = { \ + { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL }, #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \ + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX2(name, return_reference, required_num_args, type, 0) + +#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \ + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX2(name, return_reference, required_num_args, type, 1) + +#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX2(name, return_reference, required_num_args, class_name, type, is_tentative_return_type) \ static const zend_internal_arg_info name[] = { \ - { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL }, + { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL }, #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \ + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX2(name, return_reference, required_num_args, class_name, type, 0) + +#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \ + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX2(name, return_reference, required_num_args, class_name, type, 1) + +#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, return_reference, required_num_args, type, allow_null, is_tentative_return_type) \ static const zend_internal_arg_info name[] = { \ - { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL }, + { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL }, #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ - static const zend_internal_arg_info name[] = { \ - { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL }, + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, return_reference, required_num_args, type, allow_null, 0) + +#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, return_reference, required_num_args, type, allow_null, 1) + #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \ - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, allow_null) + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, 0, -1, type, allow_null, 0) #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \ static const zend_internal_arg_info name[] = { \ - { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL }, + { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(return_reference, 0, 0)), NULL }, #define ZEND_BEGIN_ARG_INFO(name, _unused) \ ZEND_BEGIN_ARG_INFO_EX(name, {}, ZEND_RETURN_VALUE, -1) #define ZEND_END_ARG_INFO() }; diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 2823d3bafb3ea..e04547300f75f 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -24,6 +24,7 @@ #include "zend_smart_str.h" ZEND_API zend_class_entry *zend_ce_attribute; +ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute; static HashTable internal_attributes; @@ -67,6 +68,11 @@ ZEND_METHOD(Attribute, __construct) ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags); } +ZEND_METHOD(ReturnTypeWillChange, __construct) +{ + ZEND_PARSE_PARAMETERS_NONE(); +} + static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) { if (attributes) { @@ -278,6 +284,9 @@ void zend_register_attribute_ce(void) zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PARAMETER"), ZEND_ATTRIBUTE_TARGET_PARAMETER); zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_ALL"), ZEND_ATTRIBUTE_TARGET_ALL); zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("IS_REPEATABLE"), ZEND_ATTRIBUTE_IS_REPEATABLE); + + zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange(); + zend_internal_attribute_register(zend_ce_return_type_will_change_attribute, ZEND_ATTRIBUTE_TARGET_METHOD); } void zend_attributes_shutdown(void) diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index 26defa8d4db60..70b0c49aeb9f2 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -8,3 +8,8 @@ final class Attribute public function __construct(int $flags = Attribute::TARGET_ALL) {} } + +final class ReturnTypeWillChange +{ + public function __construct() {} +} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index a09f9161fd61e..5f62eb8fd057d 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,12 +1,16 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0183e750e66999862a7688ecb251017110d06d1f */ + * Stub hash: 3fd949e1b9f49666bed3081ed1e8e711acd9f49c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReturnTypeWillChange___construct, 0, 0, 0) +ZEND_END_ARG_INFO() + ZEND_METHOD(Attribute, __construct); +ZEND_METHOD(ReturnTypeWillChange, __construct); static const zend_function_entry class_Attribute_methods[] = { @@ -14,6 +18,12 @@ static const zend_function_entry class_Attribute_methods[] = { ZEND_FE_END }; + +static const zend_function_entry class_ReturnTypeWillChange_methods[] = { + ZEND_ME(ReturnTypeWillChange, __construct, arginfo_class_ReturnTypeWillChange___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_Attribute(void) { zend_class_entry ce, *class_entry; @@ -30,3 +40,14 @@ static zend_class_entry *register_class_Attribute(void) return class_entry; } + +static zend_class_entry *register_class_ReturnTypeWillChange(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReturnTypeWillChange", class_ReturnTypeWillChange_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 884f653ed7245..27b2716642da6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6476,7 +6476,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall arg_infos->type = zend_compile_typename( return_type_ast, /* force_allow_null */ 0); ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS( - (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0); + (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0, /* is_tentative */ 0); } else { arg_infos->type = (zend_type) ZEND_TYPE_INIT_CODE(fallback_return_type, 0, 0); } @@ -6606,7 +6606,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_alloc_cache_slots(zend_type_get_num_classes(arg_info->type)); } - uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic) + uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0) | (visibility ? _ZEND_IS_PROMOTED_BIT : 0); ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags; if (opcode == ZEND_RECV) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 6291e4397b883..5c60a22086730 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -983,16 +983,19 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1 -/* The send mode and is_variadic flag are stored as part of zend_type */ +/* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT #define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2)) #define _ZEND_IS_PROMOTED_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 3)) +#define _ZEND_IS_TENTATIVE_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 4)) #define ZEND_ARG_SEND_MODE(arg_info) \ ((ZEND_TYPE_FULL_MASK((arg_info)->type) >> _ZEND_SEND_MODE_SHIFT) & 3) #define ZEND_ARG_IS_VARIADIC(arg_info) \ ((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_VARIADIC_BIT) != 0) #define ZEND_ARG_IS_PROMOTED(arg_info) \ ((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_PROMOTED_BIT) != 0) +#define ZEND_ARG_TYPE_IS_TENTATIVE(arg_info) \ + ((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_TENTATIVE_BIT) != 0) #define ZEND_DIM_IS (1 << 0) /* isset fetch needed for null coalesce */ #define ZEND_DIM_ALTERNATIVE_SYNTAX (1 << 1) /* deprecated curly brace usage */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 151eb4ecc59a3..18868dcad2a70 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1294,6 +1294,7 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con static bool zend_verify_internal_return_type(zend_function *zf, zval *ret) { zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1; + if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) { if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) { zend_verify_void_return_error(zf, zend_zval_type_name(ret), ""); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 9f5aa511023df..1dadd1981ba1f 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -27,10 +27,21 @@ #include "zend_operators.h" #include "zend_exceptions.h" #include "zend_enum.h" +#include "zend_attributes.h" ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL; +/* Unresolved means that class declarations that are currently not available are needed to + * determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated + * as an ERROR. */ +typedef enum { + INHERITANCE_UNRESOLVED = -1, + INHERITANCE_ERROR = 0, + INHERITANCE_WARNING = 1, + INHERITANCE_SUCCESS = 2, +} inheritance_status; + static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce); static void add_compatibility_obligation( zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope, @@ -39,7 +50,12 @@ static void add_property_compatibility_obligation( zend_class_entry *ce, const zend_property_info *child_prop, const zend_property_info *parent_prop); -static void zend_type_copy_ctor(zend_type *type, bool persistent) { +static void ZEND_COLD emit_incompatible_method_error( + const zend_function *child, zend_class_entry *child_scope, + const zend_function *parent, zend_class_entry *parent_scope, + inheritance_status status); + +static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *old_list = ZEND_TYPE_LIST(*type); size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); @@ -343,16 +359,6 @@ static bool zend_type_permits_self( return 0; } -/* Unresolved means that class declarations that are currently not available are needed to - * determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated - * as an ERROR. */ -typedef enum { - INHERITANCE_UNRESOLVED = -1, - INHERITANCE_ERROR = 0, - INHERITANCE_SUCCESS = 1, -} inheritance_status; - - static void track_class_dependency(zend_class_entry *ce, zend_string *class_name) { HashTable *ht; @@ -455,7 +461,7 @@ static inheritance_status zend_perform_covariant_class_type_check( static inheritance_status zend_perform_covariant_type_check( zend_class_entry *fe_scope, zend_type fe_type, - zend_class_entry *proto_scope, zend_type proto_type) /* {{{ */ + zend_class_entry *proto_scope, zend_type proto_type, bool tentative) /* {{{ */ { ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type)); @@ -496,7 +502,7 @@ static inheritance_status zend_perform_covariant_type_check( if (added_types) { /* Otherwise adding new types is illegal */ - return INHERITANCE_ERROR; + return tentative ? INHERITANCE_WARNING : INHERITANCE_ERROR; } } @@ -522,7 +528,7 @@ static inheritance_status zend_perform_covariant_type_check( } if (status == INHERITANCE_ERROR) { - return INHERITANCE_ERROR; + return tentative ? INHERITANCE_WARNING : INHERITANCE_ERROR; } if (status != INHERITANCE_SUCCESS) { all_success = 0; @@ -570,7 +576,7 @@ static inheritance_status zend_do_perform_arg_type_hint_check( /* Contravariant type check is performed as a covariant type check with swapped * argument order. */ return zend_perform_covariant_type_check( - proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type); + proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type, 0); } /* }}} */ @@ -660,18 +666,25 @@ static inheritance_status zend_do_perform_implementation_check( /* Check return type compatibility, but only if the prototype already specifies * a return type. Adding a new return type is always valid. */ if (proto->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - /* Removing a return type is not valid. */ + /* Removing a return type is not valid, unless the parent return type is tentative. */ if (!(fe->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { - return INHERITANCE_ERROR; + if (ZEND_ARG_TYPE_IS_TENTATIVE(&proto->common.arg_info[-1])) { + if (status == INHERITANCE_SUCCESS) { + emit_incompatible_method_error(fe, fe_scope, proto, proto_scope, INHERITANCE_WARNING); + } + return status; + } else { + return INHERITANCE_ERROR; + } } local_status = zend_perform_covariant_type_check( fe_scope, fe->common.arg_info[-1].type, - proto_scope, proto->common.arg_info[-1].type); + proto_scope, proto->common.arg_info[-1].type, ZEND_ARG_TYPE_IS_TENTATIVE(&proto->common.arg_info[-1])); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { - if (UNEXPECTED(local_status == INHERITANCE_ERROR)) { - return INHERITANCE_ERROR; + if (UNEXPECTED(local_status == INHERITANCE_ERROR || local_status == INHERITANCE_WARNING)) { + return local_status; } ZEND_ASSERT(local_status == INHERITANCE_UNRESOLVED); status = INHERITANCE_UNRESOLVED; @@ -854,6 +867,18 @@ static void ZEND_COLD emit_incompatible_method_error( zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child), "Could not check compatibility between %s and %s, because class %s is not available", ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype), ZSTR_VAL(unresolved_class)); + } else if (status == INHERITANCE_WARNING) { + zend_attribute *return_type_will_change_attribute = zend_get_attribute_str( + child->common.attributes, + "returntypewillchange", + sizeof("returntypewillchange")-1 + ); + + if (!return_type_will_change_attribute) { + zend_error_at(E_DEPRECATED, NULL, func_lineno(child), + "Declaration of %s should be compatible with %s", + ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype)); + } } else { zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child), "Declaration of %s must be compatible with %s", @@ -874,7 +899,7 @@ static void perform_delayable_implementation_check( if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { add_compatibility_obligation(ce, fe, fe_scope, proto, proto_scope); } else { - ZEND_ASSERT(status == INHERITANCE_ERROR); + ZEND_ASSERT(status == INHERITANCE_ERROR || status == INHERITANCE_WARNING); emit_incompatible_method_error(fe, fe_scope, proto, proto_scope, status); } } @@ -1048,9 +1073,9 @@ inheritance_status property_types_compatible( /* Perform a covariant type check in both directions to determined invariance. */ inheritance_status status1 = zend_perform_covariant_type_check( - child_info->ce, child_info->type, parent_info->ce, parent_info->type); + child_info->ce, child_info->type, parent_info->ce, parent_info->type, 0); inheritance_status status2 = zend_perform_covariant_type_check( - parent_info->ce, parent_info->type, child_info->ce, child_info->type); + parent_info->ce, parent_info->type, child_info->ce, child_info->type, 0); if (status1 == INHERITANCE_SUCCESS && status2 == INHERITANCE_SUCCESS) { return INHERITANCE_SUCCESS; } diff --git a/build/gen_stub.php b/build/gen_stub.php index 084e142e34598..30d1eac28f0c9 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -599,16 +599,20 @@ class ReturnInfo { public $type; /** @var Type|null */ public $phpDocType; + /** @var bool */ + public $tentativeReturnType; - public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType) { + public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType, bool $tentativeReturnType) { $this->byRef = $byRef; $this->type = $type; $this->phpDocType = $phpDocType; + $this->tentativeReturnType = $tentativeReturnType; } public function equals(ReturnInfo $other): bool { return $this->byRef === $other->byRef - && Type::equals($this->type, $other->type); + && Type::equals($this->type, $other->type) + && $this->tentativeReturnType === $other->tentativeReturnType; } public function getMethodSynopsisType(): ?Type { @@ -1431,6 +1435,7 @@ function parseFunctionLike( $isDeprecated = false; $verify = true; $docReturnType = null; + $tentativeReturnType = false; $docParamTypes = []; if ($comment) { @@ -1452,8 +1457,10 @@ function parseFunctionLike( } } else if ($tag->name === 'deprecated') { $isDeprecated = true; - } else if ($tag->name === 'no-verify') { + } else if ($tag->name === 'no-verify') { $verify = false; + } else if ($tag->name === 'tentative-return-type') { + $tentativeReturnType = true; } else if ($tag->name === 'return') { $docReturnType = $tag->getType(); } else if ($tag->name === 'param') { @@ -1530,7 +1537,8 @@ function parseFunctionLike( $return = new ReturnInfo( $func->returnsByRef(), $returnType ? Type::fromNode($returnType) : null, - $docReturnType ? Type::fromPhpDoc($docReturnType) : null + $docReturnType ? Type::fromPhpDoc($docReturnType) : null, + $tentativeReturnType ); return new FuncInfo( @@ -1814,18 +1822,22 @@ protected function pName_FullyQualified(Name\FullyQualified $node) { function funcInfoToCode(FuncInfo $funcInfo): string { $code = ''; $returnType = $funcInfo->return->type; + $isTentativeReturnType = $funcInfo->return->tentativeReturnType; + if ($returnType !== null) { if (null !== $simpleReturnType = $returnType->tryToSimpleType()) { if ($simpleReturnType->isBuiltin) { $code .= sprintf( - "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)\n", + "%s(%s, %d, %d, %s, %d)\n", + $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX", $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs, $simpleReturnType->toTypeCode(), $returnType->isNullable() ); } else { $code .= sprintf( - "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(%s, %d, %d, %s, %d)\n", + "%s(%s, %d, %d, %s, %d)\n", + $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX", $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs, $simpleReturnType->toEscapedName(), $returnType->isNullable() @@ -1835,14 +1847,16 @@ function funcInfoToCode(FuncInfo $funcInfo): string { $arginfoType = $returnType->toArginfoType(); if ($arginfoType->hasClassType()) { $code .= sprintf( - "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(%s, %d, %d, %s, %s)\n", + "%s(%s, %d, %d, %s, %s)\n", + $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX", $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs, $arginfoType->toClassTypeString(), $arginfoType->toTypeMask() ); } else { $code .= sprintf( - "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)\n", + "%s(%s, %d, %d, %s)\n", + $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX", $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs, $arginfoType->toTypeMask() diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index db00909d99b1a..afab1dc069ba0 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -336,7 +336,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) { bool by_ref = (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) != 0; - f.arg_info[i].type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(by_ref, 0)); + f.arg_info[i].type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(by_ref, 0, 0)); } f.num_args = bindptr.lpfuncdesc->cParams; diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index 63d590d9c9909..f17acf1e7ebbb 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -114,214 +114,211 @@ function date_sunset( function date_sun_info(int $timestamp, float $latitude, float $longitude): array {} -// NB: Adding return types to methods is a BC break! -// For now only using @return annotations here. - interface DateTimeInterface { - /** @return string */ - public function format(string $format); + /** @tentative-return-type */ + public function format(string $format): string; - /** @return DateTimeZone|false */ - public function getTimezone(); + /** @tentative-return-type */ + public function getTimezone(): DateTimeZone|false; - /** @return int */ - public function getOffset(); + /** @tentative-return-type */ + public function getOffset(): int; - /** @return int|false */ - public function getTimestamp(); + /** @tentative-return-type */ + public function getTimestamp(): int|false; - /** @return DateInterval|false */ - public function diff(DateTimeInterface $targetObject, bool $absolute = false); + /** @tentative-return-type */ + public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval; - /** @return void */ - public function __wakeup(); + /** @tentative-return-type */ + public function __wakeup(): void; } class DateTime implements DateTimeInterface { public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null) {} - /** @return void */ - public function __wakeup() {} + /** @tentative-return-type */ + public function __wakeup(): void {} - /** @return DateTime */ - public static function __set_state(array $array) {} + /** @tentative-return-type */ + public static function __set_state(array $array): DateTime {} - /** @return DateTime */ - public static function createFromImmutable(DateTimeImmutable $object) {} + /** @tentative-return-type */ + public static function createFromImmutable(DateTimeImmutable $object): DateTime {} public static function createFromInterface(DateTimeInterface $object): DateTime {} /** - * @return DateTime|false + * @tentative-return-type * @alias date_create_from_format */ - public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) {} + public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false {} /** - * @return array|false + * @tentative-return-type * @alias date_get_last_errors */ - public static function getLastErrors() {} + public static function getLastErrors(): array|false {} /** - * @return string + * @tentative-return-type * @alias date_format */ - public function format(string $format) {} + public function format(string $format): string {} /** - * @return DateTime|false + * @tentative-return-type * @alias date_modify */ - public function modify(string $modifier) {} + public function modify(string $modifier): DateTime|false {} /** - * @return DateTime + * @tentative-return-type * @alias date_add */ - public function add(DateInterval $interval) {} + public function add(DateInterval $interval): DateTime {} /** - * @return DateTime + * @tentative-return-type * @alias date_sub */ - public function sub(DateInterval $interval) {} + public function sub(DateInterval $interval): DateTime {} /** - * @return DateTimeZone|false + * @tentative-return-type * @alias date_timezone_get */ - public function getTimezone() {} + public function getTimezone(): DateTimeZone|false {} /** - * @return DateTime + * @tentative-return-type * @alias date_timezone_set */ - public function setTimezone(DateTimeZone $timezone) {} + public function setTimezone(DateTimeZone $timezone): DateTime {} /** - * @return int + * @tentative-return-type * @alias date_offset_get */ - public function getOffset() {} + public function getOffset(): int {} /** - * @return DateTime + * @tentative-return-type * @alias date_time_set */ - public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {} + public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime {} /** - * @return DateTime + * @tentative-return-type * @alias date_date_set */ - public function setDate(int $year, int $month, int $day) {} + public function setDate(int $year, int $month, int $day): DateTime {} /** - * @return DateTime + * @tentative-return-type * @alias date_isodate_set */ - public function setISODate(int $year, int $week, int $dayOfWeek = 1) {} + public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTime {} /** - * @return DateTime + * @tentative-return-type * @alias date_timestamp_set */ - public function setTimestamp(int $timestamp) {} + public function setTimestamp(int $timestamp): DateTime {} /** - * @return int + * @tentative-return-type * @alias date_timestamp_get */ - public function getTimestamp() {} + public function getTimestamp(): int {} /** - * @return DateInterval + * @tentative-return-type * @alias date_diff */ - public function diff(DateTimeInterface $targetObject, bool $absolute = false) {} + public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {} } class DateTimeImmutable implements DateTimeInterface { public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null) {} - /** @return void */ - public function __wakeup() {} + /** @tentative-return-type */ + public function __wakeup(): void {} - /** @return DateTimeImmutable */ - public static function __set_state(array $array) {} + /** @tentative-return-type */ + public static function __set_state(array $array): DateTimeImmutable {} /** - * @return DateTimeImmutable|false + * @tentative-return-type * @alias date_create_immutable_from_format */ - public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) {} + public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTimeImmutable|false {} /** - * @return array|false + * @tentative-return-type * @alias date_get_last_errors */ - public static function getLastErrors() {} + public static function getLastErrors(): array|false {} /** - * @return string + * @tentative-return-type * @alias date_format */ - public function format(string $format) {} + public function format(string $format): string {} /** - * @return DateTimeZone|false + * @tentative-return-type * @alias date_timezone_get */ - public function getTimezone() {} + public function getTimezone(): DateTimeZone|false {} /** - * @return int + * @tentative-return-type * @alias date_offset_get */ - public function getOffset() {} + public function getOffset(): int {} /** - * @return int + * @tentative-return-type * @alias date_timestamp_get */ - public function getTimestamp() {} + public function getTimestamp(): int {} /** - * @return DateInterval + * @tentative-return-type * @alias date_diff */ - public function diff(DateTimeInterface $targetObject, bool $absolute = false) {} + public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {} - /** @return DateTimeImmutable|false */ - public function modify(string $modifier) {} + /** @tentative-return-type */ + public function modify(string $modifier): DateTimeImmutable|false {} - /** @return DateTimeImmutable */ - public function add(DateInterval $interval) {} + /** @tentative-return-type */ + public function add(DateInterval $interval): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public function sub(DateInterval $interval) {} + /** @tentative-return-type */ + public function sub(DateInterval $interval): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public function setTimezone(DateTimeZone $timezone) {} + /** @tentative-return-type */ + public function setTimezone(DateTimeZone $timezone): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {} + /** @tentative-return-type */ + public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public function setDate(int $year, int $month, int $day) {} + /** @tentative-return-type */ + public function setDate(int $year, int $month, int $day): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public function setISODate(int $year, int $week, int $dayOfWeek = 1) {} + /** @tentative-return-type */ + public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public function setTimestamp(int $timestamp) {} + /** @tentative-return-type */ + public function setTimestamp(int $timestamp): DateTimeImmutable {} - /** @return DateTimeImmutable */ - public static function createFromMutable(DateTime $object) {} + /** @tentative-return-type */ + public static function createFromMutable(DateTime $object): DateTimeImmutable {} public static function createFromInterface(DateTimeInterface $object): DateTimeImmutable {} } @@ -331,46 +328,46 @@ class DateTimeZone public function __construct(string $timezone) {} /** - * @return string + * @tentative-return-type * @alias timezone_name_get */ - public function getName() {} + public function getName(): string {} /** - * @return int + * @tentative-return-type * @alias timezone_offset_get */ - public function getOffset(DateTimeInterface $datetime) {} + public function getOffset(DateTimeInterface $datetime): int {} /** - * @return array|false + * @tentative-return-type * @alias timezone_transitions_get */ - public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX) {} + public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false {} /** - * @return array|false + * @tentative-return-type * @alias timezone_location_get */ - public function getLocation() {} + public function getLocation(): array|false {} /** - * @return array + * @tentative-return-type * @alias timezone_abbreviations_list */ - public static function listAbbreviations() {} + public static function listAbbreviations(): array {} /** - * @return array + * @tentative-return-type * @alias timezone_identifiers_list */ - public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) {} + public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array {} - /** @return void */ - public function __wakeup() {} + /** @tentative-return-type */ + public function __wakeup(): void {} - /** @return DateTimeZone */ - public static function __set_state(array $array) {} + /** @tentative-return-type */ + public static function __set_state(array $array): DateTimeZone {} } class DateInterval @@ -378,22 +375,22 @@ class DateInterval public function __construct(string $duration) {} /** - * @return DateInterval|false + * @tentative-return-type * @alias date_interval_create_from_date_string */ - public static function createFromDateString(string $datetime) {} + public static function createFromDateString(string $datetime): DateInterval|false {} /** - * @return string + * @tentative-return-type * @alias date_interval_format */ - public function format(string $format) {} + public function format(string $format): string {} - /** @return void */ - public function __wakeup() {} + /** @tentative-return-type */ + public function __wakeup(): void {} - /** @return DateInterval */ - public static function __set_state(array $array) {} + /** @tentative-return-type */ + public static function __set_state(array $array): DateInterval {} } class DatePeriod implements IteratorAggregate @@ -406,23 +403,23 @@ class DatePeriod implements IteratorAggregate */ public function __construct($start, $interval = UNKNOWN, $end = UNKNOWN, $options = UNKNOWN) {} - /** @return DateTimeInterface */ - public function getStartDate() {} + /** @tentative-return-type */ + public function getStartDate(): DateTimeInterface {} - /** @return DateTimeInterface|null */ - public function getEndDate() {} + /** @tentative-return-type */ + public function getEndDate(): ?DateTimeInterface {} - /** @return DateInterval */ - public function getDateInterval() {} + /** @tentative-return-type */ + public function getDateInterval(): DateInterval {} - /** @return int|null */ - public function getRecurrences() {} + /** @tentative-return-type */ + public function getRecurrences(): ?int {} - /** @return void */ - public function __wakeup() {} + /** @tentative-return-type */ + public function __wakeup(): void {} - /** @return DatePeriod */ - public static function __set_state(array $array) {} + /** @tentative-return-type */ + public static function __set_state(array $array): DatePeriod {} public function getIterator(): Iterator {} } diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 8f90dd8cbadd6..095fc486b78e2 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 108136459e578cc699cffcb84d3335a11f8d5c9d */ + * Stub hash: e8bc76a5db3a225746daffe29c0b8404cf971452 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -225,36 +225,39 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_sun_info, 0, 3, IS_ARRAY, 0 ZEND_ARG_TYPE_INFO(0, longitude, IS_DOUBLE, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeInterface_format, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface_format, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeInterface_getTimezone, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTimeInterface_getTimezone, 0, 0, DateTimeZone, MAY_BE_FALSE) ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeInterface_getOffset arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface_getOffset, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeInterface_getTimestamp arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTimeInterface_getTimestamp, 0, 0, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeInterface_diff, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeInterface_diff, 0, 1, DateInterval, 0) ZEND_ARG_OBJ_INFO(0, targetObject, DateTimeInterface, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, absolute, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeInterface___wakeup arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface___wakeup, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 0, "\"now\"") ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") ZEND_END_ARG_INFO() -#define arginfo_class_DateTime___wakeup arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTime___wakeup arginfo_class_DateTimeInterface___wakeup -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___set_state, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime___set_state, 0, 1, DateTime, 0) ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_createFromImmutable, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_createFromImmutable, 0, 1, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTimeImmutable, 0) ZEND_END_ARG_INFO() @@ -262,21 +265,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_createFromInterfac ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_createFromFormat, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTime_createFromFormat, 0, 2, DateTime, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") ZEND_END_ARG_INFO() -#define arginfo_class_DateTime_getLastErrors arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTime_getLastErrors, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) +ZEND_END_ARG_INFO() #define arginfo_class_DateTime_format arginfo_class_DateTimeInterface_format -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_modify, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTime_modify, 0, 1, DateTime, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, modifier, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_add, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_add, 0, 1, DateTime, 0) ZEND_ARG_OBJ_INFO(0, interval, DateInterval, 0) ZEND_END_ARG_INFO() @@ -284,76 +288,103 @@ ZEND_END_ARG_INFO() #define arginfo_class_DateTime_getTimezone arginfo_class_DateTimeInterface_getTimezone -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTimezone, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setTimezone, 0, 1, DateTime, 0) ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DateTime_getOffset arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTime_getOffset arginfo_class_DateTimeInterface_getOffset -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTime, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setTime, 0, 2, DateTime, 0) ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microsecond, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setDate, 0, 0, 3) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setDate, 0, 3, DateTime, 0) ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, day, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setISODate, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setISODate, 0, 2, DateTime, 0) ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, week, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dayOfWeek, IS_LONG, 0, "1") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTimestamp, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setTimestamp, 0, 1, DateTime, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DateTime_getTimestamp arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTime_getTimestamp arginfo_class_DateTimeInterface_getOffset #define arginfo_class_DateTime_diff arginfo_class_DateTimeInterface_diff #define arginfo_class_DateTimeImmutable___construct arginfo_class_DateTime___construct -#define arginfo_class_DateTimeImmutable___wakeup arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTimeImmutable___wakeup arginfo_class_DateTimeInterface___wakeup -#define arginfo_class_DateTimeImmutable___set_state arginfo_class_DateTime___set_state +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable___set_state, 0, 1, DateTimeImmutable, 0) + ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_createFromFormat arginfo_class_DateTime_createFromFormat +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTimeImmutable_createFromFormat, 0, 2, DateTimeImmutable, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_getLastErrors arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTimeImmutable_getLastErrors arginfo_class_DateTime_getLastErrors #define arginfo_class_DateTimeImmutable_format arginfo_class_DateTimeInterface_format #define arginfo_class_DateTimeImmutable_getTimezone arginfo_class_DateTimeInterface_getTimezone -#define arginfo_class_DateTimeImmutable_getOffset arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTimeImmutable_getOffset arginfo_class_DateTimeInterface_getOffset -#define arginfo_class_DateTimeImmutable_getTimestamp arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTimeImmutable_getTimestamp arginfo_class_DateTimeInterface_getOffset #define arginfo_class_DateTimeImmutable_diff arginfo_class_DateTimeInterface_diff -#define arginfo_class_DateTimeImmutable_modify arginfo_class_DateTime_modify +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTimeImmutable_modify, 0, 1, DateTimeImmutable, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, modifier, IS_STRING, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_add arginfo_class_DateTime_add +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_add, 0, 1, DateTimeImmutable, 0) + ZEND_ARG_OBJ_INFO(0, interval, DateInterval, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_sub arginfo_class_DateTime_add +#define arginfo_class_DateTimeImmutable_sub arginfo_class_DateTimeImmutable_add -#define arginfo_class_DateTimeImmutable_setTimezone arginfo_class_DateTime_setTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setTimezone, 0, 1, DateTimeImmutable, 0) + ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_setTime arginfo_class_DateTime_setTime +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setTime, 0, 2, DateTimeImmutable, 0) + ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 0, "0") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microsecond, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_setDate arginfo_class_DateTime_setDate +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setDate, 0, 3, DateTimeImmutable, 0) + ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, day, IS_LONG, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_setISODate arginfo_class_DateTime_setISODate +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setISODate, 0, 2, DateTimeImmutable, 0) + ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, week, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dayOfWeek, IS_LONG, 0, "1") +ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeImmutable_setTimestamp arginfo_class_DateTime_setTimestamp +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setTimestamp, 0, 1, DateTimeImmutable, 0) + ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) +ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeImmutable_createFromMutable, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_createFromMutable, 0, 1, DateTimeImmutable, 0) ZEND_ARG_OBJ_INFO(0, object, DateTime, 0) ZEND_END_ARG_INFO() @@ -365,43 +396,49 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, timezone, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeZone_getName arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_getName, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone_getOffset, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_getOffset, 0, 1, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, datetime, DateTimeInterface, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone_getTransitions, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTimeZone_getTransitions, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampBegin, IS_LONG, 0, "PHP_INT_MIN") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "PHP_INT_MAX") ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeZone_getLocation arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTimeZone_getLocation arginfo_class_DateTime_getLastErrors -#define arginfo_class_DateTimeZone_listAbbreviations arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_listAbbreviations, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone_listIdentifiers, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_listIdentifiers, 0, 0, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timezoneGroup, IS_LONG, 0, "DateTimeZone::ALL") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, countryCode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -#define arginfo_class_DateTimeZone___wakeup arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateTimeZone___wakeup arginfo_class_DateTimeInterface___wakeup -#define arginfo_class_DateTimeZone___set_state arginfo_class_DateTime___set_state +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeZone___set_state, 0, 1, DateTimeZone, 0) + ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateInterval___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, duration, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateInterval_createFromDateString, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateInterval_createFromDateString, 0, 1, DateInterval, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_DateInterval_format arginfo_class_DateTimeInterface_format -#define arginfo_class_DateInterval___wakeup arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DateInterval___wakeup arginfo_class_DateTimeInterface___wakeup -#define arginfo_class_DateInterval___set_state arginfo_class_DateTime___set_state +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateInterval___set_state, 0, 1, DateInterval, 0) + ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DatePeriod___construct, 0, 0, 1) ZEND_ARG_INFO(0, start) @@ -410,17 +447,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DatePeriod___construct, 0, 0, 1) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() -#define arginfo_class_DatePeriod_getStartDate arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getStartDate, 0, 0, DateTimeInterface, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DatePeriod_getEndDate arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getEndDate, 0, 0, DateTimeInterface, 1) +ZEND_END_ARG_INFO() -#define arginfo_class_DatePeriod_getDateInterval arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getDateInterval, 0, 0, DateInterval, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DatePeriod_getRecurrences arginfo_class_DateTimeInterface_getTimezone +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DatePeriod_getRecurrences, 0, 0, IS_LONG, 1) +ZEND_END_ARG_INFO() -#define arginfo_class_DatePeriod___wakeup arginfo_class_DateTimeInterface_getTimezone +#define arginfo_class_DatePeriod___wakeup arginfo_class_DateTimeInterface___wakeup -#define arginfo_class_DatePeriod___set_state arginfo_class_DateTime___set_state +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod___set_state, 0, 1, DatePeriod, 0) + ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getIterator, 0, 0, Iterator, 0) ZEND_END_ARG_INFO() diff --git a/ext/date/tests/DateTime_extends_basic3.phpt b/ext/date/tests/DateTime_extends_basic3.phpt index 27fc602e00506..a1e72cf1f2c32 100644 --- a/ext/date/tests/DateTime_extends_basic3.phpt +++ b/ext/date/tests/DateTime_extends_basic3.phpt @@ -9,7 +9,7 @@ echo "*** Testing new DateTime() : with user format() method ***\n"; class DateTimeExt extends DateTime { - public function format($format = "F j, Y, g:i:s a") + public function format($format = "F j, Y, g:i:s a"): string { return parent::format($format); } diff --git a/ext/date/tests/bug55407.phpt b/ext/date/tests/bug55407.phpt index c4638c94b3352..f478a52129826 100644 --- a/ext/date/tests/bug55407.phpt +++ b/ext/date/tests/bug55407.phpt @@ -6,7 +6,7 @@ error_reporting=-1 op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - smart_str_append_printf(str, " %s- Return [ ", indent); + if ((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { + smart_str_append_printf(str, " %s- %s [ ", indent, ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]) ? "Tentative return" : "Return"); if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) { zend_string *type_str = zend_type_to_string(fptr->common.arg_info[-1].type); smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str)); @@ -3449,7 +3449,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE); + RETVAL_BOOL((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); } /* }}} */ @@ -3465,7 +3465,43 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { + if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { + RETURN_NULL(); + } + + reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1); +} +/* }}} */ + +/* {{{ Return whether the function has a return type */ +ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) +{ + reflection_object *intern; + zend_function *fptr; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(fptr); + + RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); +} +/* }}} */ + +/* {{{ Returns the return type associated with the function */ +ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) +{ + reflection_object *intern; + zend_function *fptr; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(fptr); + + if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { RETURN_NULL(); } diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 69384f88bb320..7bd22b6d839d9 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -98,6 +98,10 @@ public function hasReturnType() {} /** @return ReflectionType|null */ public function getReturnType() {} + public function hasTentativeReturnType(): bool {} + + public function getTentativeReturnType(): ?ReflectionType {} + /** @return ReflectionAttribute[] */ public function getAttributes(?string $name = null, int $flags = 0): array {} } diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index b1ea070d2ac3a..fa48db957e96c 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 47ac64b027cdeb0e9996147277f79fa9d6b876bd */ + * Stub hash: 0b2b52d4f891a594ccfcbcc0edeec97a9e0f80e6 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -59,6 +59,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFunctionAbstract_getReturnType arginfo_class_ReflectionFunctionAbstract_inNamespace +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType, 0, 0, ReflectionType, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") @@ -218,8 +224,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_isTrait arginfo_class_ReflectionFunctionAbstract_inNamespace -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_isEnum, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionClass_isEnum arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType #define arginfo_class_ReflectionClass_isAbstract arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -311,7 +316,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_isDefault arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionClass_isEnum +#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType #define arginfo_class_ReflectionProperty_getModifiers arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -325,7 +330,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_hasType arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionClass_isEnum +#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -358,7 +363,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes -#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionClass_isEnum +#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType #define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone @@ -405,7 +410,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionClass_isEnum +#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType #define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes @@ -482,7 +487,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_getTarget, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionClass_isEnum +#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType #define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes @@ -505,10 +510,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionEnum_getCases arginfo_class_ReflectionUnionType_getTypes -#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionClass_isEnum +#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnum_getBackingType, 0, 0, ReflectionType, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionEnum_getBackingType arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType #define arginfo_class_ReflectionEnumUnitCase___construct arginfo_class_ReflectionClassConstant___construct @@ -569,6 +573,8 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables); ZEND_METHOD(ReflectionFunctionAbstract, returnsReference); ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType); ZEND_METHOD(ReflectionFunctionAbstract, getReturnType); +ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType); +ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType); ZEND_METHOD(ReflectionFunctionAbstract, getAttributes); ZEND_METHOD(ReflectionFunction, __construct); ZEND_METHOD(ReflectionFunction, __toString); @@ -805,6 +811,8 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = { ZEND_ME(ReflectionFunctionAbstract, returnsReference, arginfo_class_ReflectionFunctionAbstract_returnsReference, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, hasReturnType, arginfo_class_ReflectionFunctionAbstract_hasReturnType, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getReturnType, arginfo_class_ReflectionFunctionAbstract_getReturnType, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, hasTentativeReturnType, arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, getTentativeReturnType, arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getAttributes, arginfo_class_ReflectionFunctionAbstract_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt b/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt new file mode 100644 index 0000000000000..576b87172292a --- /dev/null +++ b/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt @@ -0,0 +1,62 @@ +--TEST-- +ReflectionMethod returns tentative return type information correctly +--FILE-- +hasReturnType()); +var_dump($methodInfo->hasTentativeReturnType()); +var_dump($methodInfo->getReturnType()); +var_dump((string) $methodInfo->getTentativeReturnType()); +var_dump((string) $methodInfo); +echo "\n"; + +$methodInfo = new ReflectionMethod(MyDateTimeZone::class, 'listIdentifiers'); + +var_dump($methodInfo->hasReturnType()); +var_dump($methodInfo->hasTentativeReturnType()); +var_dump((string) $methodInfo->getReturnType()); +var_dump($methodInfo->getTentativeReturnType()); +var_dump((string) $methodInfo); +echo "\n"; + +?> +--EXPECTF-- +bool(false) +bool(true) +NULL +string(5) "array" +string(%d) "Method [ static public method listIdentifiers ] { + + - Parameters [2] { + Parameter #0 [ int $timezoneGroup = DateTimeZone::ALL ] + Parameter #1 [ ?string $countryCode = null ] + } + - Tentative return [ array ] +} +" + +bool(true) +bool(false) +string(6) "string" +NULL +string(%d) "Method [ static public method listIdentifiers ] { + @@ %s + + - Parameters [2] { + Parameter #0 [ int $timezoneGroup = %d ] + Parameter #1 [ ?string $countryCode = NULL ] + } + - Return [ string ] +} +"