From 86d8f35b76fff6b7bac4fe5590540c6fcb7fea4a Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 16 Jun 2023 01:47:08 +0100 Subject: [PATCH 01/24] Add tests for relative class types --- .../invalid_parent_type_outside_class.phpt | 10 ++++++++++ .../invalid_self_type_outside_class.phpt | 10 ++++++++++ .../invalid_static_type_outside_class.phpt | 10 ++++++++++ .../relative_type_in_closures.phpt | 13 +++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_parent_type_outside_class.phpt create mode 100644 Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_self_type_outside_class.phpt create mode 100644 Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_static_type_outside_class.phpt create mode 100644 Zend/tests/type_declarations/relative_class_types/relative_type_in_closures.phpt diff --git a/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_parent_type_outside_class.phpt b/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_parent_type_outside_class.phpt new file mode 100644 index 0000000000000..1285a710c1d0a --- /dev/null +++ b/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_parent_type_outside_class.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use parent type outside a class +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use "parent" when no class scope is active in %s on line %d diff --git a/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_self_type_outside_class.phpt b/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_self_type_outside_class.phpt new file mode 100644 index 0000000000000..7e1bc4a4f2fdd --- /dev/null +++ b/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_self_type_outside_class.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use self type outside a class +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use "self" when no class scope is active in %s on line %d diff --git a/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_static_type_outside_class.phpt b/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_static_type_outside_class.phpt new file mode 100644 index 0000000000000..4c54db2017708 --- /dev/null +++ b/Zend/tests/type_declarations/relative_class_types/invalid_types/invalid_static_type_outside_class.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use static type outside a class +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use "static" when no class scope is active in %s on line %d diff --git a/Zend/tests/type_declarations/relative_class_types/relative_type_in_closures.phpt b/Zend/tests/type_declarations/relative_class_types/relative_type_in_closures.phpt new file mode 100644 index 0000000000000..c729df12bf306 --- /dev/null +++ b/Zend/tests/type_declarations/relative_class_types/relative_type_in_closures.phpt @@ -0,0 +1,13 @@ +--TEST-- +Relative class types can be used for closures as it may be bound to a class +--FILE-- + +DONE +--EXPECT-- +DONE From 6006c31cfb19a4c9768a9620394456fd03cfdd65 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 16 Jun 2023 08:10:42 +0100 Subject: [PATCH 02/24] Resolve relative types at compile time when possible --- Zend/tests/magic_methods_021.phpt | 2 +- .../duplicate_relative_class_parent_type.phpt | 15 +++ .../duplicate_relative_class_self_type.phpt | 12 ++ .../duplicate_relative_class_static_type.phpt | 12 ++ ...solved_relative_class_type_variation1.phpt | 12 ++ ...solved_relative_class_type_variation2.phpt | 12 ++ ...solved_relative_class_type_variation3.phpt | 15 +++ ...solved_relative_class_type_variation4.phpt | 15 +++ Zend/zend_compile.c | 118 ++++++++++++++---- Zend/zend_string.h | 2 + Zend/zend_types.h | 16 ++- 11 files changed, 205 insertions(+), 26 deletions(-) create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt create mode 100644 Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt diff --git a/Zend/tests/magic_methods_021.phpt b/Zend/tests/magic_methods_021.phpt index fd3e7fa9d0ee5..33a7220e5674e 100644 --- a/Zend/tests/magic_methods_021.phpt +++ b/Zend/tests/magic_methods_021.phpt @@ -12,7 +12,7 @@ class Foo2 { } class Foo3 { - public static function __set_state(array $data): Foo3|self {} + public static function __set_state(array $data): Foo3|Foo2 {} } ?> diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt new file mode 100644 index 0000000000000..4596785263c70 --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt @@ -0,0 +1,15 @@ +--TEST-- +Duplicate parent type +--FILE-- + +--EXPECTF-- +Fatal error: Duplicate type parent is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt new file mode 100644 index 0000000000000..4fcdb1f6109e3 --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt @@ -0,0 +1,12 @@ +--TEST-- +Duplicate self type +--FILE-- + +--EXPECTF-- +Fatal error: Duplicate type self is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt new file mode 100644 index 0000000000000..9c6e37ca6e093 --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt @@ -0,0 +1,12 @@ +--TEST-- +Duplicate static type +--FILE-- + +--EXPECTF-- +Fatal error: Duplicate type static is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt new file mode 100644 index 0000000000000..03b84709c6de3 --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt @@ -0,0 +1,12 @@ +--TEST-- +Relative class type self resolving to an existing entry (after variation) +--FILE-- + +--EXPECTF-- +Fatal error: self resolves to Foo which is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt new file mode 100644 index 0000000000000..5a2bc2e3f1942 --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt @@ -0,0 +1,12 @@ +--TEST-- +Relative class type self resolving to an existing entry (before variation) +--FILE-- + +--EXPECTF-- +Fatal error: self resolves to Foo which is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt new file mode 100644 index 0000000000000..21306e154d87e --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt @@ -0,0 +1,15 @@ +--TEST-- +Relative class type parent resolving to an existing entry (after variation) +--FILE-- + +--EXPECTF-- +Fatal error: parent resolves to Foo which is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt new file mode 100644 index 0000000000000..e77980aba53f4 --- /dev/null +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt @@ -0,0 +1,15 @@ +--TEST-- +Relative class type parent resolving to an existing entry (before variation) +--FILE-- + +--EXPECTF-- +Fatal error: parent resolves to Foo which is redundant in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3cbb0c4f7a1ed..aa1c3477fa9f8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1354,10 +1354,10 @@ static zend_string *add_intersection_type(zend_string *str, ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type)); - zend_string *name = ZEND_TYPE_NAME(*single_type); - zend_string *resolved = resolve_class_name(name, scope); - intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true); - zend_string_release(resolved); + ZEND_ASSERT(!ZEND_TYPE_IS_RELATIVE_SELF(*single_type) && "Compile time disallowed to have 'self' in intersection"); + ZEND_ASSERT(!ZEND_TYPE_IS_RELATIVE_PARENT(*single_type) && "Compile time disallowed to have 'parent' in intersection"); + + intersection_str = add_type_string(intersection_str, ZEND_TYPE_NAME(*single_type), /* is_intersection */ true); } ZEND_TYPE_LIST_FOREACH_END(); ZEND_ASSERT(intersection_str); @@ -1389,13 +1389,30 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop } ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type)); - zend_string *name = ZEND_TYPE_NAME(*list_type); - zend_string *resolved = resolve_class_name(name, scope); - str = add_type_string(str, resolved, /* is_intersection */ false); - zend_string_release(resolved); + + /* We already have resolved types from compile time + * Mimic unresolved types for BC with "self" and "parent" */ + if (!scope && ZEND_TYPE_IS_RELATIVE_SELF(*list_type)) { + str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_SELF), /* is_intersection */ false); + } else if (!scope && ZEND_TYPE_IS_RELATIVE_PARENT(*list_type)) { + str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_PARENT), /* is_intersection */ false); + } else { + zend_string *name = ZEND_TYPE_NAME(*list_type); + zend_string *resolved = resolve_class_name(name, scope); + str = add_type_string(str, resolved, /* is_intersection */ false); + zend_string_release(resolved); + } } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(type)) { - str = resolve_class_name(ZEND_TYPE_NAME(type), scope); + /* We already have resolved types from compile time + * Mimic unresolved types for BC with "self" and "parent" */ + if (!scope && ZEND_TYPE_IS_RELATIVE_SELF(type)) { + str = ZSTR_KNOWN(ZEND_STR_SELF); + } else if (!scope && ZEND_TYPE_IS_RELATIVE_PARENT(type)) { + str = ZSTR_KNOWN(ZEND_STR_PARENT); + } else { + str = resolve_class_name(ZEND_TYPE_NAME(type), scope); + } } uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); @@ -6620,14 +6637,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast) return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0); } else { - zend_string *class_name = zend_ast_get_str(ast); - uint8_t type_code = zend_lookup_builtin_type_by_name(class_name); + zend_string *type_name = zend_ast_get_str(ast); + uint8_t type_code = zend_lookup_builtin_type_by_name(type_name); if (type_code != 0) { if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { zend_error_noreturn(E_COMPILE_ERROR, "Type declaration '%s' must be unqualified", - ZSTR_VAL(zend_string_tolower(class_name))); + ZSTR_VAL(zend_string_tolower(type_name))); } /* Transform iterable into a type union alias */ @@ -6641,38 +6658,58 @@ static zend_type zend_compile_single_typename(zend_ast *ast) return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0); } else { const char *correct_name; - zend_string *orig_name = zend_ast_get_str(ast); uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); + uint32_t type_flags = 0; + zend_string *class_name = type_name; + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { class_name = zend_resolve_class_name_ast(ast); zend_assert_valid_class_name(class_name); } else { + ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_SELF || fetch_type == ZEND_FETCH_CLASS_PARENT); + zend_ensure_valid_class_fetch_type(fetch_type); + if (fetch_type == ZEND_FETCH_CLASS_SELF) { + type_flags = _ZEND_TYPE_SELF_BIT; + /* Scope might be unknown for unbound closures and traits */ + if (zend_is_scope_known()) { + class_name = CG(active_class_entry)->name; + ZEND_ASSERT(class_name && "must know class name when resolving self type at compile time"); + } + } else { + ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_PARENT); + type_flags = _ZEND_TYPE_PARENT_BIT; + /* Scope might be unknown for unbound closures and traits */ + if (zend_is_scope_known()) { + class_name = CG(active_class_entry)->parent_name; + ZEND_ASSERT(class_name && "must know class name when resolving parent type at compile time"); + } + } zend_string_addref(class_name); } if (ast->attr == ZEND_NAME_NOT_FQ - && zend_is_confusable_type(orig_name, &correct_name) - && zend_is_not_imported(orig_name)) { + && zend_is_confusable_type(type_name, &correct_name) + && zend_is_not_imported(type_name)) { const char *extra = FC(current_namespace) ? " or import the class with \"use\"" : ""; if (correct_name) { zend_error(E_COMPILE_WARNING, "\"%s\" will be interpreted as a class name. Did you mean \"%s\"? " "Write \"\\%s\"%s to suppress this warning", - ZSTR_VAL(orig_name), correct_name, ZSTR_VAL(class_name), extra); + ZSTR_VAL(type_name), correct_name, ZSTR_VAL(class_name), extra); } else { zend_error(E_COMPILE_WARNING, "\"%s\" is not a supported builtin type " "and will be interpreted as a class name. " "Write \"\\%s\"%s to suppress this warning", - ZSTR_VAL(orig_name), ZSTR_VAL(class_name), extra); + ZSTR_VAL(type_name), ZSTR_VAL(class_name), extra); } } class_name = zend_new_interned_string(class_name); zend_alloc_ce_cache(class_name); - return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0); + return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, type_flags); } } } @@ -6754,7 +6791,34 @@ static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list } if (zend_string_equals_ci(ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(type))) { zend_string *single_type_str = zend_type_to_string(type); - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + if ( + ZEND_TYPE_IS_RELATIVE_SELF(type) + || ZEND_TYPE_IS_RELATIVE_PARENT(type) + ) { + if ( ( + ZEND_TYPE_FULL_MASK(type) + & ZEND_TYPE_FULL_MASK(type_list->types[i]) + & (_ZEND_TYPE_SELF_BIT|_ZEND_TYPE_PARENT_BIT)) != 0 + ) { + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } + /* zend_type_to_string() will return "self" or "parent" where the resolved type is stored in + * ZEND_TYPE_NAME() */ + zend_error_noreturn(E_COMPILE_ERROR, "%s resolves to %s which is redundant", + ZSTR_VAL(single_type_str), ZSTR_VAL(ZEND_TYPE_NAME(type)) + ); + } else if ( + ZEND_TYPE_IS_RELATIVE_SELF(type_list->types[i]) + || ZEND_TYPE_IS_RELATIVE_PARENT(type_list->types[i]) + ) { + /* zend_type_to_string() will return "self" or "parent" where the resolved type is stored in + * ZEND_TYPE_NAME() */ + zend_error_noreturn(E_COMPILE_ERROR, "%s resolves to %s which is redundant", + ZEND_TYPE_IS_RELATIVE_SELF(type_list->types[i]) ? "self" : "parent", ZSTR_VAL(ZEND_TYPE_NAME(type)) + ); + } else { + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } } } } @@ -6795,6 +6859,7 @@ static zend_type zend_compile_typename_ex( /* Switch from single name to name list. */ type_list->num_types = 1; type_list->types[0] = type; + /* Clear MAY_BE_* type flags */ ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK; } /* Mark type as list type */ @@ -6841,6 +6906,7 @@ static zend_type zend_compile_typename_ex( "Type contains both true and false, bool should be used instead"); } ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type); + /* Clear MAY_BE_* type flags */ ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK; if (ZEND_TYPE_IS_COMPLEX(single_type)) { @@ -6848,13 +6914,18 @@ static zend_type zend_compile_typename_ex( /* The first class type can be stored directly as the type ptr payload. */ ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type)); ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT; + /* Add flags indicating the named type is self/parent */ + ZEND_TYPE_FULL_MASK(type) |= (ZEND_TYPE_FULL_MASK(single_type) & _ZEND_TYPE_RELATIVE_TYPE_MASK); } else { if (type_list->num_types == 0) { /* Switch from single name to name list. */ type_list->num_types = 1; type_list->types[0] = type; + /* Clear MAY_BE_* type flags */ ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK; ZEND_TYPE_SET_LIST(type, type_list); + /* Clear flags indicating the named type is self/parent */ + ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_RELATIVE_TYPE_MASK; } type_list->types[type_list->num_types++] = single_type; @@ -6916,10 +6987,11 @@ static zend_type zend_compile_typename_ex( zend_string_release_ex(standard_type_str, false); } /* Check for "self" and "parent" too */ - if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self") - || zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) { - zend_error_noreturn(E_COMPILE_ERROR, - "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type))); + if (ZEND_TYPE_IS_RELATIVE_SELF(single_type)) { + zend_error_noreturn(E_COMPILE_ERROR, "Type self cannot be part of an intersection type"); + } + if (ZEND_TYPE_IS_RELATIVE_PARENT(single_type)) { + zend_error_noreturn(E_COMPILE_ERROR, "Type parent cannot be part of an intersection type"); } /* Add type to the type list */ diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 910e2eed250fe..db02e639f6887 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -621,6 +621,8 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_NULL_LOWERCASE, "null") \ _(ZEND_STR_MIXED, "mixed") \ _(ZEND_STR_TRAVERSABLE, "Traversable") \ + _(ZEND_STR_SELF, "self") \ + _(ZEND_STR_PARENT, "parent") \ _(ZEND_STR_SLEEP, "__sleep") \ _(ZEND_STR_WAKEUP, "__wakeup") \ _(ZEND_STR_CASES, "cases") \ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index c4a07f58874ab..dc727cece655b 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,8 +142,12 @@ typedef struct { zend_type types[1]; } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 -#define _ZEND_TYPE_MASK ((1u << 25) - 1) +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 27 +#define _ZEND_TYPE_MASK ((1u << 27) - 1) +/* Only one of these bits may be set. */ +#define _ZEND_TYPE_PARENT_BIT (1u << 26) +#define _ZEND_TYPE_SELF_BIT (1u << 25) +#define _ZEND_TYPE_RELATIVE_TYPE_MASK (_ZEND_TYPE_SELF_BIT|_ZEND_TYPE_PARENT_BIT) /* Only one of these bits may be set. */ #define _ZEND_TYPE_NAME_BIT (1u << 24) // Used to signify that type.ptr is not a `zend_string*` but a `const char*`, @@ -166,6 +170,14 @@ typedef struct { #define ZEND_TYPE_IS_SET(t) \ (((t).type_mask & _ZEND_TYPE_MASK) != 0) + +/* To determine if the type resolved type was written with "self" */ +#define ZEND_TYPE_IS_RELATIVE_SELF(t) \ + ((((t).type_mask) & _ZEND_TYPE_SELF_BIT) != 0) +/* To determine if the type resolved type was written with "parent" */ +#define ZEND_TYPE_IS_RELATIVE_PARENT(t) \ + ((((t).type_mask) & _ZEND_TYPE_PARENT_BIT) != 0) + /* If a type is complex it means it's either a list with a union or intersection, * or the void pointer is a class name */ #define ZEND_TYPE_IS_COMPLEX(t) \ From d18d306e3990b35a5e34135609a2de5c7dc7aa80 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Wed, 14 Jun 2023 11:48:52 +0100 Subject: [PATCH 03/24] Support for resolving self/parent/static in ReflectionType This is only possible if the type is resolvable. --- ext/reflection/php_reflection.c | 140 +++++++++++++----- ext/reflection/php_reflection.h | 1 + ext/reflection/php_reflection.stub.php | 5 + ext/reflection/php_reflection_arginfo.h | 21 ++- .../ReflectionExtension_getClasses_basic.phpt | 35 +++-- ext/reflection/tests/bug80190.phpt | 12 +- .../relative_class_types_in_classes.phpt | 116 +++++++++++++++ ...ve_class_types_in_classes_parameters1.phpt | 103 +++++++++++++ ...ve_class_types_in_classes_parameters2.phpt | 103 +++++++++++++ ...ve_class_types_in_classes_parameters3.phpt | 110 ++++++++++++++ ...ve_class_types_in_classes_parameters4.phpt | 110 ++++++++++++++ .../relative_class_types_in_interfaces.phpt | 86 +++++++++++ .../unresolved_relative_class_types.phpt | 81 ++++++++++ ...olved_relative_class_types_union_type.phpt | 91 ++++++++++++ 14 files changed, 959 insertions(+), 55 deletions(-) create mode 100644 ext/reflection/tests/types/relative_class_types_in_classes.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_classes_parameters1.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_classes_parameters2.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_classes_parameters3.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_classes_parameters4.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_interfaces.phpt create mode 100644 ext/reflection/tests/types/unresolved_relative_class_types.phpt create mode 100644 ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 314a0359d3fc7..6414e3da516d8 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -82,6 +82,7 @@ PHPAPI zend_class_entry *reflection_generator_ptr; PHPAPI zend_class_entry *reflection_parameter_ptr; PHPAPI zend_class_entry *reflection_type_ptr; PHPAPI zend_class_entry *reflection_named_type_ptr; +PHPAPI zend_class_entry *reflection_relative_class_type_ptr; PHPAPI zend_class_entry *reflection_intersection_type_ptr; PHPAPI zend_class_entry *reflection_union_type_ptr; PHPAPI zend_class_entry *reflection_class_ptr; @@ -1366,6 +1367,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje typedef enum { NAMED_TYPE = 0, + RELATIVE_TYPE = 3, UNION_TYPE = 1, INTERSECTION_TYPE = 2 } reflection_type_kind; @@ -1393,6 +1395,11 @@ static reflection_type_kind get_type_kind(zend_type type) { if (type_mask_without_null != 0) { return UNION_TYPE; } + + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type)); + if (ZEND_TYPE_IS_RELATIVE_SELF(type) || ZEND_TYPE_IS_RELATIVE_PARENT(type)) { + return RELATIVE_TYPE; + } return NAMED_TYPE; } if (type_mask_without_null == MAY_BE_BOOL || ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY) { @@ -1402,12 +1409,22 @@ static reflection_type_kind get_type_kind(zend_type type) { if ((type_mask_without_null & (type_mask_without_null - 1)) != 0) { return UNION_TYPE; } + + /* "static" is a relative type */ + if (type_mask_without_null == MAY_BE_STATIC) { + return RELATIVE_TYPE; + } return NAMED_TYPE; } -/* {{{ reflection_type_factory */ -static void reflection_type_factory(zend_type type, zval *object, bool legacy_behavior) -{ +/* ReflectionType private constructor + * The object_ce is used to be able to resolve back the "self", "parent", and "static" ReflectionNamedTypes + * This can be NULL, e.g. when constructing types of a free function + */ +static void reflection_type_factory( + zend_type type, zval *object, bool legacy_behavior, + zend_class_entry *object_ce +) { reflection_object *intern; type_reference *reference; reflection_type_kind type_kind = get_type_kind(type); @@ -1424,6 +1441,9 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be case NAMED_TYPE: reflection_instantiate(reflection_named_type_ptr, object); break; + case RELATIVE_TYPE: + reflection_instantiate(reflection_relative_class_type_ptr, object); + break; EMPTY_SWITCH_DEFAULT_CASE(); } @@ -1433,6 +1453,7 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be reference->legacy_behavior = legacy_behavior && type_kind == NAMED_TYPE && !is_mixed && !is_only_null; intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; + intern->ce = object_ce; /* Property types may be resolved during the lifetime of the ReflectionType. * If we reference a string, make sure it doesn't get released. However, only @@ -1443,7 +1464,6 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be zend_string_addref(ZEND_TYPE_NAME(type)); } } -/* }}} */ /* {{{ reflection_function_factory */ static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object) @@ -2698,17 +2718,14 @@ ZEND_METHOD(ReflectionParameter, getClass) * TODO: Think about moving these checks to the compiler or some sort of * lint-mode. */ - zend_string *class_name; - - class_name = ZEND_TYPE_NAME(param->arg_info->type); - if (zend_string_equals_literal_ci(class_name, "self")) { + if (ZEND_TYPE_IS_RELATIVE_SELF(param->arg_info->type)) { ce = param->fptr->common.scope; if (!ce) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Parameter uses \"self\" as type but function is not a class member"); RETURN_THROWS(); } - } else if (zend_string_equals_literal_ci(class_name, "parent")) { + } else if (ZEND_TYPE_IS_RELATIVE_PARENT(param->arg_info->type)) { ce = param->fptr->common.scope; if (!ce) { zend_throw_exception_ex(reflection_exception_ptr, 0, @@ -2722,6 +2739,7 @@ ZEND_METHOD(ReflectionParameter, getClass) } ce = ce->parent; } else { + zend_string *class_name = ZEND_TYPE_NAME(param->arg_info->type); ce = zend_lookup_class(class_name); if (!ce) { zend_throw_exception_ex(reflection_exception_ptr, 0, @@ -2763,7 +2781,7 @@ ZEND_METHOD(ReflectionParameter, getType) if (!ZEND_TYPE_IS_SET(param->arg_info->type)) { RETURN_NULL(); } - reflection_type_factory(param->arg_info->type, return_value, 1); + reflection_type_factory(param->arg_info->type, return_value, /* legacy_behavior */ true, intern->ce); } /* }}} */ @@ -3135,19 +3153,64 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin) } /* }}} */ -static void append_type(zval *return_value, zend_type type) { +/* {{{ Returns whether type is a builtin type */ +ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) +{ + reflection_object *intern; + type_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(param); + + /* Unbound closures can use relative class types */ + if (!intern->ce) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve relative class name for a closure"); + RETURN_THROWS(); + } + + if (intern->ce->ce_flags & ZEND_ACC_TRAIT) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve relative class name for a trait"); + RETURN_THROWS(); + } + + /* Support for legacy behaviour of nullable types and ReflectionNamedType */ + bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL; + zend_type resolved_type; + /* For static resolved name is the name of the class */ + if (ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC) { + if (intern->ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve \"static\" type of an interface"); + RETURN_THROWS(); + } + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /*extra flags */ 0); + } else { + ZEND_ASSERT(ZEND_TYPE_IS_RELATIVE_SELF(param->type) || ZEND_TYPE_IS_RELATIVE_PARENT(param->type)); + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /*extra flags */ 0); + } + + reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce); +} +/* }}} */ + +static void append_type(zval *return_value, zend_type type, zend_class_entry *object_ce) { zval reflection_type; /* Drop iterable BC bit for type list */ if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) { ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_ITERABLE_BIT; } - reflection_type_factory(type, &reflection_type, 0); + reflection_type_factory(type, &reflection_type, /* legacy_behavior */ false, object_ce); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &reflection_type); } -static void append_type_mask(zval *return_value, uint32_t type_mask) { - append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask)); +static void append_type_mask(zval *return_value, uint32_t type_mask, zend_class_entry *object_ce) { + append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask), object_ce); } /* {{{ Returns the types that are part of this union type */ @@ -3166,46 +3229,53 @@ ZEND_METHOD(ReflectionUnionType, getTypes) if (ZEND_TYPE_HAS_LIST(param->type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { - append_type(return_value, *list_type); + append_type(return_value, *list_type, /* object_ce */ intern->ce); } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(param->type)) { zend_string *name = ZEND_TYPE_NAME(param->type); - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0)); + uint32_t type_flags = 0; + if (ZEND_TYPE_IS_RELATIVE_SELF(param->type)) { + type_flags = _ZEND_TYPE_SELF_BIT; + } + if (ZEND_TYPE_IS_RELATIVE_PARENT(param->type)) { + type_flags = _ZEND_TYPE_PARENT_BIT; + } + append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ type_flags), /* object_ce */ intern->ce); } type_mask = ZEND_TYPE_PURE_MASK(param->type); ZEND_ASSERT(!(type_mask & MAY_BE_VOID)); ZEND_ASSERT(!(type_mask & MAY_BE_NEVER)); if (type_mask & MAY_BE_STATIC) { - append_type_mask(return_value, MAY_BE_STATIC); + append_type_mask(return_value, MAY_BE_STATIC, /* object_ce */ intern->ce); } if (type_mask & MAY_BE_CALLABLE) { - append_type_mask(return_value, MAY_BE_CALLABLE); + append_type_mask(return_value, MAY_BE_CALLABLE, /* object_ce */ NULL); } if (type_mask & MAY_BE_OBJECT) { - append_type_mask(return_value, MAY_BE_OBJECT); + append_type_mask(return_value, MAY_BE_OBJECT, /* object_ce */ NULL); } if (type_mask & MAY_BE_ARRAY) { - append_type_mask(return_value, MAY_BE_ARRAY); + append_type_mask(return_value, MAY_BE_ARRAY, /* object_ce */ NULL); } if (type_mask & MAY_BE_STRING) { - append_type_mask(return_value, MAY_BE_STRING); + append_type_mask(return_value, MAY_BE_STRING, /* object_ce */ NULL); } if (type_mask & MAY_BE_LONG) { - append_type_mask(return_value, MAY_BE_LONG); + append_type_mask(return_value, MAY_BE_LONG, /* object_ce */ NULL); } if (type_mask & MAY_BE_DOUBLE) { - append_type_mask(return_value, MAY_BE_DOUBLE); + append_type_mask(return_value, MAY_BE_DOUBLE, /* object_ce */ NULL); } if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { - append_type_mask(return_value, MAY_BE_BOOL); + append_type_mask(return_value, MAY_BE_BOOL, /* object_ce */ NULL); } else if (type_mask & MAY_BE_TRUE) { - append_type_mask(return_value, MAY_BE_TRUE); + append_type_mask(return_value, MAY_BE_TRUE, /* object_ce */ NULL); } else if (type_mask & MAY_BE_FALSE) { - append_type_mask(return_value, MAY_BE_FALSE); + append_type_mask(return_value, MAY_BE_FALSE, /* object_ce */ NULL); } if (type_mask & MAY_BE_NULL) { - append_type_mask(return_value, MAY_BE_NULL); + append_type_mask(return_value, MAY_BE_NULL, /* object_ce */ NULL); } } /* }}} */ @@ -3226,7 +3296,7 @@ ZEND_METHOD(ReflectionIntersectionType, getTypes) array_init(return_value); ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { - append_type(return_value, *list_type); + append_type(return_value, *list_type, /* object_ce */ intern->ce); } ZEND_TYPE_LIST_FOREACH_END(); } /* }}} */ @@ -3649,7 +3719,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) RETURN_NULL(); } - reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1); + reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce); } /* }}} */ @@ -3685,7 +3755,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) RETURN_NULL(); } - reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1); + reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce); } /* }}} */ @@ -3906,7 +3976,7 @@ ZEND_METHOD(ReflectionClassConstant, getType) RETURN_NULL(); } - reflection_type_factory(ref->type, return_value, 1); + reflection_type_factory(ref->type, return_value, /* legacy_behavior */ true, intern->ce); } /* Returns whether class constant has a type */ @@ -5926,7 +5996,7 @@ ZEND_METHOD(ReflectionProperty, getType) RETURN_NULL(); } - reflection_type_factory(ref->prop->type, return_value, 1); + reflection_type_factory(ref->prop->type, return_value, /* legacy_behavior */ true, intern->ce); } /* }}} */ @@ -7015,7 +7085,7 @@ ZEND_METHOD(ReflectionEnum, getBackingType) RETURN_NULL(); } else { zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0); - reflection_type_factory(type, return_value, 0); + reflection_type_factory(type, return_value, /* legacy_behavior */ false, /* object_ce */ NULL); } } @@ -7409,6 +7479,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_named_type_ptr->create_object = reflection_objects_new; reflection_named_type_ptr->default_object_handlers = &reflection_object_handlers; + reflection_relative_class_type_ptr = register_class_ReflectionRelativeClassType(reflection_named_type_ptr); + reflection_relative_class_type_ptr->create_object = reflection_objects_new; + reflection_relative_class_type_ptr->default_object_handlers = &reflection_object_handlers; + reflection_union_type_ptr = register_class_ReflectionUnionType(reflection_type_ptr); reflection_union_type_ptr->create_object = reflection_objects_new; reflection_union_type_ptr->default_object_handlers = &reflection_object_handlers; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index 6420b04520aa6..bb1b6771e94e7 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -35,6 +35,7 @@ extern PHPAPI zend_class_entry *reflection_function_ptr; extern PHPAPI zend_class_entry *reflection_parameter_ptr; extern PHPAPI zend_class_entry *reflection_type_ptr; extern PHPAPI zend_class_entry *reflection_named_type_ptr; +extern PHPAPI zend_class_entry *reflection_relative_class_type_ptr; extern PHPAPI zend_class_entry *reflection_class_ptr; extern PHPAPI zend_class_entry *reflection_object_ptr; extern PHPAPI zend_class_entry *reflection_method_ptr; diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index c9ce811bfeeb3..5ffd2b41d0a9b 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -661,6 +661,11 @@ public function getName(): string {} public function isBuiltin(): bool {} } +class ReflectionRelativeClassType extends ReflectionNamedType +{ + public function resolveToNamedType(): ReflectionNamedType {} +} + class ReflectionUnionType extends ReflectionType { public function getTypes(): array {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index e06469db82b3a..17bb35897ae66 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: c81572d388f2539d861df717b3cc8c0491fe72a0 */ + * Stub hash: e0524ce7c6db33c9868e9e70cb947b3754c59032 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -482,6 +482,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionNamedType_isBuiltin arginfo_class_ReflectionFunctionAbstract_inNamespace +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionRelativeClassType_resolveToNamedType, 0, 0, ReflectionNamedType, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_ReflectionUnionType_getTypes arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables #define arginfo_class_ReflectionIntersectionType_getTypes arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables @@ -811,6 +814,7 @@ ZEND_METHOD(ReflectionType, allowsNull); ZEND_METHOD(ReflectionType, __toString); ZEND_METHOD(ReflectionNamedType, getName); ZEND_METHOD(ReflectionNamedType, isBuiltin); +ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType); ZEND_METHOD(ReflectionUnionType, getTypes); ZEND_METHOD(ReflectionIntersectionType, getTypes); ZEND_METHOD(ReflectionExtension, __construct); @@ -1113,6 +1117,11 @@ static const zend_function_entry class_ReflectionNamedType_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_ReflectionRelativeClassType_methods[] = { + ZEND_ME(ReflectionRelativeClassType, resolveToNamedType, arginfo_class_ReflectionRelativeClassType_resolveToNamedType, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_ReflectionUnionType_methods[] = { ZEND_ME(ReflectionUnionType, getTypes, arginfo_class_ReflectionUnionType_getTypes, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -1539,6 +1548,16 @@ static zend_class_entry *register_class_ReflectionNamedType(zend_class_entry *cl return class_entry; } +static zend_class_entry *register_class_ReflectionRelativeClassType(zend_class_entry *class_entry_ReflectionNamedType) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionRelativeClassType", class_ReflectionRelativeClassType_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ReflectionNamedType); + + return class_entry; +} + static zend_class_entry *register_class_ReflectionUnionType(zend_class_entry *class_entry_ReflectionType) { zend_class_entry ce, *class_entry; diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index b7537f170a502..1008c9ea9ab0f 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -54,78 +54,83 @@ array(25) { ["name"]=> string(19) "ReflectionNamedType" } - ["ReflectionUnionType"]=> + ["ReflectionRelativeClassType"]=> object(ReflectionClass)#11 (1) { + ["name"]=> + string(27) "ReflectionRelativeClassType" + } + ["ReflectionUnionType"]=> + object(ReflectionClass)#12 (1) { ["name"]=> string(19) "ReflectionUnionType" } ["ReflectionIntersectionType"]=> - object(ReflectionClass)#12 (1) { + object(ReflectionClass)#13 (1) { ["name"]=> string(26) "ReflectionIntersectionType" } ["ReflectionMethod"]=> - object(ReflectionClass)#13 (1) { + object(ReflectionClass)#14 (1) { ["name"]=> string(16) "ReflectionMethod" } ["ReflectionClass"]=> - object(ReflectionClass)#14 (1) { + object(ReflectionClass)#15 (1) { ["name"]=> string(15) "ReflectionClass" } ["ReflectionObject"]=> - object(ReflectionClass)#15 (1) { + object(ReflectionClass)#16 (1) { ["name"]=> string(16) "ReflectionObject" } ["ReflectionProperty"]=> - object(ReflectionClass)#16 (1) { + object(ReflectionClass)#17 (1) { ["name"]=> string(18) "ReflectionProperty" } ["ReflectionClassConstant"]=> - object(ReflectionClass)#17 (1) { + object(ReflectionClass)#18 (1) { ["name"]=> string(23) "ReflectionClassConstant" } ["ReflectionExtension"]=> - object(ReflectionClass)#18 (1) { + object(ReflectionClass)#19 (1) { ["name"]=> string(19) "ReflectionExtension" } ["ReflectionZendExtension"]=> - object(ReflectionClass)#19 (1) { + object(ReflectionClass)#20 (1) { ["name"]=> string(23) "ReflectionZendExtension" } ["ReflectionReference"]=> - object(ReflectionClass)#20 (1) { + object(ReflectionClass)#21 (1) { ["name"]=> string(19) "ReflectionReference" } ["ReflectionAttribute"]=> - object(ReflectionClass)#21 (1) { + object(ReflectionClass)#22 (1) { ["name"]=> string(19) "ReflectionAttribute" } ["ReflectionEnum"]=> - object(ReflectionClass)#22 (1) { + object(ReflectionClass)#23 (1) { ["name"]=> string(14) "ReflectionEnum" } ["ReflectionEnumUnitCase"]=> - object(ReflectionClass)#23 (1) { + object(ReflectionClass)#24 (1) { ["name"]=> string(22) "ReflectionEnumUnitCase" } ["ReflectionEnumBackedCase"]=> - object(ReflectionClass)#24 (1) { + object(ReflectionClass)#25 (1) { ["name"]=> string(24) "ReflectionEnumBackedCase" } ["ReflectionFiber"]=> - object(ReflectionClass)#25 (1) { + object(ReflectionClass)#26 (1) { ["name"]=> string(15) "ReflectionFiber" } diff --git a/ext/reflection/tests/bug80190.phpt b/ext/reflection/tests/bug80190.phpt index 16f2fbba42729..6e2908f9c435f 100644 --- a/ext/reflection/tests/bug80190.phpt +++ b/ext/reflection/tests/bug80190.phpt @@ -31,13 +31,13 @@ foreach ((new ReflectionClass(C::class))->getMethods() as $method) { print ' $method->getReturnType()->getTypes() returns an array with ' . count($method->getReturnType()->getTypes()) . ' element(s)' . PHP_EOL; print ' type(s) in union: '; - + $types = []; foreach ($method->getReturnType()->getTypes() as $type) { $types[] = get_class($type) . "($type)"; } - + print join(', ', $types) . PHP_EOL; } @@ -47,21 +47,21 @@ foreach ((new ReflectionClass(C::class))->getMethods() as $method) { ?> --EXPECT-- C::a() - $method->getReturnType() returns ReflectionNamedType + $method->getReturnType() returns ReflectionRelativeClassType $method->getReturnType()->__toString() returns self C::b() $method->getReturnType() returns ReflectionUnionType $method->getReturnType()->__toString() returns stdClass|self $method->getReturnType()->getTypes() returns an array with 2 element(s) - type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(self) + type(s) in union: ReflectionNamedType(stdClass), ReflectionRelativeClassType(self) C::c() - $method->getReturnType() returns ReflectionNamedType + $method->getReturnType() returns ReflectionRelativeClassType $method->getReturnType()->__toString() returns static C::d() $method->getReturnType() returns ReflectionUnionType $method->getReturnType()->__toString() returns stdClass|static $method->getReturnType()->getTypes() returns an array with 2 element(s) - type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(static) + type(s) in union: ReflectionNamedType(stdClass), ReflectionRelativeClassType(static) diff --git a/ext/reflection/tests/types/relative_class_types_in_classes.phpt b/ext/reflection/tests/types/relative_class_types_in_classes.phpt new file mode 100644 index 0000000000000..5adb2508cfcc7 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_classes.phpt @@ -0,0 +1,116 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent, static) in classes +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $type = $method->getReturnType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } +} + +?> +--EXPECT-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Return value must be of type B, A returned +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Return value must be of type B, A returned + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Return value must be of type C, A returned + C::ping(): Return value must be of type C, B returned + Method: pong + Type: static + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::pong(): Return value must be of type C, A returned + C::pong(): Return value must be of type C, B returned + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Return value must be of type B, A returned +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Return value must be of type B, A returned + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Return value must be of type C, A returned + C::ping(): Return value must be of type C, B returned + Method: pong + Type: static + Instance of: ReflectionRelativeClassType + Resolved Type: D + Instance of: ReflectionNamedType + C::pong(): Return value must be of type D, A returned + C::pong(): Return value must be of type D, B returned + C::pong(): Return value must be of type D, C returned + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Return value must be of type B, A returned diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters1.phpt b/ext/reflection/tests/types/relative_class_types_in_classes_parameters1.phpt new file mode 100644 index 0000000000000..602f992951034 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_classes_parameters1.phpt @@ -0,0 +1,103 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters2.phpt b/ext/reflection/tests/types/relative_class_types_in_classes_parameters2.phpt new file mode 100644 index 0000000000000..274c791851893 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_classes_parameters2.phpt @@ -0,0 +1,103 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, parameter nullable types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d +Class: C + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d + Method: foo + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d +Class: D + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d + Method: foo + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters3.phpt b/ext/reflection/tests/types/relative_class_types_in_classes_parameters3.phpt new file mode 100644 index 0000000000000..bea4bcfe6217f --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_classes_parameters3.phpt @@ -0,0 +1,110 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, parameter union types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "int" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters4.phpt b/ext/reflection/tests/types/relative_class_types_in_classes_parameters4.phpt new file mode 100644 index 0000000000000..3410a10a2c7e7 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_classes_parameters4.phpt @@ -0,0 +1,110 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, parameter DNF types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "(X&Y)" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_interfaces.phpt b/ext/reflection/tests/types/relative_class_types_in_interfaces.phpt new file mode 100644 index 0000000000000..450947fbb84ce --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_interfaces.phpt @@ -0,0 +1,86 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent, static) in interfaces +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $type = $method->getReturnType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (ReflectionException $e) { + echo $e->getMessage(), PHP_EOL; + } + } +} + +?> +--EXPECT-- +Interface: A +Interface: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType +Interface: C + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + Method: pong + Type: static + Instance of: ReflectionRelativeClassType +Cannot resolve "static" type of an interface + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType +Interface: D + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + Method: pong + Type: static + Instance of: ReflectionRelativeClassType +Cannot resolve "static" type of an interface + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType diff --git a/ext/reflection/tests/types/unresolved_relative_class_types.phpt b/ext/reflection/tests/types/unresolved_relative_class_types.phpt new file mode 100644 index 0000000000000..169f3d2ec3e94 --- /dev/null +++ b/ext/reflection/tests/types/unresolved_relative_class_types.phpt @@ -0,0 +1,81 @@ +--TEST-- +ReflectionTypes of unresolved relative class types (self, parent, static) +--FILE-- +getReturnType(); + echo "\tType: ", $type, PHP_EOL; + echo "\tInstance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (ReflectionException $e) { + echo $e->getMessage(), PHP_EOL; + } +} + +echo PHP_EOL, "Trait:", PHP_EOL; +trait A { + public function bar(object $o): parent { return $o; } + public function ping(object $o): self { return $o; } + public function pong(object $o): static { return $o; } +} + +$rc = new ReflectionClass('A'); +$methods = $rc->getMethods(); +foreach ($methods as $method) { + echo "Method: ", $method->name, PHP_EOL; + $type = $method->getReturnType(); + echo "\tType: ", $type, PHP_EOL; + echo "\tInstance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (ReflectionException $e) { + echo $e->getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +Closure: + Type: self + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a closure +Closure: + Type: parent + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a closure +Closure: + Type: static + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a closure + +Trait: +Method: bar + Type: parent + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a trait +Method: ping + Type: self + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a trait +Method: pong + Type: static + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a trait diff --git a/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt b/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt new file mode 100644 index 0000000000000..3886a4f0c5ddb --- /dev/null +++ b/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt @@ -0,0 +1,91 @@ +--TEST-- +ReflectionTypes of unresolved relative class types (self, parent, static) in union types +--FILE-- +getReturnType(); + foreach ($type->getTypes() as $single_type) { + if (!($single_type instanceof ReflectionRelativeClassType)) { + continue; + } + echo "\tType: ", $single_type, PHP_EOL; + echo "\tInstance of: ", $single_type::class, PHP_EOL; + try { + $resolvedType = $single_type->resolveToNamedType(); + echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (ReflectionException $e) { + echo $e->getMessage(), PHP_EOL; + } + } +} + +echo PHP_EOL, "Trait:", PHP_EOL; +trait A { + public function bar(object $o): X|parent { return $o; } + public function ping(object $o): X|self { return $o; } + public function pong(object $o): X|static { return $o; } +} + +$rc = new ReflectionClass('A'); +$methods = $rc->getMethods(); +foreach ($methods as $method) { + echo "Method: ", $method->name, PHP_EOL; + $type = $method->getReturnType(); + foreach ($type->getTypes() as $single_type) { + if (!($single_type instanceof ReflectionRelativeClassType)) { + continue; + } + echo "\tType: ", $single_type, PHP_EOL; + echo "\tInstance of: ", $single_type::class, PHP_EOL; + try { + $resolvedType = $single_type->resolveToNamedType(); + echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (ReflectionException $e) { + echo $e->getMessage(), PHP_EOL; + } + } +} + +?> +--EXPECT-- +Closure: + Type: self + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a closure +Closure: + Type: parent + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a closure +Closure: + Type: static + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a closure + +Trait: +Method: bar + Type: parent + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a trait +Method: ping + Type: self + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a trait +Method: pong + Type: static + Instance of: ReflectionRelativeClassType +Cannot resolve relative class name for a trait From f640603af7bdafb22455a5d9319b9b91ae77b6e5 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Sun, 18 Jun 2023 15:55:42 +0100 Subject: [PATCH 04/24] Resolve trait relative class types when added to a class Also throw a compile error if trying to use a trait using the parent type in a class not having a parent --- Zend/tests/{ => traits}/bug80055.phpt | 0 .../trait_parent_type_in_class_no_parent.phpt | 14 +++ ...nt_type_in_class_no_parent_variation2.phpt | 14 +++ ...nt_type_in_class_no_parent_variation3.phpt | 14 +++ ...nt_type_in_class_no_parent_variation4.phpt | 14 +++ Zend/zend_inheritance.c | 99 +++++++++++++++++ ..._types_in_classes_variadic_parameter1.phpt | 103 ++++++++++++++++++ ..._class_types_in_trait_used_in_classes.phpt | 101 +++++++++++++++++ ...s_in_trait_used_in_classes_parameters.phpt | 90 +++++++++++++++ ..._in_trait_used_in_classes_parameters2.phpt | 88 +++++++++++++++ ..._in_trait_used_in_classes_parameters3.phpt | 95 ++++++++++++++++ ..._in_trait_used_in_classes_parameters4.phpt | 95 ++++++++++++++++ 12 files changed, 727 insertions(+) rename Zend/tests/{ => traits}/bug80055.phpt (100%) create mode 100644 Zend/tests/traits/trait_parent_type_in_class_no_parent.phpt create mode 100644 Zend/tests/traits/trait_parent_type_in_class_no_parent_variation2.phpt create mode 100644 Zend/tests/traits/trait_parent_type_in_class_no_parent_variation3.phpt create mode 100644 Zend/tests/traits/trait_parent_type_in_class_no_parent_variation4.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters2.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters3.phpt create mode 100644 ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters4.phpt diff --git a/Zend/tests/bug80055.phpt b/Zend/tests/traits/bug80055.phpt similarity index 100% rename from Zend/tests/bug80055.phpt rename to Zend/tests/traits/bug80055.phpt diff --git a/Zend/tests/traits/trait_parent_type_in_class_no_parent.phpt b/Zend/tests/traits/trait_parent_type_in_class_no_parent.phpt new file mode 100644 index 0000000000000..cd620d81ae991 --- /dev/null +++ b/Zend/tests/traits/trait_parent_type_in_class_no_parent.phpt @@ -0,0 +1,14 @@ +--TEST-- +Cannot use a trait which references parent as a type in a class with no parent, single type +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d diff --git a/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation2.phpt b/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation2.phpt new file mode 100644 index 0000000000000..c20dc635167d5 --- /dev/null +++ b/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Cannot use a trait which references parent as a type in a class with no parent, nullable type +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d diff --git a/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation3.phpt b/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation3.phpt new file mode 100644 index 0000000000000..e83fb946bca4a --- /dev/null +++ b/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation3.phpt @@ -0,0 +1,14 @@ +--TEST-- +Cannot use a trait which references parent as a type in a class with no parent, union type +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d diff --git a/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation4.phpt b/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation4.phpt new file mode 100644 index 0000000000000..d21294427267b --- /dev/null +++ b/Zend/tests/traits/trait_parent_type_in_class_no_parent_variation4.phpt @@ -0,0 +1,14 @@ +--TEST-- +Cannot use a trait which references parent as a type in a class with no parent, DNF type +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use trait which has "parent" as a type when current class scope has no parent in %s on line %d diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 491d68714b3c2..b6c536e05431f 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1946,6 +1946,102 @@ static zend_class_entry *fixup_trait_scope(const zend_function *fn, zend_class_e return fn->common.scope->ce_flags & ZEND_ACC_TRAIT ? ce : fn->common.scope; } +/* We cannot modify the type in-place (e.g. via a pointer) as it is written to SHM */ +static zend_type zend_resolve_single_type(zend_type type, const zend_class_entry *const ce) +{ + /* Only built-in types + static */ + if (!ZEND_TYPE_IS_COMPLEX(type)) { + return type; + } + + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type) || (ZEND_TYPE_HAS_LIST(type))); + if (ZEND_TYPE_HAS_NAME(type)) { + if (ZEND_TYPE_IS_RELATIVE_SELF(type)) { + zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); + return resolved_type; + } else if (ZEND_TYPE_IS_RELATIVE_PARENT(type)) { + if (!ce->parent) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); + return (zend_type) ZEND_TYPE_INIT_NONE(0); + } + zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->parent->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); + return resolved_type; + } else { + return type; + } + } + + /* Intersection types cannot have relative class_types */ + if (ZEND_TYPE_IS_INTERSECTION(type)) { + return type; + } + + const zend_type_list *union_type_list = ZEND_TYPE_LIST(type); + zend_type *single_type; + + /* TODO Only do allocation if need to resolve types, as type is stored in SHM */ + zend_type_list *new_union_type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(union_type_list->num_types)); + memcpy(new_union_type_list, union_type_list, ZEND_TYPE_LIST_SIZE(union_type_list->num_types)); + + ZEND_TYPE_LIST_FOREACH(new_union_type_list, single_type) { + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type) || (ZEND_TYPE_IS_INTERSECTION(*single_type))); + + /* Intersections types cannot have self or parent */ + if (ZEND_TYPE_IS_INTERSECTION(*single_type)) { + continue; + } + if (ZEND_TYPE_IS_RELATIVE_SELF(*single_type)) { + zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(*single_type)); + memmove(single_type, &resolved_type, sizeof(zend_type)); + } + if (ZEND_TYPE_IS_RELATIVE_PARENT(*single_type)) { + if (!ce->parent) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); + return (zend_type) ZEND_TYPE_INIT_NONE(0); + } + zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->parent->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(*single_type)); + memmove(single_type, &resolved_type, sizeof(zend_type)); + } + } ZEND_TYPE_LIST_FOREACH_END(); + + zend_type new_type = (zend_type) ZEND_TYPE_INIT_NONE(0); + ZEND_TYPE_SET_LIST(new_type, new_union_type_list); + ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_ARENA_BIT; + /* Inform that the type list is a union type */ + ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_UNION_BIT; + return new_type; +} + +static void zend_resolve_trait_relative_class_types(zend_function *const fn, const zend_class_entry *const ce) +{ + /* We are adding trait methods to another trait, delay resolution */ + if (ce->ce_flags & ZEND_ACC_TRAIT) { + return; + } + /* No type info */ + if (!fn->common.arg_info) { + return; + } + + bool has_return_type = fn->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE; + /* Variadic parameters are not counted as part of the standard number of arguments */ + bool has_variadic_type = fn->common.fn_flags & ZEND_ACC_VARIADIC; + uint32_t num_args = fn->common.num_args + has_variadic_type; + /* TODO Only do allocation if need to resolve types, as arg_info is stored in SHM */ + size_t allocated_size = sizeof(zend_arg_info) * (has_return_type + num_args); + + zend_arg_info *new_arg_infos = zend_arena_alloc(&CG(arena), allocated_size); + memcpy(new_arg_infos, fn->common.arg_info - has_return_type, allocated_size); + fn->common.arg_info = new_arg_infos + has_return_type; + + for (uint32_t i = 0; i < num_args + has_return_type; i++) { + zend_type type = new_arg_infos[i].type; + new_arg_infos[i].type = zend_resolve_single_type(type, ce); + } +} + static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_string *key, zend_function *fn) /* {{{ */ { zend_function *existing_fn = NULL; @@ -1992,10 +2088,13 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ if (UNEXPECTED(fn->type == ZEND_INTERNAL_FUNCTION)) { new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_internal_function)); memcpy(new_fn, fn, sizeof(zend_internal_function)); + zend_resolve_trait_relative_class_types(new_fn, ce); new_fn->common.fn_flags |= ZEND_ACC_ARENA_ALLOCATED; } else { new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_fn, fn, sizeof(zend_op_array)); + zend_resolve_trait_relative_class_types(new_fn, ce); + new_fn->op_array.fn_flags |= ZEND_ACC_TRAIT_CLONE; new_fn->op_array.fn_flags &= ~ZEND_ACC_IMMUTABLE; } new_fn->common.fn_flags |= ZEND_ACC_TRAIT_CLONE; diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt b/ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt new file mode 100644 index 0000000000000..74b94fbfe918c --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt @@ -0,0 +1,103 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 must be of type B, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 must be of type B, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes.phpt b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes.phpt new file mode 100644 index 0000000000000..e3cf26aeb72aa --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes.phpt @@ -0,0 +1,101 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent, static) from traits in classes, return types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $type = $method->getReturnType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } +} + +?> +--EXPECT-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Return value must be of type B, A returned + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Return value must be of type C, A returned + C::ping(): Return value must be of type C, B returned + Method: pong + Type: static + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::pong(): Return value must be of type C, A returned + C::pong(): Return value must be of type C, B returned +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Return value must be of type B, A returned + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Return value must be of type C, A returned + C::ping(): Return value must be of type C, B returned + Method: pong + Type: static + Instance of: ReflectionRelativeClassType + Resolved Type: D + Instance of: ReflectionNamedType + C::pong(): Return value must be of type D, A returned + C::pong(): Return value must be of type D, B returned + C::pong(): Return value must be of type D, C returned diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters.phpt b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters.phpt new file mode 100644 index 0000000000000..ab50cfb212d84 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters.phpt @@ -0,0 +1,90 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters2.phpt b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters2.phpt new file mode 100644 index 0000000000000..4142be2590af1 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters2.phpt @@ -0,0 +1,88 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, parameter nullable types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d +Class: D + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters3.phpt b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters3.phpt new file mode 100644 index 0000000000000..2e7cc819f95ed --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters3.phpt @@ -0,0 +1,95 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, parameter union types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "int" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters4.phpt b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters4.phpt new file mode 100644 index 0000000000000..17144bfdce977 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters4.phpt @@ -0,0 +1,95 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, parameter DNF types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "(X&Y)" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d From 9894438e454ad7fd38b354713378c785dcb05181 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Sat, 24 Jun 2023 16:34:49 +0100 Subject: [PATCH 05/24] More tests --- .../classes_by_ref_parameters1_simple.phpt | 103 ++++++++++++++++ .../classes_by_ref_parameters2_nullable.phpt | 103 ++++++++++++++++ .../classes_by_ref_parameters3_union.phpt | 110 ++++++++++++++++++ .../classes_by_ref_parameters4_dnf.phpt | 110 ++++++++++++++++++ .../classes_parameters1_simple.phpt} | 0 .../classes_parameters2_nullable.phpt} | 0 .../classes_parameters3_union.phpt} | 0 .../classes_parameters4_dnf.phpt} | 0 .../classes_return_type.phpt} | 0 .../classes_variadic_parameter1_simple.phpt} | 8 +- .../interfaces_return_type.phpt} | 0 ..._in_classes_by_ref_parameters1_simple.phpt | 88 ++++++++++++++ ...n_classes_by_ref_parameters2_nullable.phpt | 88 ++++++++++++++ ...d_in_classes_by_ref_parameters3_union.phpt | 95 +++++++++++++++ ...sed_in_classes_by_ref_parameters4_dnf.phpt | 95 +++++++++++++++ ...t_used_in_classes_parameters1_simple.phpt} | 0 ...used_in_classes_parameters2_nullable.phpt} | 0 ...it_used_in_classes_parameters3_union.phpt} | 0 ...rait_used_in_classes_parameters4_dnf.phpt} | 0 .../trait_used_in_classes_return_type.phpt} | 0 ...in_classes_variadic_parameter1_simple.phpt | 88 ++++++++++++++ ...classes_variadic_parameters2_nullable.phpt | 88 ++++++++++++++ ...in_classes_variadic_parameters3_union.phpt | 95 +++++++++++++++ ...d_in_classes_variadic_parameters4_dnf.phpt | 95 +++++++++++++++ 24 files changed, 1162 insertions(+), 4 deletions(-) create mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt rename ext/reflection/tests/types/{relative_class_types_in_classes_parameters1.phpt => relative_class_types/classes_parameters1_simple.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_classes_parameters2.phpt => relative_class_types/classes_parameters2_nullable.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_classes_parameters3.phpt => relative_class_types/classes_parameters3_union.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_classes_parameters4.phpt => relative_class_types/classes_parameters4_dnf.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_classes.phpt => relative_class_types/classes_return_type.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_classes_variadic_parameter1.phpt => relative_class_types/classes_variadic_parameter1_simple.phpt} (93%) rename ext/reflection/tests/types/{relative_class_types_in_interfaces.phpt => relative_class_types/interfaces_return_type.phpt} (100%) create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt rename ext/reflection/tests/types/{relative_class_types_in_trait_used_in_classes_parameters.phpt => relative_class_types/trait_used_in_classes_parameters1_simple.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_trait_used_in_classes_parameters2.phpt => relative_class_types/trait_used_in_classes_parameters2_nullable.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_trait_used_in_classes_parameters3.phpt => relative_class_types/trait_used_in_classes_parameters3_union.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_trait_used_in_classes_parameters4.phpt => relative_class_types/trait_used_in_classes_parameters4_dnf.phpt} (100%) rename ext/reflection/tests/types/{relative_class_types_in_trait_used_in_classes.phpt => relative_class_types/trait_used_in_classes_return_type.phpt} (100%) create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt new file mode 100644 index 0000000000000..32dbb9574f54d --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt @@ -0,0 +1,103 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt new file mode 100644 index 0000000000000..31b7cd01813e1 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt @@ -0,0 +1,103 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter nullable types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d +Class: C + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d + Method: foo + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d +Class: D + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d + Method: foo + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt new file mode 100644 index 0000000000000..cc75ae40840bb --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt @@ -0,0 +1,110 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter union types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "int" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt new file mode 100644 index 0000000000000..106ad01b8deb3 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt @@ -0,0 +1,110 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter union types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "(X&Y)" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d + Method: foo + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters1.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_classes_parameters1.phpt rename to ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters2.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_classes_parameters2.phpt rename to ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters3.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_classes_parameters3.phpt rename to ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_parameters4.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_classes_parameters4.phpt rename to ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_classes.phpt b/ext/reflection/tests/types/relative_class_types/classes_return_type.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_classes.phpt rename to ext/reflection/tests/types/relative_class_types/classes_return_type.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt b/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt similarity index 93% rename from ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt rename to ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt index 74b94fbfe918c..81f486f23975d 100644 --- a/ext/reflection/tests/types/relative_class_types_in_classes_variadic_parameter1.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt @@ -1,5 +1,5 @@ --TEST-- -ReflectionTypes of relative class types (self, parent) in classes, parameter types +ReflectionTypes of relative class types (self, parent) in classes, variadic parameter types --FILE-- getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt new file mode 100644 index 0000000000000..590276caa4cfd --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt @@ -0,0 +1,88 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter nullable types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d +Class: D + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt new file mode 100644 index 0000000000000..15218030faf49 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt @@ -0,0 +1,95 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter union types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "int" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt new file mode 100644 index 0000000000000..2e303f15ae79d --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt @@ -0,0 +1,95 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter DNF types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "(X&Y)" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters.phpt rename to ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters2.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters2.phpt rename to ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters3.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters3.phpt rename to ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters4.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes_parameters4.phpt rename to ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt diff --git a/ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_return_type.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types_in_trait_used_in_classes.phpt rename to ext/reflection/tests/types/relative_class_types/trait_used_in_classes_return_type.phpt diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt new file mode 100644 index 0000000000000..5181047cf1903 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt @@ -0,0 +1,88 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt new file mode 100644 index 0000000000000..62bf3a2a1cf97 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt @@ -0,0 +1,88 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter nullable types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type ?C, B given, called in %s on line %d +Class: D + Method: bar + Type: ?parent + Instance of: ReflectionRelativeClassType + Resolved Type: ?B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type ?B, A given, called in %s on line %d + Method: ping + Type: ?self + Instance of: ReflectionRelativeClassType + Resolved Type: ?C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type ?C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt new file mode 100644 index 0000000000000..5d47c2cd51614 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt @@ -0,0 +1,95 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter union types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "int" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 must be of type C|int, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type B|int, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type C|int, A given, called in %s on line %d + C::ping(): Argument #1 must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt new file mode 100644 index 0000000000000..7e9c9ac520ff5 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt @@ -0,0 +1,95 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $unionType = $param->getType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "(X&Y)" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type (X&Y)|C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 must be of type (X&Y)|B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 must be of type (X&Y)|C, A given, called in %s on line %d + C::ping(): Argument #1 must be of type (X&Y)|C, B given, called in %s on line %d From 18c6501be673e733c18398d5f25215339b9388bf Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Sat, 24 Jun 2023 17:28:25 +0100 Subject: [PATCH 06/24] Only allocate new arg_infos if types are being resolved --- Zend/zend_inheritance.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index b6c536e05431f..7969a6f63a660 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2029,16 +2029,23 @@ static void zend_resolve_trait_relative_class_types(zend_function *const fn, con /* Variadic parameters are not counted as part of the standard number of arguments */ bool has_variadic_type = fn->common.fn_flags & ZEND_ACC_VARIADIC; uint32_t num_args = fn->common.num_args + has_variadic_type; - /* TODO Only do allocation if need to resolve types, as arg_info is stored in SHM */ size_t allocated_size = sizeof(zend_arg_info) * (has_return_type + num_args); - zend_arg_info *new_arg_infos = zend_arena_alloc(&CG(arena), allocated_size); - memcpy(new_arg_infos, fn->common.arg_info - has_return_type, allocated_size); - fn->common.arg_info = new_arg_infos + has_return_type; + zend_arg_info *new_arg_infos = fn->common.arg_info - has_return_type; + bool has_resolved_type = false; for (uint32_t i = 0; i < num_args + has_return_type; i++) { zend_type type = new_arg_infos[i].type; - new_arg_infos[i].type = zend_resolve_single_type(type, ce); + zend_type resolved_type = zend_resolve_single_type(type, ce); + if (type.ptr != resolved_type.ptr) { + if (!has_resolved_type) { + new_arg_infos = zend_arena_alloc(&CG(arena), allocated_size); + memcpy(new_arg_infos, fn->common.arg_info - has_return_type, allocated_size); + fn->common.arg_info = new_arg_infos + has_return_type; + has_resolved_type = true; + } + new_arg_infos[i].type = resolved_type; + } } } From 560b1d648129d3dc2fc62c80684c378ebf0897e1 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Sat, 24 Jun 2023 17:52:00 +0100 Subject: [PATCH 07/24] Only allocate new zend_type_list if types are being resolved --- Zend/zend_inheritance.c | 96 +++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 7969a6f63a660..7945cefdaef73 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1946,6 +1946,31 @@ static zend_class_entry *fixup_trait_scope(const zend_function *fn, zend_class_e return fn->common.scope->ce_flags & ZEND_ACC_TRAIT ? ce : fn->common.scope; } +/* If the type was resolved then either the zend_string pointer is different, or the zend_type_list is */ +static inline bool zend_was_type_resolved(zend_type original_type, zend_type resolved_type) +{ + return original_type.ptr != resolved_type.ptr; +} + +static zend_type zend_resolve_name_type(zend_type type, const zend_class_entry *const ce) +{ + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type)); + if (ZEND_TYPE_IS_RELATIVE_SELF(type)) { + zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); + return resolved_type; + } else if (ZEND_TYPE_IS_RELATIVE_PARENT(type)) { + if (!ce->parent) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); + return (zend_type) ZEND_TYPE_INIT_NONE(0); + } + zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->parent->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); + return resolved_type; + } else { + return type; + } +} + /* We cannot modify the type in-place (e.g. via a pointer) as it is written to SHM */ static zend_type zend_resolve_single_type(zend_type type, const zend_class_entry *const ce) { @@ -1956,20 +1981,7 @@ static zend_type zend_resolve_single_type(zend_type type, const zend_class_entry ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type) || (ZEND_TYPE_HAS_LIST(type))); if (ZEND_TYPE_HAS_NAME(type)) { - if (ZEND_TYPE_IS_RELATIVE_SELF(type)) { - zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); - return resolved_type; - } else if (ZEND_TYPE_IS_RELATIVE_PARENT(type)) { - if (!ce->parent) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); - return (zend_type) ZEND_TYPE_INIT_NONE(0); - } - zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->parent->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); - return resolved_type; - } else { - return type; - } + return zend_resolve_name_type(type, ce); } /* Intersection types cannot have relative class_types */ @@ -1977,41 +1989,41 @@ static zend_type zend_resolve_single_type(zend_type type, const zend_class_entry return type; } - const zend_type_list *union_type_list = ZEND_TYPE_LIST(type); - zend_type *single_type; - - /* TODO Only do allocation if need to resolve types, as type is stored in SHM */ - zend_type_list *new_union_type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(union_type_list->num_types)); - memcpy(new_union_type_list, union_type_list, ZEND_TYPE_LIST_SIZE(union_type_list->num_types)); + zend_type_list *union_type_list = ZEND_TYPE_LIST(type); + bool has_resolved_type = false; + /* We don't use ZEND_TYPE_LIST_FOREACH() as we need to keep track of the array index */ + for (uint32_t i = 0; i < union_type_list->num_types; i++) { + zend_type single_type = union_type_list->types[i]; - ZEND_TYPE_LIST_FOREACH(new_union_type_list, single_type) { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type) || (ZEND_TYPE_IS_INTERSECTION(*single_type))); + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(single_type) || (ZEND_TYPE_IS_INTERSECTION(single_type))); /* Intersections types cannot have self or parent */ - if (ZEND_TYPE_IS_INTERSECTION(*single_type)) { + if (ZEND_TYPE_IS_INTERSECTION(single_type)) { continue; } - if (ZEND_TYPE_IS_RELATIVE_SELF(*single_type)) { - zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(*single_type)); - memmove(single_type, &resolved_type, sizeof(zend_type)); - } - if (ZEND_TYPE_IS_RELATIVE_PARENT(*single_type)) { - if (!ce->parent) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); - return (zend_type) ZEND_TYPE_INIT_NONE(0); + + zend_type resolved_type = zend_resolve_name_type(single_type, ce); + if (zend_was_type_resolved(type, resolved_type)) { + if (!has_resolved_type) { + const zend_type_list *old_union_type_list = ZEND_TYPE_LIST(type); + union_type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(old_union_type_list->num_types)); + memcpy(union_type_list, old_union_type_list, ZEND_TYPE_LIST_SIZE(old_union_type_list->num_types)); + has_resolved_type = true; } - zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->parent->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(*single_type)); - memmove(single_type, &resolved_type, sizeof(zend_type)); + union_type_list->types[i] = resolved_type; } - } ZEND_TYPE_LIST_FOREACH_END(); + } - zend_type new_type = (zend_type) ZEND_TYPE_INIT_NONE(0); - ZEND_TYPE_SET_LIST(new_type, new_union_type_list); - ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_ARENA_BIT; - /* Inform that the type list is a union type */ - ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_UNION_BIT; - return new_type; + if (has_resolved_type) { + zend_type new_type = (zend_type) ZEND_TYPE_INIT_NONE(0); + ZEND_TYPE_SET_LIST(new_type, union_type_list); + ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_ARENA_BIT; + /* Inform that the type list is a union type */ + ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_UNION_BIT; + return new_type; + } else { + return type; + } } static void zend_resolve_trait_relative_class_types(zend_function *const fn, const zend_class_entry *const ce) @@ -2037,7 +2049,7 @@ static void zend_resolve_trait_relative_class_types(zend_function *const fn, con for (uint32_t i = 0; i < num_args + has_return_type; i++) { zend_type type = new_arg_infos[i].type; zend_type resolved_type = zend_resolve_single_type(type, ce); - if (type.ptr != resolved_type.ptr) { + if (zend_was_type_resolved(type, resolved_type)) { if (!has_resolved_type) { new_arg_infos = zend_arena_alloc(&CG(arena), allocated_size); memcpy(new_arg_infos, fn->common.arg_info - has_return_type, allocated_size); From 08648dddf29580316b1475a277c0e366bbcfa13e Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Tue, 27 Jun 2023 18:40:14 +0100 Subject: [PATCH 08/24] Address minor review comments --- Zend/zend_compile.c | 12 +++--------- Zend/zend_inheritance.c | 2 +- Zend/zend_types.h | 3 +++ ext/reflection/php_reflection.c | 9 ++++----- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index aa1c3477fa9f8..e9ec29b820e86 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6791,14 +6791,11 @@ static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list } if (zend_string_equals_ci(ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(type))) { zend_string *single_type_str = zend_type_to_string(type); - if ( - ZEND_TYPE_IS_RELATIVE_SELF(type) - || ZEND_TYPE_IS_RELATIVE_PARENT(type) - ) { + if (ZEND_TYPE_IS_RELATIVE_TYPE(type)) { if ( ( ZEND_TYPE_FULL_MASK(type) & ZEND_TYPE_FULL_MASK(type_list->types[i]) - & (_ZEND_TYPE_SELF_BIT|_ZEND_TYPE_PARENT_BIT)) != 0 + & (_ZEND_TYPE_RELATIVE_TYPE_MASK)) != 0 ) { zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); } @@ -6807,10 +6804,7 @@ static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list zend_error_noreturn(E_COMPILE_ERROR, "%s resolves to %s which is redundant", ZSTR_VAL(single_type_str), ZSTR_VAL(ZEND_TYPE_NAME(type)) ); - } else if ( - ZEND_TYPE_IS_RELATIVE_SELF(type_list->types[i]) - || ZEND_TYPE_IS_RELATIVE_PARENT(type_list->types[i]) - ) { + } else if (ZEND_TYPE_IS_RELATIVE_TYPE(type_list->types[i])) { /* zend_type_to_string() will return "self" or "parent" where the resolved type is stored in * ZEND_TYPE_NAME() */ zend_error_noreturn(E_COMPILE_ERROR, "%s resolves to %s which is redundant", diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 7945cefdaef73..bc2c12bb941c5 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2003,7 +2003,7 @@ static zend_type zend_resolve_single_type(zend_type type, const zend_class_entry } zend_type resolved_type = zend_resolve_name_type(single_type, ce); - if (zend_was_type_resolved(type, resolved_type)) { + if (zend_was_type_resolved(single_type, resolved_type)) { if (!has_resolved_type) { const zend_type_list *old_union_type_list = ZEND_TYPE_LIST(type); union_type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(old_union_type_list->num_types)); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index dc727cece655b..fe0ba7df03b9d 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -171,6 +171,9 @@ typedef struct { (((t).type_mask & _ZEND_TYPE_MASK) != 0) +/* To determine if the type resolved type was written with "self" or "parent" */ +#define ZEND_TYPE_IS_RELATIVE_TYPE(t) \ + ((((t).type_mask) & _ZEND_TYPE_RELATIVE_TYPE_MASK) != 0) /* To determine if the type resolved type was written with "self" */ #define ZEND_TYPE_IS_RELATIVE_SELF(t) \ ((((t).type_mask) & _ZEND_TYPE_SELF_BIT) != 0) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 6414e3da516d8..396643ad6c578 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1367,9 +1367,9 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje typedef enum { NAMED_TYPE = 0, - RELATIVE_TYPE = 3, UNION_TYPE = 1, - INTERSECTION_TYPE = 2 + INTERSECTION_TYPE = 2, + RELATIVE_TYPE = 3 } reflection_type_kind; /* For backwards compatibility reasons, we need to return T|null style unions @@ -1397,7 +1397,7 @@ static reflection_type_kind get_type_kind(zend_type type) { } ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type)); - if (ZEND_TYPE_IS_RELATIVE_SELF(type) || ZEND_TYPE_IS_RELATIVE_PARENT(type)) { + if (ZEND_TYPE_IS_RELATIVE_TYPE(type)) { return RELATIVE_TYPE; } return NAMED_TYPE; @@ -1410,7 +1410,6 @@ static reflection_type_kind get_type_kind(zend_type type) { return UNION_TYPE; } - /* "static" is a relative type */ if (type_mask_without_null == MAY_BE_STATIC) { return RELATIVE_TYPE; } @@ -3189,7 +3188,7 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) } resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /*extra flags */ 0); } else { - ZEND_ASSERT(ZEND_TYPE_IS_RELATIVE_SELF(param->type) || ZEND_TYPE_IS_RELATIVE_PARENT(param->type)); + ZEND_ASSERT(ZEND_TYPE_IS_RELATIVE_TYPE(param->type)); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /*extra flags */ 0); } From ebfb5afbfff064e77fe9d17cba603c22c679a63c Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Tue, 27 Jun 2023 18:53:01 +0100 Subject: [PATCH 09/24] Drop return for engine compiler error bail --- Zend/zend_inheritance.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index bc2c12bb941c5..b279e6c8d9a33 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1962,7 +1962,6 @@ static zend_type zend_resolve_name_type(zend_type type, const zend_class_entry * if (!ce->parent) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); - return (zend_type) ZEND_TYPE_INIT_NONE(0); } zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->parent->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); return resolved_type; From c9a39aa13cf87bf0458d215a777a8b436bebcaa5 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Tue, 25 Jul 2023 10:32:42 +0100 Subject: [PATCH 10/24] Change function name --- Zend/zend_inheritance.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index b279e6c8d9a33..46fa34602856a 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1971,7 +1971,7 @@ static zend_type zend_resolve_name_type(zend_type type, const zend_class_entry * } /* We cannot modify the type in-place (e.g. via a pointer) as it is written to SHM */ -static zend_type zend_resolve_single_type(zend_type type, const zend_class_entry *const ce) +static zend_type zend_resolve_type(zend_type type, const zend_class_entry *const ce) { /* Only built-in types + static */ if (!ZEND_TYPE_IS_COMPLEX(type)) { @@ -2047,7 +2047,7 @@ static void zend_resolve_trait_relative_class_types(zend_function *const fn, con for (uint32_t i = 0; i < num_args + has_return_type; i++) { zend_type type = new_arg_infos[i].type; - zend_type resolved_type = zend_resolve_single_type(type, ce); + zend_type resolved_type = zend_resolve_type(type, ce); if (zend_was_type_resolved(type, resolved_type)) { if (!has_resolved_type) { new_arg_infos = zend_arena_alloc(&CG(arena), allocated_size); From 274f10b0cfb205244221d0764df9e10af13755ac Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Tue, 25 Jul 2023 10:38:24 +0100 Subject: [PATCH 11/24] Add eval test --- ...it_used_in_classes_parameters1_simple.phpt | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt diff --git a/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt new file mode 100644 index 0000000000000..fcd0a9ece0361 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt @@ -0,0 +1,94 @@ +--TEST-- +eval code: ReflectionTypes of relative class types (self, parent) from traits in classes, parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} +CODE; + +eval($code); + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d From 8969b8d4532ff104af3f63f7b30959864f8b3af4 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Tue, 25 Jul 2023 10:46:28 +0100 Subject: [PATCH 12/24] Add test in multiple distinct unrelated classes --- ..._used_in_2_classes_parameters1_simple.phpt | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt new file mode 100644 index 0000000000000..df5b4e4aa88ef --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt @@ -0,0 +1,169 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in 2 different unrelated classes, parameter types +--FILE-- +getMethods(); + foreach ($methods as $method) { + echo "\tMethod: ", $method->name, PHP_EOL; + $parameters = $method->getParameters(); + foreach ($parameters as $param) { + $type = $param->getType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; + } + } + } + } +} + +?> +--EXPECTF-- +Class: A +Class: B +Class: C + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, A2 given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, B2 given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, C2 given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, D2 given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, A2 given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B2 given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, C2 given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, D2 given, called in %s on line %d +Class: D + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType + C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, A2 given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, B2 given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, C2 given, called in %s on line %d + C::bar(): Argument #1 ($o) must be of type B, D2 given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType + C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, A2 given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, B2 given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, C2 given, called in %s on line %d + C::ping(): Argument #1 ($o) must be of type C, D2 given, called in %s on line %d +Class: A2 +Class: B2 +Class: C2 + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B2 + Instance of: ReflectionNamedType + C2::bar(): Argument #1 ($o) must be of type B2, A given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, B given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, C given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, D given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, A2 given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C2 + Instance of: ReflectionNamedType + C2::ping(): Argument #1 ($o) must be of type C2, A given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, B given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, C given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, D given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, A2 given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, B2 given, called in %s on line %d +Class: D2 + Method: bar + Type: parent + Instance of: ReflectionRelativeClassType + Resolved Type: B2 + Instance of: ReflectionNamedType + C2::bar(): Argument #1 ($o) must be of type B2, A given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, B given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, C given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, D given, called in %s on line %d + C2::bar(): Argument #1 ($o) must be of type B2, A2 given, called in %s on line %d + Method: ping + Type: self + Instance of: ReflectionRelativeClassType + Resolved Type: C2 + Instance of: ReflectionNamedType + C2::ping(): Argument #1 ($o) must be of type C2, A given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, B given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, C given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, D given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, A2 given, called in %s on line %d + C2::ping(): Argument #1 ($o) must be of type C2, B2 given, called in %s on line %d From 6c4f630596f6650ff1f971c839fb35f248a20b01 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 28 Feb 2024 18:03:53 +0000 Subject: [PATCH 13/24] Do not preserve BC for self/parent and always resolve when possible --- ...alid_unresolved_relative_type_parent.phpt} | 6 +- ...nvalid_unresolved_relative_type_self.phpt} | 4 +- .../resolved_relativre_type_valid.phpt | 19 ++++ .../duplicate_relative_class_parent_type.phpt | 2 +- .../duplicate_relative_class_self_type.phpt | 2 +- ...solved_relative_class_type_variation1.phpt | 2 +- ...solved_relative_class_type_variation2.phpt | 2 +- ...solved_relative_class_type_variation3.phpt | 2 +- ...solved_relative_class_type_variation4.phpt | 2 +- Zend/zend_compile.c | 69 +++----------- Zend/zend_inheritance.c | 4 +- Zend/zend_types.h | 19 +--- ext/reflection/php_reflection.c | 26 +++--- ext/reflection/tests/bug80190.phpt | 8 +- .../tests/types/ReflectionType_001.phpt | 8 +- .../classes_by_ref_parameters1_simple.phpt | 45 +++------- .../classes_by_ref_parameters2_nullable.phpt | 45 +++------- .../classes_by_ref_parameters3_union.phpt | 47 ++++------ .../classes_by_ref_parameters4_dnf.phpt | 47 ++++------ .../classes_parameters1_simple.phpt | 45 +++------- .../classes_parameters2_nullable.phpt | 45 +++------- .../classes_parameters3_union.phpt | 90 ++++++++----------- .../classes_parameters4_dnf.phpt | 47 ++++------ .../classes_return_type.phpt | 50 ++++------- .../classes_variadic_parameter1_simple.phpt | 45 +++------- ...it_used_in_classes_parameters1_simple.phpt | 27 ++---- .../interfaces_return_type.phpt | 46 ++++------ ..._used_in_2_classes_parameters1_simple.phpt | 51 ++++------- ..._in_classes_by_ref_parameters1_simple.phpt | 27 ++---- ...n_classes_by_ref_parameters2_nullable.phpt | 27 ++---- ...d_in_classes_by_ref_parameters3_union.phpt | 29 ++---- ...sed_in_classes_by_ref_parameters4_dnf.phpt | 29 ++---- ...it_used_in_classes_parameters1_simple.phpt | 27 ++---- ..._used_in_classes_parameters2_nullable.phpt | 27 ++---- ...ait_used_in_classes_parameters3_union.phpt | 29 ++---- ...trait_used_in_classes_parameters4_dnf.phpt | 29 ++---- .../trait_used_in_classes_return_type.phpt | 32 +++---- ...in_classes_variadic_parameter1_simple.phpt | 27 ++---- ...classes_variadic_parameters2_nullable.phpt | 27 ++---- ...in_classes_variadic_parameters3_union.phpt | 29 ++---- ...d_in_classes_variadic_parameters4_dnf.phpt | 29 ++---- 41 files changed, 394 insertions(+), 779 deletions(-) rename Zend/tests/type_declarations/intersection_types/invalid_types/{invalid_parent_type.phpt => invalid_unresolved_relative_type_parent.phpt} (66%) rename Zend/tests/type_declarations/intersection_types/invalid_types/{invalid_self_type.phpt => invalid_unresolved_relative_type_self.phpt} (68%) create mode 100644 Zend/tests/type_declarations/intersection_types/resolved_relativre_type_valid.phpt diff --git a/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_parent_type.phpt b/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_unresolved_relative_type_parent.phpt similarity index 66% rename from Zend/tests/type_declarations/intersection_types/invalid_types/invalid_parent_type.phpt rename to Zend/tests/type_declarations/intersection_types/invalid_types/invalid_unresolved_relative_type_parent.phpt index e3a86771a2189..55bfd28e306f6 100644 --- a/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_parent_type.phpt +++ b/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_unresolved_relative_type_parent.phpt @@ -1,11 +1,9 @@ --TEST-- -parent type cannot take part in an intersection type +parent type cannot take part in an intersection type when unresolved --FILE-- +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt index 4596785263c70..2312fed21e9e6 100644 --- a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt @@ -12,4 +12,4 @@ class Bar extends Foo { ?> --EXPECTF-- -Fatal error: Duplicate type parent is redundant in %s on line %d +Fatal error: Duplicate type Foo is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt index 4fcdb1f6109e3..fd3d073f97ba4 100644 --- a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt @@ -9,4 +9,4 @@ class Foo { ?> --EXPECTF-- -Fatal error: Duplicate type self is redundant in %s on line %d +Fatal error: Duplicate type Foo is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt index 03b84709c6de3..b0eb58bfc30eb 100644 --- a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt @@ -9,4 +9,4 @@ class Foo { ?> --EXPECTF-- -Fatal error: self resolves to Foo which is redundant in %s on line %d +Fatal error: Duplicate type Foo is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt index 5a2bc2e3f1942..54342a9915d6d 100644 --- a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt @@ -9,4 +9,4 @@ class Foo { ?> --EXPECTF-- -Fatal error: self resolves to Foo which is redundant in %s on line %d +Fatal error: Duplicate type Foo is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt index 21306e154d87e..e1bcbf8b1e1a5 100644 --- a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt @@ -12,4 +12,4 @@ class Bar extends Foo { ?> --EXPECTF-- -Fatal error: parent resolves to Foo which is redundant in %s on line %d +Fatal error: Duplicate type Foo is redundant in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt index e77980aba53f4..0b5a513a24397 100644 --- a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt +++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt @@ -12,4 +12,4 @@ class Bar extends Foo { ?> --EXPECTF-- -Fatal error: parent resolves to Foo which is redundant in %s on line %d +Fatal error: Duplicate type Foo is redundant in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e9ec29b820e86..d779afc116991 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1354,8 +1354,6 @@ static zend_string *add_intersection_type(zend_string *str, ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type)); - ZEND_ASSERT(!ZEND_TYPE_IS_RELATIVE_SELF(*single_type) && "Compile time disallowed to have 'self' in intersection"); - ZEND_ASSERT(!ZEND_TYPE_IS_RELATIVE_PARENT(*single_type) && "Compile time disallowed to have 'parent' in intersection"); intersection_str = add_type_string(intersection_str, ZEND_TYPE_NAME(*single_type), /* is_intersection */ true); } ZEND_TYPE_LIST_FOREACH_END(); @@ -1390,29 +1388,13 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type)); - /* We already have resolved types from compile time - * Mimic unresolved types for BC with "self" and "parent" */ - if (!scope && ZEND_TYPE_IS_RELATIVE_SELF(*list_type)) { - str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_SELF), /* is_intersection */ false); - } else if (!scope && ZEND_TYPE_IS_RELATIVE_PARENT(*list_type)) { - str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_PARENT), /* is_intersection */ false); - } else { - zend_string *name = ZEND_TYPE_NAME(*list_type); - zend_string *resolved = resolve_class_name(name, scope); - str = add_type_string(str, resolved, /* is_intersection */ false); - zend_string_release(resolved); - } + zend_string *name = ZEND_TYPE_NAME(*list_type); + zend_string *resolved = resolve_class_name(name, scope); + str = add_type_string(str, resolved, /* is_intersection */ false); + zend_string_release(resolved); } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(type)) { - /* We already have resolved types from compile time - * Mimic unresolved types for BC with "self" and "parent" */ - if (!scope && ZEND_TYPE_IS_RELATIVE_SELF(type)) { - str = ZSTR_KNOWN(ZEND_STR_SELF); - } else if (!scope && ZEND_TYPE_IS_RELATIVE_PARENT(type)) { - str = ZSTR_KNOWN(ZEND_STR_PARENT); - } else { - str = resolve_class_name(ZEND_TYPE_NAME(type), scope); - } + str = resolve_class_name(ZEND_TYPE_NAME(type), scope); } uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); @@ -6659,7 +6641,6 @@ static zend_type zend_compile_single_typename(zend_ast *ast) } else { const char *correct_name; uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); - uint32_t type_flags = 0; zend_string *class_name = type_name; if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { @@ -6670,7 +6651,6 @@ static zend_type zend_compile_single_typename(zend_ast *ast) zend_ensure_valid_class_fetch_type(fetch_type); if (fetch_type == ZEND_FETCH_CLASS_SELF) { - type_flags = _ZEND_TYPE_SELF_BIT; /* Scope might be unknown for unbound closures and traits */ if (zend_is_scope_known()) { class_name = CG(active_class_entry)->name; @@ -6678,7 +6658,6 @@ static zend_type zend_compile_single_typename(zend_ast *ast) } } else { ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_PARENT); - type_flags = _ZEND_TYPE_PARENT_BIT; /* Scope might be unknown for unbound closures and traits */ if (zend_is_scope_known()) { class_name = CG(active_class_entry)->parent_name; @@ -6709,7 +6688,7 @@ static zend_type zend_compile_single_typename(zend_ast *ast) class_name = zend_new_interned_string(class_name); zend_alloc_ce_cache(class_name); - return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, type_flags); + return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, 0); } } } @@ -6791,28 +6770,7 @@ static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list } if (zend_string_equals_ci(ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(type))) { zend_string *single_type_str = zend_type_to_string(type); - if (ZEND_TYPE_IS_RELATIVE_TYPE(type)) { - if ( ( - ZEND_TYPE_FULL_MASK(type) - & ZEND_TYPE_FULL_MASK(type_list->types[i]) - & (_ZEND_TYPE_RELATIVE_TYPE_MASK)) != 0 - ) { - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); - } - /* zend_type_to_string() will return "self" or "parent" where the resolved type is stored in - * ZEND_TYPE_NAME() */ - zend_error_noreturn(E_COMPILE_ERROR, "%s resolves to %s which is redundant", - ZSTR_VAL(single_type_str), ZSTR_VAL(ZEND_TYPE_NAME(type)) - ); - } else if (ZEND_TYPE_IS_RELATIVE_TYPE(type_list->types[i])) { - /* zend_type_to_string() will return "self" or "parent" where the resolved type is stored in - * ZEND_TYPE_NAME() */ - zend_error_noreturn(E_COMPILE_ERROR, "%s resolves to %s which is redundant", - ZEND_TYPE_IS_RELATIVE_SELF(type_list->types[i]) ? "self" : "parent", ZSTR_VAL(ZEND_TYPE_NAME(type)) - ); - } else { - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); - } + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); } } } @@ -6908,8 +6866,6 @@ static zend_type zend_compile_typename_ex( /* The first class type can be stored directly as the type ptr payload. */ ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type)); ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT; - /* Add flags indicating the named type is self/parent */ - ZEND_TYPE_FULL_MASK(type) |= (ZEND_TYPE_FULL_MASK(single_type) & _ZEND_TYPE_RELATIVE_TYPE_MASK); } else { if (type_list->num_types == 0) { /* Switch from single name to name list. */ @@ -6918,8 +6874,6 @@ static zend_type zend_compile_typename_ex( /* Clear MAY_BE_* type flags */ ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK; ZEND_TYPE_SET_LIST(type, type_list); - /* Clear flags indicating the named type is self/parent */ - ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_RELATIVE_TYPE_MASK; } type_list->types[type_list->num_types++] = single_type; @@ -6981,11 +6935,10 @@ static zend_type zend_compile_typename_ex( zend_string_release_ex(standard_type_str, false); } /* Check for "self" and "parent" too */ - if (ZEND_TYPE_IS_RELATIVE_SELF(single_type)) { - zend_error_noreturn(E_COMPILE_ERROR, "Type self cannot be part of an intersection type"); - } - if (ZEND_TYPE_IS_RELATIVE_PARENT(single_type)) { - zend_error_noreturn(E_COMPILE_ERROR, "Type parent cannot be part of an intersection type"); + if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self") + || zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) { + zend_error_noreturn(E_COMPILE_ERROR, + "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type))); } /* Add type to the type list */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 46fa34602856a..0b2ad43e1d8f9 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1955,10 +1955,10 @@ static inline bool zend_was_type_resolved(zend_type original_type, zend_type res static zend_type zend_resolve_name_type(zend_type type, const zend_class_entry *const ce) { ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type)); - if (ZEND_TYPE_IS_RELATIVE_SELF(type)) { + if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "self")) { zend_type resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(zend_string_copy(ce->name), /* allows_null */ false, /* extra_flags */ ZEND_TYPE_FULL_MASK(type)); return resolved_type; - } else if (ZEND_TYPE_IS_RELATIVE_PARENT(type)) { + } else if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "parent")) { if (!ce->parent) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use trait which has \"parent\" as a type when current class scope has no parent"); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index fe0ba7df03b9d..c4a07f58874ab 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,12 +142,8 @@ typedef struct { zend_type types[1]; } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 27 -#define _ZEND_TYPE_MASK ((1u << 27) - 1) -/* Only one of these bits may be set. */ -#define _ZEND_TYPE_PARENT_BIT (1u << 26) -#define _ZEND_TYPE_SELF_BIT (1u << 25) -#define _ZEND_TYPE_RELATIVE_TYPE_MASK (_ZEND_TYPE_SELF_BIT|_ZEND_TYPE_PARENT_BIT) +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 +#define _ZEND_TYPE_MASK ((1u << 25) - 1) /* Only one of these bits may be set. */ #define _ZEND_TYPE_NAME_BIT (1u << 24) // Used to signify that type.ptr is not a `zend_string*` but a `const char*`, @@ -170,17 +166,6 @@ typedef struct { #define ZEND_TYPE_IS_SET(t) \ (((t).type_mask & _ZEND_TYPE_MASK) != 0) - -/* To determine if the type resolved type was written with "self" or "parent" */ -#define ZEND_TYPE_IS_RELATIVE_TYPE(t) \ - ((((t).type_mask) & _ZEND_TYPE_RELATIVE_TYPE_MASK) != 0) -/* To determine if the type resolved type was written with "self" */ -#define ZEND_TYPE_IS_RELATIVE_SELF(t) \ - ((((t).type_mask) & _ZEND_TYPE_SELF_BIT) != 0) -/* To determine if the type resolved type was written with "parent" */ -#define ZEND_TYPE_IS_RELATIVE_PARENT(t) \ - ((((t).type_mask) & _ZEND_TYPE_PARENT_BIT) != 0) - /* If a type is complex it means it's either a list with a union or intersection, * or the void pointer is a class name */ #define ZEND_TYPE_IS_COMPLEX(t) \ diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 396643ad6c578..a84e00b25555a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1397,7 +1397,10 @@ static reflection_type_kind get_type_kind(zend_type type) { } ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type)); - if (ZEND_TYPE_IS_RELATIVE_TYPE(type)) { + if ( + zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "self") + || zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "parent") + ) { return RELATIVE_TYPE; } return NAMED_TYPE; @@ -2717,14 +2720,15 @@ ZEND_METHOD(ReflectionParameter, getClass) * TODO: Think about moving these checks to the compiler or some sort of * lint-mode. */ - if (ZEND_TYPE_IS_RELATIVE_SELF(param->arg_info->type)) { + zend_string *class_name = ZEND_TYPE_NAME(param->arg_info->type); + if (zend_string_equals_literal_ci(class_name, "self")) { ce = param->fptr->common.scope; if (!ce) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Parameter uses \"self\" as type but function is not a class member"); RETURN_THROWS(); } - } else if (ZEND_TYPE_IS_RELATIVE_PARENT(param->arg_info->type)) { + } else if (zend_string_equals_literal_ci(class_name, "parent")) { ce = param->fptr->common.scope; if (!ce) { zend_throw_exception_ex(reflection_exception_ptr, 0, @@ -2738,7 +2742,6 @@ ZEND_METHOD(ReflectionParameter, getClass) } ce = ce->parent; } else { - zend_string *class_name = ZEND_TYPE_NAME(param->arg_info->type); ce = zend_lookup_class(class_name); if (!ce) { zend_throw_exception_ex(reflection_exception_ptr, 0, @@ -3186,11 +3189,11 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) "Cannot resolve \"static\" type of an interface"); RETURN_THROWS(); } - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /*extra flags */ 0); + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); } else { - ZEND_ASSERT(ZEND_TYPE_IS_RELATIVE_TYPE(param->type)); + ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /*extra flags */ 0); + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /* extra flags */ 0); } reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce); @@ -3232,14 +3235,7 @@ ZEND_METHOD(ReflectionUnionType, getTypes) } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(param->type)) { zend_string *name = ZEND_TYPE_NAME(param->type); - uint32_t type_flags = 0; - if (ZEND_TYPE_IS_RELATIVE_SELF(param->type)) { - type_flags = _ZEND_TYPE_SELF_BIT; - } - if (ZEND_TYPE_IS_RELATIVE_PARENT(param->type)) { - type_flags = _ZEND_TYPE_PARENT_BIT; - } - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ type_flags), /* object_ce */ intern->ce); + append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ 0), /* object_ce */ intern->ce); } type_mask = ZEND_TYPE_PURE_MASK(param->type); diff --git a/ext/reflection/tests/bug80190.phpt b/ext/reflection/tests/bug80190.phpt index 6e2908f9c435f..5cf390c3e5266 100644 --- a/ext/reflection/tests/bug80190.phpt +++ b/ext/reflection/tests/bug80190.phpt @@ -47,14 +47,14 @@ foreach ((new ReflectionClass(C::class))->getMethods() as $method) { ?> --EXPECT-- C::a() - $method->getReturnType() returns ReflectionRelativeClassType - $method->getReturnType()->__toString() returns self + $method->getReturnType() returns ReflectionNamedType + $method->getReturnType()->__toString() returns C C::b() $method->getReturnType() returns ReflectionUnionType - $method->getReturnType()->__toString() returns stdClass|self + $method->getReturnType()->__toString() returns stdClass|C $method->getReturnType()->getTypes() returns an array with 2 element(s) - type(s) in union: ReflectionNamedType(stdClass), ReflectionRelativeClassType(self) + type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(C) C::c() $method->getReturnType() returns ReflectionRelativeClassType diff --git a/ext/reflection/tests/types/ReflectionType_001.phpt b/ext/reflection/tests/types/ReflectionType_001.phpt index 3441878f155d7..33b3bbffac2a8 100644 --- a/ext/reflection/tests/types/ReflectionType_001.phpt +++ b/ext/reflection/tests/types/ReflectionType_001.phpt @@ -166,12 +166,12 @@ string(10) "SplSubject" bool(true) bool(false) bool(false) -string(4) "self" +string(1) "c" ** Method 2 - parameter 0 bool(true) bool(false) bool(false) -string(6) "parent" +string(8) "stdClass" ** Method 3 - parameter 0 bool(true) bool(false) @@ -195,12 +195,12 @@ string(3) "int" bool(true) bool(false) bool(false) -string(4) "self" +string(1) "c" ** Function/method return type 4 bool(true) bool(false) bool(false) -string(6) "parent" +string(8) "stdClass" ** Function/method return type 5 bool(true) bool(false) diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt index 32dbb9574f54d..1a82e81fced4d 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt @@ -36,9 +36,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -56,48 +53,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt index 31b7cd01813e1..576848d68f2e3 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt @@ -36,9 +36,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -56,48 +53,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Class: C Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d Method: foo - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Class: D Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d Method: foo - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt index cc75ae40840bb..4ba35e0a1627f 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt @@ -36,15 +36,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type->isBuiltin()) { // Do not care about "int" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -63,48 +60,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt index 106ad01b8deb3..ec8877116b245 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt @@ -36,15 +36,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if (!($type instanceof ReflectionNamedType)) { // Do not care about "(X&Y)" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -63,48 +60,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt index 602f992951034..20615edd5703c 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt @@ -36,9 +36,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -56,48 +53,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt index 274c791851893..d77afc8a7d5e4 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt @@ -36,9 +36,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -56,48 +53,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Class: C Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d Method: foo - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Class: D Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d Method: foo - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt index bea4bcfe6217f..e3fad0701e93c 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt @@ -1,5 +1,5 @@ --TEST-- -ReflectionTypes of relative class types (self, parent) in classes, parameter union types +ReflectionTypes of relative class type static in classes, return union types --FILE-- getMethods(); foreach ($methods as $method) { echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { - // Do not care about "int" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + $unionType = $method->getReturnType(); + $types = $unionType->getTypes(); + foreach ($types as $type) { + if (!($type instanceof ReflectionRelativeClassType)) { + // Do not care about "int" type here; + continue; + } + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } + foreach ($instances as $arg) { + try { + $instance->{$method->name}($arg); + } catch (\TypeError $e) { + echo "\t\t\t\t", $e->getMessage(), PHP_EOL; } } } @@ -63,48 +59,40 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self + Type: static Instance of: ReflectionRelativeClassType Resolved Type: B Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + B::foo(): Return value must be of type B|int, A returned Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: self + Type: static Instance of: ReflectionRelativeClassType Resolved Type: C Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d + C::bar(): Return value must be of type C|int, A returned + C::bar(): Return value must be of type C|int, B returned Method: foo - Type: self + Type: static Instance of: ReflectionRelativeClassType - Resolved Type: B + Resolved Type: C Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + B::foo(): Return value must be of type C|int, A returned + B::foo(): Return value must be of type C|int, B returned Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: self + Type: static Instance of: ReflectionRelativeClassType - Resolved Type: C + Resolved Type: D Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d + C::bar(): Return value must be of type D|int, A returned + C::bar(): Return value must be of type D|int, B returned + C::bar(): Return value must be of type D|int, C returned Method: foo - Type: self + Type: static Instance of: ReflectionRelativeClassType - Resolved Type: B + Resolved Type: D Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d + B::foo(): Return value must be of type D|int, A returned + B::foo(): Return value must be of type D|int, B returned + B::foo(): Return value must be of type D|int, C returned diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt index 3410a10a2c7e7..e7a44073d69e9 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt @@ -36,15 +36,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if (!($type instanceof ReflectionNamedType)) { // Do not care about "(X&Y)" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -63,48 +60,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_return_type.phpt b/ext/reflection/tests/types/relative_class_types/classes_return_type.phpt index 5adb2508cfcc7..85609a81d53ba 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_return_type.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_return_type.phpt @@ -35,9 +35,11 @@ foreach ($instances as $instance) { $type = $method->getReturnType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + if ($type instanceof ReflectionRelativeClassType) { + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } foreach ($instances as $arg) { try { @@ -54,23 +56,17 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Return value must be of type B, A returned Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Return value must be of type B, A returned Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Return value must be of type C, A returned C::ping(): Return value must be of type C, B returned Method: pong @@ -81,23 +77,17 @@ Class: C C::pong(): Return value must be of type C, A returned C::pong(): Return value must be of type C, B returned Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Return value must be of type B, A returned Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Return value must be of type B, A returned Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Return value must be of type C, A returned C::ping(): Return value must be of type C, B returned Method: pong @@ -109,8 +99,6 @@ Class: D C::pong(): Return value must be of type D, B returned C::pong(): Return value must be of type D, C returned Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Return value must be of type B, A returned diff --git a/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt index 81f486f23975d..823816738ef5e 100644 --- a/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt @@ -36,9 +36,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -56,48 +53,34 @@ foreach ($instances as $instance) { Class: A Class: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 must be of type B, A given, called in %s on line %d Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type C, A given, called in %s on line %d C::ping(): Argument #1 must be of type C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 must be of type B, A given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type C, A given, called in %s on line %d C::ping(): Argument #1 must be of type C, B given, called in %s on line %d Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType B::foo(): Argument #1 must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt index fcd0a9ece0361..b63fa6e6600a2 100644 --- a/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/eval_trait_used_in_classes_parameters1_simple.phpt @@ -42,9 +42,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -66,29 +63,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/interfaces_return_type.phpt b/ext/reflection/tests/types/relative_class_types/interfaces_return_type.phpt index 450947fbb84ce..d22a12ac45bf3 100644 --- a/ext/reflection/tests/types/relative_class_types/interfaces_return_type.phpt +++ b/ext/reflection/tests/types/relative_class_types/interfaces_return_type.phpt @@ -12,7 +12,7 @@ interface B extends A { } interface C extends B { - //public function bar(object $o): parent; // This compile errors + //public function bar(object $o): parent; // This compiles errors public function ping(object $o): self; public function pong(object $o): static; } @@ -35,12 +35,14 @@ foreach ($interfaces as $interface) { $type = $method->getReturnType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - try { - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; - } catch (ReflectionException $e) { - echo $e->getMessage(), PHP_EOL; + if ($type instanceof ReflectionRelativeClassType) { + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (ReflectionException $e) { + echo $e->getMessage(), PHP_EOL; + } } } } @@ -50,37 +52,27 @@ foreach ($interfaces as $interface) { Interface: A Interface: B Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType Interface: C Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType Method: pong Type: static Instance of: ReflectionRelativeClassType Cannot resolve "static" type of an interface Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType Interface: D Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType Method: pong Type: static Instance of: ReflectionRelativeClassType Cannot resolve "static" type of an interface Method: foo - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt index df5b4e4aa88ef..c4edbb3f8f7e5 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt @@ -58,9 +58,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -79,20 +76,16 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, A2 given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, B2 given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, C2 given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, D2 given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, A2 given, called in %s on line %d @@ -101,20 +94,16 @@ Class: C C::ping(): Argument #1 ($o) must be of type C, D2 given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, A2 given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, B2 given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, C2 given, called in %s on line %d C::bar(): Argument #1 ($o) must be of type B, D2 given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, A2 given, called in %s on line %d @@ -125,20 +114,16 @@ Class: A2 Class: B2 Class: C2 Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B2 - Instance of: ReflectionNamedType + Type: B2 + Instance of: ReflectionNamedType C2::bar(): Argument #1 ($o) must be of type B2, A given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, B given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, C given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, D given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, A2 given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C2 - Instance of: ReflectionNamedType + Type: C2 + Instance of: ReflectionNamedType C2::ping(): Argument #1 ($o) must be of type C2, A given, called in %s on line %d C2::ping(): Argument #1 ($o) must be of type C2, B given, called in %s on line %d C2::ping(): Argument #1 ($o) must be of type C2, C given, called in %s on line %d @@ -147,20 +132,16 @@ Class: C2 C2::ping(): Argument #1 ($o) must be of type C2, B2 given, called in %s on line %d Class: D2 Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B2 - Instance of: ReflectionNamedType + Type: B2 + Instance of: ReflectionNamedType C2::bar(): Argument #1 ($o) must be of type B2, A given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, B given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, C given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, D given, called in %s on line %d C2::bar(): Argument #1 ($o) must be of type B2, A2 given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C2 - Instance of: ReflectionNamedType + Type: C2 + Instance of: ReflectionNamedType C2::ping(): Argument #1 ($o) must be of type C2, A given, called in %s on line %d C2::ping(): Argument #1 ($o) must be of type C2, B given, called in %s on line %d C2::ping(): Argument #1 ($o) must be of type C2, C given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt index 5517ea27a35a9..54c0e8ff19a01 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt @@ -39,9 +39,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -60,29 +57,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt index 590276caa4cfd..e79040e41c502 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt @@ -39,9 +39,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -60,29 +57,21 @@ Class: A Class: B Class: C Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d Class: D Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt index 15218030faf49..67b0f938e4050 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt @@ -39,15 +39,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type->isBuiltin()) { // Do not care about "int" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -67,29 +64,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt index 2e303f15ae79d..dce321a272695 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt @@ -39,15 +39,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type instanceof ReflectionIntersectionType) { // Do not care about "(X&Y)" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -67,29 +64,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt index ab50cfb212d84..5a19330158ab2 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt @@ -41,9 +41,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -62,29 +59,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt index 4142be2590af1..d74f4ec5eda1a 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt @@ -39,9 +39,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -60,29 +57,21 @@ Class: A Class: B Class: C Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d Class: D Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt index 2e7cc819f95ed..f717b05c58a92 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt @@ -39,15 +39,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type->isBuiltin()) { // Do not care about "int" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -67,29 +64,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt index 17144bfdce977..48cc62672f7cc 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt @@ -39,15 +39,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type instanceof ReflectionIntersectionType) { // Do not care about "(X&Y)" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -67,29 +64,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_return_type.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_return_type.phpt index e3cf26aeb72aa..b21ceee43ea64 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_return_type.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_return_type.phpt @@ -38,9 +38,11 @@ foreach ($instances as $instance) { $type = $method->getReturnType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + if ($type instanceof ReflectionRelativeClassType) { + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } foreach ($instances as $arg) { try { @@ -58,16 +60,12 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Return value must be of type B, A returned Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Return value must be of type C, A returned C::ping(): Return value must be of type C, B returned Method: pong @@ -79,16 +77,12 @@ Class: C C::pong(): Return value must be of type C, B returned Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Return value must be of type B, A returned Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Return value must be of type C, A returned C::ping(): Return value must be of type C, B returned Method: pong diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt index 5181047cf1903..f3048b79e743a 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt @@ -39,9 +39,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -60,29 +57,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type C, A given, called in %s on line %d C::ping(): Argument #1 must be of type C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type C, A given, called in %s on line %d C::ping(): Argument #1 must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt index 62bf3a2a1cf97..705de69b9e6ef 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt @@ -39,9 +39,6 @@ foreach ($instances as $instance) { $type = $param->getType(); echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -60,29 +57,21 @@ Class: A Class: B Class: C Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 must be of type ?C, B given, called in %s on line %d Class: D Method: bar - Type: ?parent - Instance of: ReflectionRelativeClassType - Resolved Type: ?B - Instance of: ReflectionNamedType + Type: ?B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type ?B, A given, called in %s on line %d Method: ping - Type: ?self - Instance of: ReflectionRelativeClassType - Resolved Type: ?C - Instance of: ReflectionNamedType + Type: ?C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type ?C, A given, called in %s on line %d C::ping(): Argument #1 must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt index 5d47c2cd51614..72e97758683f0 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt @@ -39,15 +39,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type->isBuiltin()) { // Do not care about "int" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -67,29 +64,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 must be of type C|int, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type B|int, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type C|int, A given, called in %s on line %d C::ping(): Argument #1 must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt index 7e9c9ac520ff5..6ce61995c52af 100644 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt +++ b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt @@ -39,15 +39,12 @@ foreach ($instances as $instance) { $unionType = $param->getType(); $types = $unionType->getTypes(); foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { + if ($type instanceof ReflectionIntersectionType) { // Do not care about "(X&Y)" type here; continue; } echo "\t\tType: ", $type, PHP_EOL; echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; foreach ($instances as $arg) { try { @@ -67,29 +64,21 @@ Class: A Class: B Class: C Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 must be of type (X&Y)|C, B given, called in %s on line %d Class: D Method: bar - Type: parent - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType + Type: B + Instance of: ReflectionNamedType C::bar(): Argument #1 must be of type (X&Y)|B, A given, called in %s on line %d Method: ping - Type: self - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType + Type: C + Instance of: ReflectionNamedType C::ping(): Argument #1 must be of type (X&Y)|C, A given, called in %s on line %d C::ping(): Argument #1 must be of type (X&Y)|C, B given, called in %s on line %d From d67ba9a80f8a4eeb359335c1364019cc0962b9a6 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 29 Feb 2024 03:31:33 +0000 Subject: [PATCH 14/24] Add tests and try to resolve bound closure --- ext/reflection/php_reflection.c | 6 ++- ...osure_bound_resolvable_relative_types.phpt | 43 +++++++++++++++++++ .../closure_unresolvable_relative_types.phpt | 40 +++++++++++++++++ .../traits_unresolvable_relative_types.phpt | 42 ++++++++++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/traits_unresolvable_relative_types.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a84e00b25555a..eda8bc04651a2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1645,8 +1645,13 @@ ZEND_METHOD(ReflectionFunction, __construct) Z_PARAM_OBJ_OF_CLASS_OR_STR(closure_obj, zend_ce_closure, fname) ZEND_PARSE_PARAMETERS_END(); + /* Set CE to NULL */ + intern->ce = NULL; if (closure_obj) { fptr = (zend_function*)zend_get_closure_method_def(closure_obj); + //zend_object *tmp = NULL; + ///* We use the get_closure object handler that always succeds to fetch the function and CE pointers */ + //closure_obj->handlers->get_closure(closure_obj, &intern->ce, &fptr, /* obj_ptr */ &tmp, /* check_only */ false); } else { if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) { /* Ignore leading "\" */ @@ -1681,7 +1686,6 @@ ZEND_METHOD(ReflectionFunction, __construct) } else { ZVAL_UNDEF(&intern->obj); } - intern->ce = NULL; } /* }}} */ diff --git a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt new file mode 100644 index 0000000000000..b7a24716ec41a --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt @@ -0,0 +1,43 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types +--XFAIL-- +I'm confused why it not possible to chope the bound scope via Reflection +--FILE-- +bindTo($b); + $rc = new ReflectionFunction($instance); + echo "\tBound to: ", $rc->getClosureScopeClass()->name, PHP_EOL; + $type = $rc->getReturnType(); + echo "\tType: ", $type, PHP_EOL; + echo "\tInstance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- diff --git a/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt b/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt new file mode 100644 index 0000000000000..c4d59178cfa1a --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt @@ -0,0 +1,40 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types +--FILE-- +getReturnType(); + echo "\t\tType: ", $type, PHP_EOL; + echo "\t\tInstance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +Type: self + Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve relative class name for a closure + Type: parent + Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve relative class name for a closure + Type: static + Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve relative class name for a closure diff --git a/ext/reflection/tests/types/relative_class_types/traits_unresolvable_relative_types.phpt b/ext/reflection/tests/types/relative_class_types/traits_unresolvable_relative_types.phpt new file mode 100644 index 0000000000000..0586b8206d9b7 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/traits_unresolvable_relative_types.phpt @@ -0,0 +1,42 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types +--FILE-- +getMethods(); +foreach ($methods as $method) { + echo "Method: ", $method->name, PHP_EOL; + $type = $method->getReturnType(); + echo "\tType: ", $type, PHP_EOL; + echo "\tInstance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +Method: bar + Type: parent + Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve relative class name for a trait +Method: ping + Type: self + Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve relative class name for a trait +Method: pong + Type: static + Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve relative class name for a trait From f40e9bf844e800daa846e0dd8eec7b5a65a8207d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 8 Mar 2024 14:12:38 +0000 Subject: [PATCH 15/24] Solve bound closure --- ext/reflection/php_reflection.c | 88 +++++++++++-------- ...osure_bound_resolvable_relative_types.phpt | 36 +++++--- ...losure_bound_unresolvable_parent_type.phpt | 31 +++++++ 3 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 ext/reflection/tests/types/relative_class_types/closure_bound_unresolvable_parent_type.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index eda8bc04651a2..722633844b2b1 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1467,20 +1467,41 @@ static void reflection_type_factory( } } -/* {{{ reflection_function_factory */ -static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object) +/* copy of hidden zend_closure */ +typedef struct _zend_closure { + zend_object std; + zend_function func; + zval this_ptr; + zend_class_entry *called_scope; + zif_handler orig_internal_handler; +} zend_closure; + +static void reflection_function_create_common(zend_function *function, zend_object *closure_object, zval *object) { - reflection_object *intern; - reflection_instantiate(reflection_function_ptr, object); - intern = Z_REFLECTION_P(object); + reflection_object *intern = Z_REFLECTION_P(object); + intern->ptr = function; intern->ref_type = REF_TYPE_FUNCTION; intern->ce = NULL; if (closure_object) { - ZVAL_OBJ_COPY(&intern->obj, Z_OBJ_P(closure_object)); + ZVAL_OBJ_COPY(&intern->obj, closure_object); + zend_closure *closure = (zend_closure*) closure_object; + /* if it is bound to a class, resolve to it */ + if (closure->called_scope) { + intern->ce = closure->called_scope; + } + } else { + ZVAL_UNDEF(&intern->obj); } ZVAL_STR_COPY(reflection_prop_name(object), function->common.function_name); } + +/* {{{ reflection_function_factory */ +static void reflection_function_factory(zend_function *function, zend_object *closure_object, zval *object) +{ + reflection_instantiate(reflection_function_ptr, object); + reflection_function_create_common(function, closure_object, object); +} /* }}} */ /* {{{ reflection_method_factory */ @@ -1632,27 +1653,18 @@ ZEND_METHOD(Reflection, getModifierNames) /* {{{ Constructor. Throws an Exception in case the given function does not exist */ ZEND_METHOD(ReflectionFunction, __construct) { - zval *object; zend_object *closure_obj = NULL; - reflection_object *intern; + zend_string *fname = NULL; zend_function *fptr; - zend_string *fname, *lcname; - - object = ZEND_THIS; - intern = Z_REFLECTION_P(object); ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJ_OF_CLASS_OR_STR(closure_obj, zend_ce_closure, fname) ZEND_PARSE_PARAMETERS_END(); - /* Set CE to NULL */ - intern->ce = NULL; if (closure_obj) { fptr = (zend_function*)zend_get_closure_method_def(closure_obj); - //zend_object *tmp = NULL; - ///* We use the get_closure object handler that always succeds to fetch the function and CE pointers */ - //closure_obj->handlers->get_closure(closure_obj, &intern->ce, &fptr, /* obj_ptr */ &tmp, /* check_only */ false); } else { + zend_string *lcname; if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) { /* Ignore leading "\" */ ALLOCA_FLAG(use_heap) @@ -1673,19 +1685,15 @@ ZEND_METHOD(ReflectionFunction, __construct) } } - if (intern->ptr) { - zval_ptr_dtor(&intern->obj); - zval_ptr_dtor(reflection_prop_name(object)); - } + // TODO How can this ever be hit? + //zval *object = ZEND_THIS; + //reflection_object *intern = Z_REFLECTION_P(object); + //if (intern->ptr) { + // zval_ptr_dtor(&intern->obj); + // zval_ptr_dtor(reflection_prop_name(object)); + //} - ZVAL_STR_COPY(reflection_prop_name(object), fptr->common.function_name); - intern->ptr = fptr; - intern->ref_type = REF_TYPE_FUNCTION; - if (closure_obj) { - ZVAL_OBJ_COPY(&intern->obj, closure_obj); - } else { - ZVAL_UNDEF(&intern->obj); - } + reflection_function_create_common(fptr, closure_obj, ZEND_THIS); } /* }}} */ @@ -2389,9 +2397,7 @@ ZEND_METHOD(ReflectionGenerator, getFunction) REFLECTION_CHECK_VALID_GENERATOR(ex) if (ex->func->common.fn_flags & ZEND_ACC_CLOSURE) { - zval closure; - ZVAL_OBJ(&closure, ZEND_CLOSURE_OBJECT(ex->func)); - reflection_function_factory(ex->func, &closure, return_value); + reflection_function_factory(ex->func, ZEND_CLOSURE_OBJECT(ex->func), return_value); } else if (ex->func->op_array.scope) { reflection_method_factory(ex->func->op_array.scope, ex->func, NULL, return_value); } else { @@ -2674,7 +2680,7 @@ ZEND_METHOD(ReflectionParameter, getDeclaringFunction) GET_REFLECTION_OBJECT_PTR(param); if (!param->fptr->common.scope) { - reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value); + reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : Z_OBJ(intern->obj), return_value); } else { reflection_method_factory(param->fptr->common.scope, _copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value); } @@ -3159,7 +3165,7 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin) } /* }}} */ -/* {{{ Returns whether type is a builtin type */ +/* {{{ Resolve a relative class type to a proper named type */ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) { reflection_object *intern; @@ -3195,9 +3201,19 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) } resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); } else { - ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")); ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /* extra flags */ 0); + ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")); + + if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self")) { + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); + } else { + if (!intern->ce->parent) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve \"parent\" type when class has no parent"); + RETURN_THROWS(); + } + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->parent->name, allows_null, /* extra flags */ 0); + } } reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce); diff --git a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt index b7a24716ec41a..58defa5c542cd 100644 --- a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt +++ b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt @@ -1,7 +1,5 @@ --TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types ---XFAIL-- -I'm confused why it not possible to chope the bound scope via Reflection +ReflectionTypes of relative class types (self, parent) in Closure bound to class with parent --FILE-- bindTo($b); - $rc = new ReflectionFunction($instance); - echo "\tBound to: ", $rc->getClosureScopeClass()->name, PHP_EOL; + $fn = $instance->bindTo($b); + $rc = new ReflectionFunction($fn); + echo "Bound to: ", $rc->getClosureCalledClass()->name, PHP_EOL; $type = $rc->getReturnType(); - echo "\tType: ", $type, PHP_EOL; - echo "\tInstance of: ", $type::class, PHP_EOL; + echo "Type: ", $type, PHP_EOL; + echo "Instance of: ", $type::class, PHP_EOL; try { $resolvedType = $type->resolveToNamedType(); - echo "\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL; + echo "\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\tInstance of: ", $resolvedType::class, PHP_EOL; } catch (\Throwable $e) { echo $e::class, ': ', $e->getMessage(), PHP_EOL; } @@ -41,3 +36,18 @@ foreach ($instances as $instance) { ?> --EXPECT-- +Bound to: B +Type: self +Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType +Bound to: B +Type: parent +Instance of: ReflectionRelativeClassType + Resolved Type: A + Instance of: ReflectionNamedType +Bound to: B +Type: static +Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType diff --git a/ext/reflection/tests/types/relative_class_types/closure_bound_unresolvable_parent_type.phpt b/ext/reflection/tests/types/relative_class_types/closure_bound_unresolvable_parent_type.phpt new file mode 100644 index 0000000000000..3d924a075abe6 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/closure_bound_unresolvable_parent_type.phpt @@ -0,0 +1,31 @@ +--TEST-- +ReflectionTypes of relative class type parent in Closure bound to class with no parent +--FILE-- +bindTo($a); +$rc = new ReflectionFunction($fn); +echo "Bound to: ", $rc->getClosureCalledClass()->name, PHP_EOL; +$type = $rc->getReturnType(); +echo "Type: ", $type, PHP_EOL; +echo "Instance of: ", $type::class, PHP_EOL; +try { + $resolvedType = $type->resolveToNamedType(); + echo "\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\tInstance of: ", $resolvedType::class, PHP_EOL; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Bound to: A +Type: parent +Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve "parent" type when class has no parent From eb3c7e8a4f09c617604cd3b52996ec40850d78c3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 15 Apr 2024 11:51:20 +0100 Subject: [PATCH 16/24] Fix resolution for closures by propagating Closure object --- ext/reflection/php_reflection.c | 245 ++++++++++++------ .../classes_by_ref_parameters1_simple.phpt | 86 ------ .../classes_by_ref_parameters2_nullable.phpt | 86 ------ .../classes_by_ref_parameters3_union.phpt | 93 ------- .../classes_by_ref_parameters4_dnf.phpt | 93 ------- .../classes_parameters1_simple.phpt | 86 ------ .../classes_parameters2_nullable.phpt | 86 ------ .../classes_parameters3_union.phpt | 98 ------- .../classes_parameters4_dnf.phpt | 93 ------- .../classes_variadic_parameter1_simple.phpt | 86 ------ ...osure_bound_resolvable_relative_types.phpt | 65 ++++- ...ound_resolvable_relative_types_arnaud.phpt | 22 ++ ..._used_in_2_classes_parameters1_simple.phpt | 150 ----------- ..._in_classes_by_ref_parameters1_simple.phpt | 77 ------ ...n_classes_by_ref_parameters2_nullable.phpt | 77 ------ ...d_in_classes_by_ref_parameters3_union.phpt | 84 ------ ...sed_in_classes_by_ref_parameters4_dnf.phpt | 84 ------ ...it_used_in_classes_parameters1_simple.phpt | 79 ------ ..._used_in_classes_parameters2_nullable.phpt | 77 ------ ...ait_used_in_classes_parameters3_union.phpt | 84 ------ ...trait_used_in_classes_parameters4_dnf.phpt | 84 ------ ...in_classes_variadic_parameter1_simple.phpt | 77 ------ ...classes_variadic_parameters2_nullable.phpt | 77 ------ ...in_classes_variadic_parameters3_union.phpt | 84 ------ ...d_in_classes_variadic_parameters4_dnf.phpt | 84 ------ 25 files changed, 241 insertions(+), 2016 deletions(-) delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_arnaud.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt delete mode 100644 ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 722633844b2b1..1a8f7b54c8e0e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1336,7 +1336,7 @@ static void reflection_extension_factory(zval *object, const char *name_str) /* }}} */ /* {{{ reflection_parameter_factory */ -static void reflection_parameter_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, uint32_t offset, bool required, zval *object) +static void reflection_parameter_factory(zend_function *fptr, zend_object *closure_object, struct _zend_arg_info *arg_info, uint32_t offset, bool required, zval *object) { reflection_object *intern; parameter_reference *reference; @@ -1353,7 +1353,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje intern->ref_type = REF_TYPE_PARAMETER; intern->ce = fptr->common.scope; if (closure_object) { - ZVAL_OBJ_COPY(&intern->obj, Z_OBJ_P(closure_object)); + ZVAL_OBJ_COPY(&intern->obj, closure_object); } prop_name = reflection_prop_name(object); @@ -1369,7 +1369,8 @@ typedef enum { NAMED_TYPE = 0, UNION_TYPE = 1, INTERSECTION_TYPE = 2, - RELATIVE_TYPE = 3 + SELF_PARENT_TYPE = 3, + STATIC_TYPE = 4 } reflection_type_kind; /* For backwards compatibility reasons, we need to return T|null style unions @@ -1401,7 +1402,7 @@ static reflection_type_kind get_type_kind(zend_type type) { zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "parent") ) { - return RELATIVE_TYPE; + return SELF_PARENT_TYPE; } return NAMED_TYPE; } @@ -1414,7 +1415,7 @@ static reflection_type_kind get_type_kind(zend_type type) { } if (type_mask_without_null == MAY_BE_STATIC) { - return RELATIVE_TYPE; + return STATIC_TYPE; } return NAMED_TYPE; } @@ -1425,7 +1426,7 @@ static reflection_type_kind get_type_kind(zend_type type) { */ static void reflection_type_factory( zend_type type, zval *object, bool legacy_behavior, - zend_class_entry *object_ce + zend_class_entry *object_ce, zend_object *closure_object ) { reflection_object *intern; type_reference *reference; @@ -1443,7 +1444,8 @@ static void reflection_type_factory( case NAMED_TYPE: reflection_instantiate(reflection_named_type_ptr, object); break; - case RELATIVE_TYPE: + case SELF_PARENT_TYPE: + case STATIC_TYPE: reflection_instantiate(reflection_relative_class_type_ptr, object); break; EMPTY_SWITCH_DEFAULT_CASE(); @@ -1457,6 +1459,27 @@ static void reflection_type_factory( intern->ref_type = REF_TYPE_TYPE; intern->ce = object_ce; + if (closure_object) { + ZVAL_OBJ_COPY(&intern->obj, closure_object); + + /* If bound to a class */ + //if (object_ce) { + zend_function *fptr = NULL; + zend_class_entry *called_scope = NULL; + zend_object *this_obj = NULL; + closure_object->ce->default_object_handlers->get_closure(closure_object, &called_scope, &fptr, &this_obj, /* check_only */ true); + /* it was bound to something, assign ce to be zend_ce_closure as static can be completely different than self/parent + * and needs to be resolved depending on the type. */ + if (type_kind == SELF_PARENT_TYPE) { + intern->ce = fptr->common.scope; + } else { + intern->ce = called_scope; + } + //} + } else { + ZVAL_UNDEF(&intern->obj); + } + /* Property types may be resolved during the lifetime of the ReflectionType. * If we reference a string, make sure it doesn't get released. However, only * do this for the top-level type, as resolutions inside type lists will be @@ -1467,15 +1490,6 @@ static void reflection_type_factory( } } -/* copy of hidden zend_closure */ -typedef struct _zend_closure { - zend_object std; - zend_function func; - zval this_ptr; - zend_class_entry *called_scope; - zif_handler orig_internal_handler; -} zend_closure; - static void reflection_function_create_common(zend_function *function, zend_object *closure_object, zval *object) { reflection_object *intern = Z_REFLECTION_P(object); @@ -1485,11 +1499,6 @@ static void reflection_function_create_common(zend_function *function, zend_obje intern->ce = NULL; if (closure_object) { ZVAL_OBJ_COPY(&intern->obj, closure_object); - zend_closure *closure = (zend_closure*) closure_object; - /* if it is bound to a class, resolve to it */ - if (closure->called_scope) { - intern->ce = closure->called_scope; - } } else { ZVAL_UNDEF(&intern->obj); } @@ -1505,7 +1514,7 @@ static void reflection_function_factory(zend_function *function, zend_object *cl /* }}} */ /* {{{ reflection_method_factory */ -static void reflection_method_factory(zend_class_entry *ce, zend_function *method, zval *closure_object, zval *object) +static void reflection_method_factory(zend_class_entry *ce, zend_function *method, zend_object *closure_object, zval *object) { reflection_object *intern; @@ -1515,7 +1524,8 @@ static void reflection_method_factory(zend_class_entry *ce, zend_function *metho intern->ref_type = REF_TYPE_FUNCTION; intern->ce = ce; if (closure_object) { - ZVAL_OBJ_COPY(&intern->obj, Z_OBJ_P(closure_object)); + intern->ce = zend_ce_closure; + ZVAL_OBJ_COPY(&intern->obj, closure_object); } ZVAL_STR_COPY(reflection_prop_name(object), method->common.function_name); @@ -2073,7 +2083,7 @@ ZEND_METHOD(ReflectionFunction, invoke) fcc.object = NULL; if (!Z_ISUNDEF(intern->obj)) { - Z_OBJ_HT(intern->obj)->get_closure( + Z_OBJ_HANDLER(intern->obj, get_closure)( Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, 0); } @@ -2112,7 +2122,7 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) fcc.object = NULL; if (!Z_ISUNDEF(intern->obj)) { - Z_OBJ_HT(intern->obj)->get_closure( + Z_OBJ_HANDLER(intern->obj, get_closure)( Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, 0); } @@ -2215,7 +2225,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getParameters) reflection_parameter_factory( _copy_function(fptr), - Z_ISUNDEF(intern->obj) ? NULL : &intern->obj, + Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj), arg_info, i, i < fptr->common.required_num_args, @@ -2682,7 +2692,7 @@ ZEND_METHOD(ReflectionParameter, getDeclaringFunction) if (!param->fptr->common.scope) { reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : Z_OBJ(intern->obj), return_value); } else { - reflection_method_factory(param->fptr->common.scope, _copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value); + reflection_method_factory(param->fptr->common.scope, _copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : Z_OBJ(intern->obj), return_value); } } /* }}} */ @@ -2793,7 +2803,13 @@ ZEND_METHOD(ReflectionParameter, getType) if (!ZEND_TYPE_IS_SET(param->arg_info->type)) { RETURN_NULL(); } - reflection_type_factory(param->arg_info->type, return_value, /* legacy_behavior */ true, intern->ce); + reflection_type_factory( + param->arg_info->type, + return_value, + /* legacy_behavior */ true, + intern->ce, + Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj) + ); } /* }}} */ @@ -3176,11 +3192,55 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) } GET_REFLECTION_OBJECT_PTR(param); - /* Unbound closures can use relative class types */ - if (!intern->ce) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot resolve relative class name for a closure"); - RETURN_THROWS(); + /* Is closure */ + if (!Z_ISUNDEF(intern->obj)) { + /* Unbound closures can use relative class types */ + if (!intern->ce) { + ZEND_ASSERT(!Z_ISUNDEF(intern->obj)); + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve relative class name for a closure"); + RETURN_THROWS(); + } + + /* Attempt to resolve bound Closure */ + zend_function *fptr = NULL; + zend_class_entry *called_scope = NULL; + zend_object *this_obj = NULL; + + Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &fptr, &this_obj, /* check_only */ true); + + /* Support for legacy behaviour of nullable types and ReflectionNamedType */ + bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL; + zend_type resolved_closure_type; + + if (ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC) { + resolved_closure_type = (zend_type) ZEND_TYPE_INIT_CLASS(called_scope->name, allows_null, /* extra flags */ 0); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); + ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")); + + zend_class_entry *self_ce = fptr->common.scope; + if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self")) { + resolved_closure_type = (zend_type) ZEND_TYPE_INIT_CLASS(self_ce->name, allows_null, /* extra flags */ 0); + } else { + if (!self_ce->parent) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve \"parent\" type when class has no parent"); + RETURN_THROWS(); + } + resolved_closure_type = (zend_type) ZEND_TYPE_INIT_CLASS(self_ce->parent->name, allows_null, /* extra flags */ 0); + } + } + + + reflection_type_factory( + resolved_closure_type, + return_value, + /* legacy_behavior */ true, + intern->ce, + /* closure_object */ NULL + ); + return; } if (intern->ce->ce_flags & ZEND_ACC_TRAIT) { @@ -3189,50 +3249,48 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) RETURN_THROWS(); } + /* For all other case self and parent will have been resolved at compile time */ + ZEND_ASSERT(ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC); /* Support for legacy behaviour of nullable types and ReflectionNamedType */ bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL; zend_type resolved_type; /* For static resolved name is the name of the class */ - if (ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC) { - if (intern->ce->ce_flags & ZEND_ACC_INTERFACE) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot resolve \"static\" type of an interface"); - RETURN_THROWS(); - } - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); - } else { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); - ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")); - - if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self")) { - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); - } else { - if (!intern->ce->parent) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot resolve \"parent\" type when class has no parent"); - RETURN_THROWS(); - } - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->parent->name, allows_null, /* extra flags */ 0); - } + if (intern->ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve \"static\" type of an interface"); + RETURN_THROWS(); } + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); - reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce); + reflection_type_factory( + resolved_type, + return_value, + /* legacy_behavior */ true, + intern->ce, + /* closure_object */ NULL + ); } /* }}} */ -static void append_type(zval *return_value, zend_type type, zend_class_entry *object_ce) { +static void append_type(zval *return_value, zend_type type, zend_class_entry *object_ce, zend_object *closure_obj) { zval reflection_type; /* Drop iterable BC bit for type list */ if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) { ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_ITERABLE_BIT; } - reflection_type_factory(type, &reflection_type, /* legacy_behavior */ false, object_ce); + reflection_type_factory( + type, + &reflection_type, + /* legacy_behavior */ false, + object_ce, + closure_obj + ); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &reflection_type); } -static void append_type_mask(zval *return_value, uint32_t type_mask, zend_class_entry *object_ce) { - append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask), object_ce); +static void append_type_mask(zval *return_value, uint32_t type_mask, zend_class_entry *object_ce, zend_object *closure_obj) { + append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask), object_ce, closure_obj); } /* {{{ Returns the types that are part of this union type */ @@ -3251,46 +3309,46 @@ ZEND_METHOD(ReflectionUnionType, getTypes) if (ZEND_TYPE_HAS_LIST(param->type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { - append_type(return_value, *list_type, /* object_ce */ intern->ce); + append_type(return_value, *list_type, /* object_ce */ intern->ce, /* closure_obj */ Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj)); } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(param->type)) { zend_string *name = ZEND_TYPE_NAME(param->type); - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ 0), /* object_ce */ intern->ce); + append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ 0), /* object_ce */ intern->ce, /* closure_obj */ Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj)); } type_mask = ZEND_TYPE_PURE_MASK(param->type); ZEND_ASSERT(!(type_mask & MAY_BE_VOID)); ZEND_ASSERT(!(type_mask & MAY_BE_NEVER)); if (type_mask & MAY_BE_STATIC) { - append_type_mask(return_value, MAY_BE_STATIC, /* object_ce */ intern->ce); + append_type_mask(return_value, MAY_BE_STATIC, /* object_ce */ intern->ce, /* closure_obj */ Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj)); } if (type_mask & MAY_BE_CALLABLE) { - append_type_mask(return_value, MAY_BE_CALLABLE, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_CALLABLE, /* object_ce */ NULL, /* closure_obj */ NULL); } if (type_mask & MAY_BE_OBJECT) { - append_type_mask(return_value, MAY_BE_OBJECT, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_OBJECT, /* object_ce */ NULL, /* closure_obj */ NULL); } if (type_mask & MAY_BE_ARRAY) { - append_type_mask(return_value, MAY_BE_ARRAY, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_ARRAY, /* object_ce */ NULL, /* closure_obj */ NULL); } if (type_mask & MAY_BE_STRING) { - append_type_mask(return_value, MAY_BE_STRING, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_STRING, /* object_ce */ NULL, /* closure_obj */ NULL); } if (type_mask & MAY_BE_LONG) { - append_type_mask(return_value, MAY_BE_LONG, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_LONG, /* object_ce */ NULL, /* closure_obj */ NULL); } if (type_mask & MAY_BE_DOUBLE) { - append_type_mask(return_value, MAY_BE_DOUBLE, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_DOUBLE, /* object_ce */ NULL, /* closure_obj */ NULL); } if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { - append_type_mask(return_value, MAY_BE_BOOL, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_BOOL, /* object_ce */ NULL, /* closure_obj */ NULL); } else if (type_mask & MAY_BE_TRUE) { - append_type_mask(return_value, MAY_BE_TRUE, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_TRUE, /* object_ce */ NULL, /* closure_obj */ NULL); } else if (type_mask & MAY_BE_FALSE) { - append_type_mask(return_value, MAY_BE_FALSE, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_FALSE, /* object_ce */ NULL, /* closure_obj */ NULL); } if (type_mask & MAY_BE_NULL) { - append_type_mask(return_value, MAY_BE_NULL, /* object_ce */ NULL); + append_type_mask(return_value, MAY_BE_NULL, /* object_ce */ NULL, /* closure_obj */ NULL); } } /* }}} */ @@ -3311,7 +3369,8 @@ ZEND_METHOD(ReflectionIntersectionType, getTypes) array_init(return_value); ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { - append_type(return_value, *list_type, /* object_ce */ intern->ce); + /* Intersection types cannot have a relative class type */ + append_type(return_value, *list_type, /* object_ce */ intern->ce, /* closure_obj */ NULL); } ZEND_TYPE_LIST_FOREACH_END(); } /* }}} */ @@ -3734,7 +3793,13 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) RETURN_NULL(); } - reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce); + reflection_type_factory( + fptr->common.arg_info[-1].type, + return_value, + /* legacy_behavior */ true, + intern->ce, + Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj) + ); } /* }}} */ @@ -3770,7 +3835,13 @@ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) RETURN_NULL(); } - reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce); + reflection_type_factory( + fptr->common.arg_info[-1].type, + return_value, + /* legacy_behavior */ true, + intern->ce, + Z_ISUNDEF(intern->obj) ? NULL : Z_OBJ(intern->obj) + ); } /* }}} */ @@ -3991,7 +4062,13 @@ ZEND_METHOD(ReflectionClassConstant, getType) RETURN_NULL(); } - reflection_type_factory(ref->type, return_value, /* legacy_behavior */ true, intern->ce); + reflection_type_factory( + ref->type, + return_value, + /* legacy_behavior */ true, + intern->ce, + /* closure_object */ NULL + ); } /* Returns whether class constant has a type */ @@ -6011,7 +6088,13 @@ ZEND_METHOD(ReflectionProperty, getType) RETURN_NULL(); } - reflection_type_factory(ref->prop->type, return_value, /* legacy_behavior */ true, intern->ce); + reflection_type_factory( + ref->prop->type, + return_value, + /* legacy_behavior */ true, + intern->ce, + /* closure_object */ NULL + ); } /* }}} */ @@ -7100,7 +7183,13 @@ ZEND_METHOD(ReflectionEnum, getBackingType) RETURN_NULL(); } else { zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0); - reflection_type_factory(type, return_value, /* legacy_behavior */ false, /* object_ce */ NULL); + reflection_type_factory( + type, + return_value, + /* legacy_behavior */ false, + /* object_ce */ NULL, + /* closure_object */ NULL + ); } } diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt deleted file mode 100644 index 1a82e81fced4d..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters1_simple.phpt +++ /dev/null @@ -1,86 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt deleted file mode 100644 index 576848d68f2e3..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters2_nullable.phpt +++ /dev/null @@ -1,86 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter nullable types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: ?B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d -Class: C - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d - Method: foo - Type: ?B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d -Class: D - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d - Method: foo - Type: ?B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt deleted file mode 100644 index 4ba35e0a1627f..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters3_union.phpt +++ /dev/null @@ -1,93 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter union types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type->isBuiltin()) { - // Do not care about "int" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt deleted file mode 100644 index ec8877116b245..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_by_ref_parameters4_dnf.phpt +++ /dev/null @@ -1,93 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, by-ref parameter union types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if (!($type instanceof ReflectionNamedType)) { - // Do not care about "(X&Y)" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt deleted file mode 100644 index 20615edd5703c..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters1_simple.phpt +++ /dev/null @@ -1,86 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt deleted file mode 100644 index d77afc8a7d5e4..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters2_nullable.phpt +++ /dev/null @@ -1,86 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, parameter nullable types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: ?B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d -Class: C - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d - Method: foo - Type: ?B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d -Class: D - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d - Method: foo - Type: ?B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt deleted file mode 100644 index e3fad0701e93c..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters3_union.phpt +++ /dev/null @@ -1,98 +0,0 @@ ---TEST-- -ReflectionTypes of relative class type static in classes, return union types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $unionType = $method->getReturnType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if (!($type instanceof ReflectionRelativeClassType)) { - // Do not care about "int" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - $resolvedType = $type->resolveToNamedType(); - echo "\t\t\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\t\t\tInstance of: ", $resolvedType::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: static - Instance of: ReflectionRelativeClassType - Resolved Type: B - Instance of: ReflectionNamedType - B::foo(): Return value must be of type B|int, A returned -Class: C - Method: bar - Type: static - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType - C::bar(): Return value must be of type C|int, A returned - C::bar(): Return value must be of type C|int, B returned - Method: foo - Type: static - Instance of: ReflectionRelativeClassType - Resolved Type: C - Instance of: ReflectionNamedType - B::foo(): Return value must be of type C|int, A returned - B::foo(): Return value must be of type C|int, B returned -Class: D - Method: bar - Type: static - Instance of: ReflectionRelativeClassType - Resolved Type: D - Instance of: ReflectionNamedType - C::bar(): Return value must be of type D|int, A returned - C::bar(): Return value must be of type D|int, B returned - C::bar(): Return value must be of type D|int, C returned - Method: foo - Type: static - Instance of: ReflectionRelativeClassType - Resolved Type: D - Instance of: ReflectionNamedType - B::foo(): Return value must be of type D|int, A returned - B::foo(): Return value must be of type D|int, B returned - B::foo(): Return value must be of type D|int, C returned diff --git a/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt deleted file mode 100644 index e7a44073d69e9..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_parameters4_dnf.phpt +++ /dev/null @@ -1,93 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, parameter DNF types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if (!($type instanceof ReflectionNamedType)) { - // Do not care about "(X&Y)" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt b/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt deleted file mode 100644 index 823816738ef5e..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/classes_variadic_parameter1_simple.phpt +++ /dev/null @@ -1,86 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) in classes, variadic parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 must be of type B, A given, called in %s on line %d -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 must be of type B, A given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type C, B given, called in %s on line %d - Method: foo - Type: B - Instance of: ReflectionNamedType - B::foo(): Argument #1 must be of type B, A given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt index 58defa5c542cd..75285c0935f77 100644 --- a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt +++ b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types.phpt @@ -15,27 +15,51 @@ $instances = [ $fnParent, $fnStatic, ]; +$scopes = [ + 'static', + B::class, + A::class, +]; $b = new B(); -foreach ($instances as $instance) { - $fn = $instance->bindTo($b); - $rc = new ReflectionFunction($fn); - echo "Bound to: ", $rc->getClosureCalledClass()->name, PHP_EOL; - $type = $rc->getReturnType(); - echo "Type: ", $type, PHP_EOL; - echo "Instance of: ", $type::class, PHP_EOL; - try { - $resolvedType = $type->resolveToNamedType(); - echo "\tResolved Type: ", $resolvedType, PHP_EOL; - echo "\tInstance of: ", $resolvedType::class, PHP_EOL; - } catch (\Throwable $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; +foreach ($scopes as $scope) { + echo "Scope as \"$scope\"", PHP_EOL; + foreach ($instances as $instance) { + $fn = $instance->bindTo($b, $scope); + $rc = new ReflectionFunction($fn); + echo "Bound to: ", $rc->getClosureCalledClass()->name, PHP_EOL; + $type = $rc->getReturnType(); + echo "Type: ", $type, PHP_EOL; + echo "Instance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } } } ?> --EXPECT-- +Scope as "static" +Bound to: B +Type: self +Instance of: ReflectionRelativeClassType + Resolved Type: Closure + Instance of: ReflectionNamedType +Bound to: B +Type: parent +Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve "parent" type when class has no parent +Bound to: B +Type: static +Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType +Scope as "B" Bound to: B Type: self Instance of: ReflectionRelativeClassType @@ -51,3 +75,18 @@ Type: static Instance of: ReflectionRelativeClassType Resolved Type: B Instance of: ReflectionNamedType +Scope as "A" +Bound to: B +Type: self +Instance of: ReflectionRelativeClassType + Resolved Type: A + Instance of: ReflectionNamedType +Bound to: B +Type: parent +Instance of: ReflectionRelativeClassType +ReflectionException: Cannot resolve "parent" type when class has no parent +Bound to: B +Type: static +Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType diff --git a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_arnaud.phpt b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_arnaud.phpt new file mode 100644 index 0000000000000..354d6c93857ae --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_arnaud.phpt @@ -0,0 +1,22 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in Closure bound to class with parent +--FILE-- +getReturnType()->resolveToNamedType()->__toString(); + +// Prints B == A +printf("%s == %s ?\n", $f()::class, $resolved); + +?> +--EXPECT-- +B == B ? diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt deleted file mode 100644 index c4edbb3f8f7e5..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_2_classes_parameters1_simple.phpt +++ /dev/null @@ -1,150 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in 2 different unrelated classes, parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, A2 given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, B2 given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, C2 given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, D2 given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, A2 given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B2 given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, C2 given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, D2 given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, A2 given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, B2 given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, C2 given, called in %s on line %d - C::bar(): Argument #1 ($o) must be of type B, D2 given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, A2 given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B2 given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, C2 given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, D2 given, called in %s on line %d -Class: A2 -Class: B2 -Class: C2 - Method: bar - Type: B2 - Instance of: ReflectionNamedType - C2::bar(): Argument #1 ($o) must be of type B2, A given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, B given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, C given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, D given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, A2 given, called in %s on line %d - Method: ping - Type: C2 - Instance of: ReflectionNamedType - C2::ping(): Argument #1 ($o) must be of type C2, A given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, B given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, C given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, D given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, A2 given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, B2 given, called in %s on line %d -Class: D2 - Method: bar - Type: B2 - Instance of: ReflectionNamedType - C2::bar(): Argument #1 ($o) must be of type B2, A given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, B given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, C given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, D given, called in %s on line %d - C2::bar(): Argument #1 ($o) must be of type B2, A2 given, called in %s on line %d - Method: ping - Type: C2 - Instance of: ReflectionNamedType - C2::ping(): Argument #1 ($o) must be of type C2, A given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, B given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, C given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, D given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, A2 given, called in %s on line %d - C2::ping(): Argument #1 ($o) must be of type C2, B2 given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt deleted file mode 100644 index 54c0e8ff19a01..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters1_simple.phpt +++ /dev/null @@ -1,77 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt deleted file mode 100644 index e79040e41c502..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters2_nullable.phpt +++ /dev/null @@ -1,77 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter nullable types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d -Class: D - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt deleted file mode 100644 index 67b0f938e4050..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters3_union.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter union types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type->isBuiltin()) { - // Do not care about "int" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt deleted file mode 100644 index dce321a272695..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_by_ref_parameters4_dnf.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, by-ref parameter DNF types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type instanceof ReflectionIntersectionType) { - // Do not care about "(X&Y)" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt deleted file mode 100644 index 5a19330158ab2..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters1_simple.phpt +++ /dev/null @@ -1,79 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt deleted file mode 100644 index d74f4ec5eda1a..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters2_nullable.phpt +++ /dev/null @@ -1,77 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, parameter nullable types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d -Class: D - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt deleted file mode 100644 index f717b05c58a92..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters3_union.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, parameter union types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type->isBuiltin()) { - // Do not care about "int" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt deleted file mode 100644 index 48cc62672f7cc..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_parameters4_dnf.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, parameter DNF types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type instanceof ReflectionIntersectionType) { - // Do not care about "(X&Y)" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 ($o) must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 ($o) must be of type (X&Y)|C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt deleted file mode 100644 index f3048b79e743a..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameter1_simple.phpt +++ /dev/null @@ -1,77 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type C, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt deleted file mode 100644 index 705de69b9e6ef..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters2_nullable.phpt +++ /dev/null @@ -1,77 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter nullable types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $type = $param->getType(); - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type ?C, B given, called in %s on line %d -Class: D - Method: bar - Type: ?B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type ?B, A given, called in %s on line %d - Method: ping - Type: ?C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type ?C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type ?C, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt deleted file mode 100644 index 72e97758683f0..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters3_union.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter union types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type->isBuiltin()) { - // Do not care about "int" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 must be of type C|int, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type B|int, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type C|int, A given, called in %s on line %d - C::ping(): Argument #1 must be of type C|int, B given, called in %s on line %d diff --git a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt b/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt deleted file mode 100644 index 6ce61995c52af..0000000000000 --- a/ext/reflection/tests/types/relative_class_types/trait_used_in_classes_variadic_parameters4_dnf.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types ---FILE-- -getMethods(); - foreach ($methods as $method) { - echo "\tMethod: ", $method->name, PHP_EOL; - $parameters = $method->getParameters(); - foreach ($parameters as $param) { - $unionType = $param->getType(); - $types = $unionType->getTypes(); - foreach ($types as $type) { - if ($type instanceof ReflectionIntersectionType) { - // Do not care about "(X&Y)" type here; - continue; - } - echo "\t\tType: ", $type, PHP_EOL; - echo "\t\tInstance of: ", $type::class, PHP_EOL; - - foreach ($instances as $arg) { - try { - $instance->{$method->name}($arg); - } catch (\TypeError $e) { - echo "\t\t\t\t", $e->getMessage(), PHP_EOL; - } - } - } - } - } -} - -?> ---EXPECTF-- -Class: A -Class: B -Class: C - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type (X&Y)|C, B given, called in %s on line %d -Class: D - Method: bar - Type: B - Instance of: ReflectionNamedType - C::bar(): Argument #1 must be of type (X&Y)|B, A given, called in %s on line %d - Method: ping - Type: C - Instance of: ReflectionNamedType - C::ping(): Argument #1 must be of type (X&Y)|C, A given, called in %s on line %d - C::ping(): Argument #1 must be of type (X&Y)|C, B given, called in %s on line %d From 41ef0597a7f28a74b59bb5b7ca3bca0780af0f32 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 15 Apr 2024 14:58:11 +0100 Subject: [PATCH 17/24] Fix memory leak when calling constructor manually --- ext/reflection/php_reflection.c | 13 +++++------ .../ReflectionFunction_construct_recall.phpt | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 ext/reflection/tests/ReflectionFunction_construct_recall.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1a8f7b54c8e0e..d079d22d8eaa7 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1695,13 +1695,12 @@ ZEND_METHOD(ReflectionFunction, __construct) } } - // TODO How can this ever be hit? - //zval *object = ZEND_THIS; - //reflection_object *intern = Z_REFLECTION_P(object); - //if (intern->ptr) { - // zval_ptr_dtor(&intern->obj); - // zval_ptr_dtor(reflection_prop_name(object)); - //} + /* If the constructor is manually called again we need to free the storage */ + reflection_object *intern = Z_REFLECTION_P(ZEND_THIS); + if (intern->ptr) { + zval_ptr_dtor(&intern->obj); + zval_ptr_dtor(reflection_prop_name(ZEND_THIS)); + } reflection_function_create_common(fptr, closure_obj, ZEND_THIS); } diff --git a/ext/reflection/tests/ReflectionFunction_construct_recall.phpt b/ext/reflection/tests/ReflectionFunction_construct_recall.phpt new file mode 100644 index 0000000000000..ed11827a9dd1a --- /dev/null +++ b/ext/reflection/tests/ReflectionFunction_construct_recall.phpt @@ -0,0 +1,22 @@ +--TEST-- +ReflectionFunction constructor called manually after instantiation +--FILE-- + $a . 'hello'; + +$r = new ReflectionFunction($fn1); +var_dump($r); +$r->__construct('strpos'); +var_dump($r); + +?> +--EXPECTF-- +object(ReflectionFunction)#2 (1) { + ["name"]=> + string(%d) "%s" +} +object(ReflectionFunction)#2 (1) { + ["name"]=> + string(6) "strpos" +} From 118ca1c24702ccb24bd3ff8c3ccbe4b700290af8 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 15 Apr 2024 15:05:37 +0100 Subject: [PATCH 18/24] Remove self/parent as known zend_strings --- Zend/zend_string.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index db02e639f6887..910e2eed250fe 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -621,8 +621,6 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_NULL_LOWERCASE, "null") \ _(ZEND_STR_MIXED, "mixed") \ _(ZEND_STR_TRAVERSABLE, "Traversable") \ - _(ZEND_STR_SELF, "self") \ - _(ZEND_STR_PARENT, "parent") \ _(ZEND_STR_SLEEP, "__sleep") \ _(ZEND_STR_WAKEUP, "__wakeup") \ _(ZEND_STR_CASES, "cases") \ From c0a2da30da071931fe907b55194c5a4c61a97831 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 16 Apr 2024 21:07:56 +0100 Subject: [PATCH 19/24] Address review comments --- ...phpt => resolved_relative_type_valid.phpt} | 2 +- ext/reflection/php_reflection.c | 95 ++++++------------- ..._bound_resolvable_relative_types_002.phpt} | 0 .../closure_unresolvable_relative_types.phpt | 6 +- .../unresolved_relative_class_types.phpt | 6 +- ...olved_relative_class_types_union_type.phpt | 6 +- 6 files changed, 39 insertions(+), 76 deletions(-) rename Zend/tests/type_declarations/intersection_types/{resolved_relativre_type_valid.phpt => resolved_relative_type_valid.phpt} (69%) rename ext/reflection/tests/types/relative_class_types/{closure_bound_resolvable_relative_types_arnaud.phpt => closure_bound_resolvable_relative_types_002.phpt} (100%) diff --git a/Zend/tests/type_declarations/intersection_types/resolved_relativre_type_valid.phpt b/Zend/tests/type_declarations/intersection_types/resolved_relative_type_valid.phpt similarity index 69% rename from Zend/tests/type_declarations/intersection_types/resolved_relativre_type_valid.phpt rename to Zend/tests/type_declarations/intersection_types/resolved_relative_type_valid.phpt index 6d5f3c794407f..b25e447aa6fe2 100644 --- a/Zend/tests/type_declarations/intersection_types/resolved_relativre_type_valid.phpt +++ b/Zend/tests/type_declarations/intersection_types/resolved_relative_type_valid.phpt @@ -1,5 +1,5 @@ --TEST-- -parent type cannot take part in an intersection type +parent/self type can take part in an intersection type if it is resolvable at compile time --FILE-- obj, closure_object); - /* If bound to a class */ - //if (object_ce) { - zend_function *fptr = NULL; - zend_class_entry *called_scope = NULL; - zend_object *this_obj = NULL; - closure_object->ce->default_object_handlers->get_closure(closure_object, &called_scope, &fptr, &this_obj, /* check_only */ true); - /* it was bound to something, assign ce to be zend_ce_closure as static can be completely different than self/parent - * and needs to be resolved depending on the type. */ - if (type_kind == SELF_PARENT_TYPE) { - intern->ce = fptr->common.scope; - } else { - intern->ce = called_scope; - } - //} + zend_function *fptr = NULL; + zend_class_entry *called_scope = NULL; + zend_object *this_obj = NULL; + closure_object->ce->default_object_handlers->get_closure(closure_object, &called_scope, &fptr, &this_obj, /* check_only */ true); + /* it was bound to something, assign ce to be zend_ce_closure as static can be completely different than self/parent + * and needs to be resolved depending on the type. */ + if (type_kind == STATIC_TYPE) { + intern->ce = called_scope; + } else { + intern->ce = fptr->common.scope; + } } else { ZVAL_UNDEF(&intern->obj); } @@ -3191,55 +3188,12 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) } GET_REFLECTION_OBJECT_PTR(param); - /* Is closure */ - if (!Z_ISUNDEF(intern->obj)) { - /* Unbound closures can use relative class types */ - if (!intern->ce) { - ZEND_ASSERT(!Z_ISUNDEF(intern->obj)); - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot resolve relative class name for a closure"); - RETURN_THROWS(); - } - - /* Attempt to resolve bound Closure */ - zend_function *fptr = NULL; - zend_class_entry *called_scope = NULL; - zend_object *this_obj = NULL; - - Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &fptr, &this_obj, /* check_only */ true); - - /* Support for legacy behaviour of nullable types and ReflectionNamedType */ - bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL; - zend_type resolved_closure_type; - - if (ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC) { - resolved_closure_type = (zend_type) ZEND_TYPE_INIT_CLASS(called_scope->name, allows_null, /* extra flags */ 0); - } else { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type)); - ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")); - - zend_class_entry *self_ce = fptr->common.scope; - if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self")) { - resolved_closure_type = (zend_type) ZEND_TYPE_INIT_CLASS(self_ce->name, allows_null, /* extra flags */ 0); - } else { - if (!self_ce->parent) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot resolve \"parent\" type when class has no parent"); - RETURN_THROWS(); - } - resolved_closure_type = (zend_type) ZEND_TYPE_INIT_CLASS(self_ce->parent->name, allows_null, /* extra flags */ 0); - } - } - - - reflection_type_factory( - resolved_closure_type, - return_value, - /* legacy_behavior */ true, - intern->ce, - /* closure_object */ NULL - ); - return; + /* Unbound closures can have relative class types that we cannot resolve */ + if (!intern->ce) { + ZEND_ASSERT(!Z_ISUNDEF(intern->obj)); + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve relative class name for a static closure"); + RETURN_THROWS(); } if (intern->ce->ce_flags & ZEND_ACC_TRAIT) { @@ -3248,8 +3202,6 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) RETURN_THROWS(); } - /* For all other case self and parent will have been resolved at compile time */ - ZEND_ASSERT(ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC); /* Support for legacy behaviour of nullable types and ReflectionNamedType */ bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL; zend_type resolved_type; @@ -3259,7 +3211,18 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) "Cannot resolve \"static\" type of an interface"); RETURN_THROWS(); } - resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); + + /* The only cases where self/parent are not resolved at compile time is in Closures */ + if (ZEND_TYPE_IS_COMPLEX(param->type) && zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent")) { + if (!intern->ce->parent) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Cannot resolve \"parent\" type when class has no parent"); + RETURN_THROWS(); + } + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->parent->name, allows_null, /* extra flags */ 0); + } else { + resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0); + } reflection_type_factory( resolved_type, diff --git a/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_arnaud.phpt b/ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_002.phpt similarity index 100% rename from ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_arnaud.phpt rename to ext/reflection/tests/types/relative_class_types/closure_bound_resolvable_relative_types_002.phpt diff --git a/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt b/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt index c4d59178cfa1a..64864ff8b5696 100644 --- a/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt +++ b/ext/reflection/tests/types/relative_class_types/closure_unresolvable_relative_types.phpt @@ -31,10 +31,10 @@ foreach ($instances as $instance) { --EXPECT-- Type: self Instance of: ReflectionRelativeClassType -ReflectionException: Cannot resolve relative class name for a closure +ReflectionException: Cannot resolve relative class name for a static closure Type: parent Instance of: ReflectionRelativeClassType -ReflectionException: Cannot resolve relative class name for a closure +ReflectionException: Cannot resolve relative class name for a static closure Type: static Instance of: ReflectionRelativeClassType -ReflectionException: Cannot resolve relative class name for a closure +ReflectionException: Cannot resolve relative class name for a static closure diff --git a/ext/reflection/tests/types/unresolved_relative_class_types.phpt b/ext/reflection/tests/types/unresolved_relative_class_types.phpt index 169f3d2ec3e94..af748494456de 100644 --- a/ext/reflection/tests/types/unresolved_relative_class_types.phpt +++ b/ext/reflection/tests/types/unresolved_relative_class_types.phpt @@ -56,15 +56,15 @@ foreach ($methods as $method) { Closure: Type: self Instance of: ReflectionRelativeClassType -Cannot resolve relative class name for a closure +Cannot resolve relative class name for a static closure Closure: Type: parent Instance of: ReflectionRelativeClassType -Cannot resolve relative class name for a closure +Cannot resolve relative class name for a static closure Closure: Type: static Instance of: ReflectionRelativeClassType -Cannot resolve relative class name for a closure +Cannot resolve relative class name for a static closure Trait: Method: bar diff --git a/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt b/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt index 3886a4f0c5ddb..e2547b49b495a 100644 --- a/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt +++ b/ext/reflection/tests/types/unresolved_relative_class_types_union_type.phpt @@ -66,15 +66,15 @@ foreach ($methods as $method) { Closure: Type: self Instance of: ReflectionRelativeClassType -Cannot resolve relative class name for a closure +Cannot resolve relative class name for a static closure Closure: Type: parent Instance of: ReflectionRelativeClassType -Cannot resolve relative class name for a closure +Cannot resolve relative class name for a static closure Closure: Type: static Instance of: ReflectionRelativeClassType -Cannot resolve relative class name for a closure +Cannot resolve relative class name for a static closure Trait: Method: bar From 3f275c4b2249dfbe668a4a394b9b02c83adb4956 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 16 Apr 2024 21:09:13 +0100 Subject: [PATCH 20/24] Remove object copy which is not need anymore --- ext/reflection/php_reflection.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index f0f07b3b1c504..a20666e6688f3 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1458,10 +1458,9 @@ static void reflection_type_factory( intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; intern->ce = object_ce; + ZVAL_UNDEF(&intern->obj); if (closure_object) { - ZVAL_OBJ_COPY(&intern->obj, closure_object); - zend_function *fptr = NULL; zend_class_entry *called_scope = NULL; zend_object *this_obj = NULL; @@ -1473,8 +1472,6 @@ static void reflection_type_factory( } else { intern->ce = fptr->common.scope; } - } else { - ZVAL_UNDEF(&intern->obj); } /* Property types may be resolved during the lifetime of the ReflectionType. @@ -3190,7 +3187,6 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType) /* Unbound closures can have relative class types that we cannot resolve */ if (!intern->ce) { - ZEND_ASSERT(!Z_ISUNDEF(intern->obj)); zend_throw_exception_ex(reflection_exception_ptr, 0, "Cannot resolve relative class name for a static closure"); RETURN_THROWS(); From aa79d51d0ba98f9b9a0fbe64a3271b633c73f2ec Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 17 Apr 2024 14:55:11 +0100 Subject: [PATCH 21/24] Add more tests --- ext/reflection/php_reflection.c | 1 - .../closure_automatic_bound_class.phpt | 54 +++++++++++++++++ ..._getDeclaringFunction_type_resolution.phpt | 58 +++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/types/relative_class_types/closure_automatic_bound_class.phpt create mode 100644 ext/reflection/tests/types/relative_class_types/closures_bound_getDeclaringFunction_type_resolution.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a20666e6688f3..80c48cd256f7b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1518,7 +1518,6 @@ static void reflection_method_factory(zend_class_entry *ce, zend_function *metho intern->ref_type = REF_TYPE_FUNCTION; intern->ce = ce; if (closure_object) { - intern->ce = zend_ce_closure; ZVAL_OBJ_COPY(&intern->obj, closure_object); } diff --git a/ext/reflection/tests/types/relative_class_types/closure_automatic_bound_class.phpt b/ext/reflection/tests/types/relative_class_types/closure_automatic_bound_class.phpt new file mode 100644 index 0000000000000..bef1a248bbb10 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/closure_automatic_bound_class.phpt @@ -0,0 +1,54 @@ +--TEST-- +ReflectionTypes of relative class types (self, parent) in Closure bound to class automatically +--FILE-- +getClosureCalledClass()->name, PHP_EOL; + $type = $rc->getReturnType(); + echo "Type: ", $type, PHP_EOL; + echo "Instance of: ", $type::class, PHP_EOL; + try { + $resolvedType = $type->resolveToNamedType(); + echo "\tResolved Type: ", $resolvedType, PHP_EOL; + echo "\tInstance of: ", $resolvedType::class, PHP_EOL; + } catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } + } +} + +$b = new C(); +$b->test(); + + +?> +--EXPECT-- +Bound to: C +Type: self +Instance of: ReflectionRelativeClassType + Resolved Type: B + Instance of: ReflectionNamedType +Bound to: C +Type: parent +Instance of: ReflectionRelativeClassType + Resolved Type: A + Instance of: ReflectionNamedType +Bound to: C +Type: static +Instance of: ReflectionRelativeClassType + Resolved Type: C + Instance of: ReflectionNamedType diff --git a/ext/reflection/tests/types/relative_class_types/closures_bound_getDeclaringFunction_type_resolution.phpt b/ext/reflection/tests/types/relative_class_types/closures_bound_getDeclaringFunction_type_resolution.phpt new file mode 100644 index 0000000000000..dfe1517c7a002 --- /dev/null +++ b/ext/reflection/tests/types/relative_class_types/closures_bound_getDeclaringFunction_type_resolution.phpt @@ -0,0 +1,58 @@ +--TEST-- +Check that a bound Closure retrieved via getDeclaringFunction() can still resolve types +--FILE-- +bindTo($c, B::class); + $rClosure = new ReflectionFunction($fn); + var_dump($rClosure->name == $name); + + $params = $rClosure->getParameters(); + unset ($rClosure); + + $closureFromParam = $params[0]->getDeclaringFunction(); + var_dump($closureFromParam->name == $name); + var_dump($closureFromParam::class); + $type = $closureFromParam->getReturnType(); + var_dump((string) $type); + $resolvedType = $type->resolveToNamedType(); + var_dump((string) $resolvedType); +} + +?> +--EXPECT-- +bool(true) +bool(true) +string(16) "ReflectionMethod" +string(4) "self" +string(1) "B" +bool(true) +bool(true) +string(16) "ReflectionMethod" +string(6) "parent" +string(1) "A" +bool(true) +bool(true) +string(16) "ReflectionMethod" +string(6) "static" +string(1) "C" From 62095c4b4d81979cbef7cfe045ccb810ea3a113d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 17 Apr 2024 22:52:39 +0100 Subject: [PATCH 22/24] Add test failing assertion about memory being freed --- .../relative_type_in_evaled_trait.phpt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt diff --git a/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt b/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt new file mode 100644 index 0000000000000..a5666dd20cce2 --- /dev/null +++ b/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt @@ -0,0 +1,34 @@ +--TEST-- +Eval code should not leak memory when using traits +--FILE-- +bar(); +var_dump($a2); + +//$b1 = new B(); +//$b2 = $b1->bar(); +//var_dump($b2); + +?> +DONE +--EXPECT-- +object(A)#2 (0) { +} +DONE From 5bea1b0885610183326cd9efd49fff5a72ca58df Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 17 Apr 2024 22:53:34 +0100 Subject: [PATCH 23/24] [skip-ci] W.I.P. changes to try and fix test stopping for today --- Zend/zend_inheritance.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 0b2ad43e1d8f9..8102ebd9479ab 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1988,6 +1988,7 @@ static zend_type zend_resolve_type(zend_type type, const zend_class_entry *const return type; } + ZEND_ASSERT(ZEND_TYPE_USES_ARENA(type)); zend_type_list *union_type_list = ZEND_TYPE_LIST(type); bool has_resolved_type = false; /* We don't use ZEND_TYPE_LIST_FOREACH() as we need to keep track of the array index */ @@ -2019,6 +2020,7 @@ static zend_type zend_resolve_type(zend_type type, const zend_class_entry *const ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_ARENA_BIT; /* Inform that the type list is a union type */ ZEND_TYPE_FULL_MASK(new_type) |= _ZEND_TYPE_UNION_BIT; + ZEND_TYPE_FULL_MASK(new_type) = ZEND_TYPE_FULL_MASK(type); return new_type; } else { return type; @@ -2112,7 +2114,7 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_fn, fn, sizeof(zend_op_array)); zend_resolve_trait_relative_class_types(new_fn, ce); - new_fn->op_array.fn_flags |= ZEND_ACC_TRAIT_CLONE; + //new_fn->op_array.fn_flags |= ZEND_ACC_TRAIT_CLONE; new_fn->op_array.fn_flags &= ~ZEND_ACC_IMMUTABLE; } new_fn->common.fn_flags |= ZEND_ACC_TRAIT_CLONE; From 8fc28e9233e46cc55214a118aa5fcaa0f853fc2e Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 18 Apr 2024 16:05:27 +0100 Subject: [PATCH 24/24] [skip-ci] More test and assertion to try to figure out wth is going on Seems like a pointer not allocated via ZMM gets into the arena???? --- ...tive_type_in_evaled_class_using_trait.phpt | 27 +++++++++++++++++++ .../relative_type_in_evaled_trait.phpt | 13 +++------ Zend/zend_opcode.c | 1 + 3 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_class_using_trait.phpt diff --git a/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_class_using_trait.phpt b/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_class_using_trait.phpt new file mode 100644 index 0000000000000..0c0e64cdec86e --- /dev/null +++ b/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_class_using_trait.phpt @@ -0,0 +1,27 @@ +--TEST-- +Eval Class definition should not leak memory when using compiled traits +--FILE-- +bar(); +var_dump($a2); + +?> +DONE +--EXPECT-- +object(A)#2 (0) { +} +DONE diff --git a/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt b/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt index a5666dd20cce2..74e71f6c324e7 100644 --- a/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt +++ b/Zend/tests/type_declarations/relative_class_types/relative_type_in_evaled_trait.phpt @@ -4,28 +4,21 @@ Eval code should not leak memory when using traits bar(); var_dump($a2); -//$b1 = new B(); -//$b2 = $b1->bar(); -//var_dump($b2); - ?> DONE --EXPECT-- diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 8828a03d7b618..403e6587403c3 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -110,6 +110,7 @@ ZEND_API void destroy_zend_function(zend_function *function) ZEND_API void zend_type_release(zend_type type, bool persistent) { if (ZEND_TYPE_HAS_LIST(type)) { + ZEND_ASSERT((!persistent && !ZEND_TYPE_USES_ARENA(type)) && "User defined list types must be arena allocated"); zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { zend_type_release(*list_type, persistent);