diff --git a/Zend/tests/friends/001.phpt b/Zend/tests/friends/001.phpt new file mode 100644 index 0000000000000..3a1148f562bb0 --- /dev/null +++ b/Zend/tests/friends/001.phpt @@ -0,0 +1,8 @@ +--TEST-- +Circular Friendship +--FILE-- + +--EXPECTF-- +Fatal error: Class A may not be friends with itself in %s/001.php on line %d diff --git a/Zend/tests/friends/002.phpt b/Zend/tests/friends/002.phpt new file mode 100644 index 0000000000000..94ce708d06d7a --- /dev/null +++ b/Zend/tests/friends/002.phpt @@ -0,0 +1,10 @@ +--TEST-- +Circular Friendship Parent +--FILE-- + +--EXPECTF-- +Fatal error: Class B may not be friends with parent A in %s/002.php on line %d diff --git a/Zend/tests/friends/003.phpt b/Zend/tests/friends/003.phpt new file mode 100644 index 0000000000000..01cfe68d09d7f --- /dev/null +++ b/Zend/tests/friends/003.phpt @@ -0,0 +1,11 @@ +--TEST-- +No Friendship +--FILE-- + +--EXPECTF-- +Fatal error: Class C is not a friend of A in %s/003.php on line %d + diff --git a/Zend/tests/friends/004.phpt b/Zend/tests/friends/004.phpt new file mode 100644 index 0000000000000..e6721e44984f0 --- /dev/null +++ b/Zend/tests/friends/004.phpt @@ -0,0 +1,21 @@ +--TEST-- +Friendship +--FILE-- +getFriendNames()); +} +?> +--EXPECT-- +array(1) { + [0]=> + string(1) "B" +} +array(0) { +} diff --git a/Zend/tests/friends/005.phpt b/Zend/tests/friends/005.phpt new file mode 100644 index 0000000000000..4cc7acf81c5a0 --- /dev/null +++ b/Zend/tests/friends/005.phpt @@ -0,0 +1,26 @@ +--TEST-- +Friendship Inheritance +--FILE-- +getFriendNames()); +} +?> +--EXPECT-- +array(2) { + [0]=> + string(1) "B" + [1]=> + string(1) "C" +} +array(1) { + [0]=> + string(1) "C" +} + diff --git a/Zend/tests/friends/006.phpt b/Zend/tests/friends/006.phpt new file mode 100644 index 0000000000000..f5bff4a2fbf7d --- /dev/null +++ b/Zend/tests/friends/006.phpt @@ -0,0 +1,8 @@ +--TEST-- +Circular Friendship Interface +--FILE-- + +--EXPECTF-- +Fatal error: Interface A may not be friends with itself in %s/006.php on line %d diff --git a/Zend/tests/friends/007.phpt b/Zend/tests/friends/007.phpt new file mode 100644 index 0000000000000..0e44f02370faf --- /dev/null +++ b/Zend/tests/friends/007.phpt @@ -0,0 +1,10 @@ +--TEST-- +Circular Friendship Parent Interface +--FILE-- + +--EXPECTF-- +Fatal error: Interface B may not be friends with parent A in %s/007.php on line %d diff --git a/Zend/tests/friends/008.phpt b/Zend/tests/friends/008.phpt new file mode 100644 index 0000000000000..4dc79fc56cda1 --- /dev/null +++ b/Zend/tests/friends/008.phpt @@ -0,0 +1,10 @@ +--TEST-- +No Friendship Interface +--FILE-- + +--EXPECTF-- +Fatal error: Class C is not a friend of A in %s/008.php on line %d diff --git a/Zend/tests/friends/009.phpt b/Zend/tests/friends/009.phpt new file mode 100644 index 0000000000000..a63dc1da956e4 --- /dev/null +++ b/Zend/tests/friends/009.phpt @@ -0,0 +1,21 @@ +--TEST-- +Friendship Interfaces +--FILE-- +getFriendNames()); +} +?> +--EXPECT-- +array(1) { + [0]=> + string(1) "B" +} +array(0) { +} diff --git a/Zend/tests/friends/010.phpt b/Zend/tests/friends/010.phpt new file mode 100644 index 0000000000000..68fbf66be0597 --- /dev/null +++ b/Zend/tests/friends/010.phpt @@ -0,0 +1,26 @@ +--TEST-- +Friendship Inheritance Interfaces +--FILE-- +getFriendNames()); +} +?> +--EXPECT-- +array(2) { + [0]=> + string(1) "B" + [1]=> + string(1) "C" +} +array(1) { + [0]=> + string(1) "C" +} + diff --git a/Zend/zend.h b/Zend/zend.h index d58c02f4475b4..2acc05040a242 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -161,7 +161,7 @@ struct _zend_class_entry { HashTable function_table; HashTable properties_info; HashTable constants_table; - + ZEND_MAP_PTR_DEF(zend_class_mutable_data*, mutable_data); zend_inheritance_cache_entry *inheritance_cache; @@ -198,9 +198,12 @@ struct _zend_class_entry { int (*serialize)(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data); int (*unserialize)(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data); + uint32_t num_friends; uint32_t num_interfaces; uint32_t num_traits; + zend_class_name *friends; + /* class_entry or string(s) depending on ZEND_ACC_LINKED */ union { zend_class_entry **interfaces; diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index f1ea28112bdde..199ea69b02569 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -111,7 +111,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast * ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4, zend_ast *child5 ) { zend_ast_decl *ast; @@ -129,6 +129,7 @@ ZEND_API zend_ast *zend_ast_create_decl( ast->child[2] = child2; ast->child[3] = child3; ast->child[4] = child4; + ast->child[5] = child5; return (zend_ast *) ast; } diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index d2940bbe633a8..29fe424de74b6 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -66,6 +66,7 @@ enum _zend_ast_kind { ZEND_AST_ATTRIBUTE_LIST, ZEND_AST_ATTRIBUTE_GROUP, ZEND_AST_MATCH_ARM_LIST, + ZEND_AST_CLASS_FRIEND_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -210,7 +211,7 @@ typedef struct _zend_ast_decl { unsigned char *lex_pos; zend_string *doc_comment; zend_string *name; - zend_ast *child[5]; + zend_ast *child[6]; } zend_ast_decl; typedef void (*zend_ast_process_t)(zend_ast *ast); @@ -294,7 +295,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4, zend_ast *child5 ); ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 039e95913f6d3..c476707b761a4 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1784,6 +1784,8 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand ce->attributes = NULL; ce->enum_backing_type = IS_UNDEF; ce->backed_enum_table = NULL; + ce->num_friends = 0; + ce->friends = NULL; if (nullify_handlers) { ce->constructor = NULL; @@ -7576,6 +7578,52 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 0); } +static void zend_compile_class_friends(zend_class_entry *ce, zend_ast *ast) { + uint32_t i; + zend_ast_list *list = zend_ast_get_list(ast); + + ce->num_friends = list->children; + ce->friends = ecalloc( + sizeof(zend_class_name), list->children); + + for (i = 0; i < list->children; i++) { + zend_ast *class_ast = list->child[i]; + + ce->friends[i].name = + zend_resolve_const_class_name_reference( + class_ast, "class name"); + ce->friends[i].lc_name = zend_string_tolower(ce->friends[i].name); + + if (zend_string_equals_ci(ce->name, ce->friends[i].lc_name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s %s may not be friends with itself", + (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class", + ZSTR_VAL(ce->name)); + } + + if (ce->parent_name && + zend_string_equals_ci(ce->parent_name, ce->friends[i].lc_name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s %s may not be friends with parent %s", + (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class", + ZSTR_VAL(ce->name), ZSTR_VAL(ce->parent_name)); + } + + if (ce->num_interfaces) { + uint32_t ii = 0; + + for (ii = 0; ii < ce->num_interfaces; ii++) { + if (zend_string_equals(ce->interface_names[ii].lc_name, ce->friends[i].lc_name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s %s may not be friends with parent %s", + (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class", + ZSTR_VAL(ce->name), ZSTR_VAL(ce->interface_names[ii].name)); + } + } + } + } +} + static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -7583,6 +7631,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) zend_ast *implements_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; zend_ast *enum_backing_type_ast = decl->child[4]; + zend_ast *friends_ast = decl->child[5]; zend_string *name, *lcname; zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); zend_op *opline; @@ -7666,6 +7715,10 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) zend_compile_implements(implements_ast); } + if (friends_ast) { + zend_compile_class_friends(ce, friends_ast); + } + if (ce->ce_flags & ZEND_ACC_ENUM) { if (enum_backing_type_ast != NULL) { zend_compile_enum_backing_type(ce, enum_backing_type_ast); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 82af3d8afa75a..d1c200150c62f 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1404,6 +1404,69 @@ void zend_build_properties_info_table(zend_class_entry *ce) } ZEND_HASH_FOREACH_END(); } +void do_inherit_friends(zend_class_entry *ce, zend_class_entry *parent) { + if (!ce->num_friends) { + if (parent->num_friends == 1) { + return; + } + + zend_class_name *friend = ce->friends = + ecalloc( + sizeof(zend_class_name), parent->num_friends); + + zend_class_name *begin = parent->friends, + *end = begin + parent->num_friends; + + while (begin < end) { + if (UNEXPECTED(!begin->name)) { + begin++; + continue; + } + + if (UNEXPECTED(zend_string_equals_ci(ce->name, begin->name))) { + begin++; + continue; + } + + memcpy(friend, begin, sizeof(zend_class_name)); + zend_string_addref(friend->name); + zend_string_addref(friend->lc_name); + + ce->num_friends++; + friend++; + begin++; + } + } else { + ce->friends = erealloc(ce->friends, + sizeof(zend_class_name) * + (ce->num_friends + parent->num_friends)); + + zend_class_name *begin = parent->friends, + *end = begin + parent->num_friends, + *friend = ce->friends + ce->num_friends; + + while (begin < end) { + if (UNEXPECTED(!begin->name)) { + begin++; + continue; + } + + if (UNEXPECTED(zend_string_equals_ci(ce->name, begin->name))) { + begin++; + continue; + } + + memcpy(friend, begin, sizeof(zend_class_name)); + zend_string_addref(friend->name); + zend_string_addref(friend->lc_name); + + ce->num_friends++; + friend++; + begin++; + } + } +} + ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */ { zend_property_info *property_info; @@ -1429,6 +1492,35 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par } } + if (UNEXPECTED(parent_ce->num_friends)) { + zend_class_name *begin = parent_ce->friends, + *end = begin + parent_ce->num_friends; + bool friend = false; + + while (begin < end) { + if (!begin->name) { + begin++; + continue; + } + + if (zend_string_equals_ci(begin->lc_name, ce->name)) { + friend = true; + break; + } + + begin++; + } + + if (!friend) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s %s is not a friend of %s", + (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class", + ZSTR_VAL(ce->name), ZSTR_VAL(parent_ce->name)); + } + + do_inherit_friends(ce, parent_ce); + } + if (ce->parent_name) { zend_string_release_ex(ce->parent_name, 0); } @@ -2743,6 +2835,38 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string free_alloca(traits_and_interfaces, use_heap); return NULL; } + + if (UNEXPECTED(iface->num_friends)) { + zend_class_name *begin = iface->friends, + *end = begin + iface->num_friends; + bool friend = false; + + while (begin < end) { + if (!begin->name) { + begin++; + continue; + } + + if (zend_string_equals_ci(begin->lc_name, ce->name)) { + friend = true; + break; + } + + begin++; + } + + if (!friend) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s %s is not a friend of %s", + (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class", + ZSTR_VAL(ce->name), ZSTR_VAL(iface->name)); + } + + if ((ce->ce_flags & ZEND_ACC_INTERFACE)) { + do_inherit_friends(ce, iface); + } + } + traits_and_interfaces[ce->num_traits + i] = iface; if (iface) { UPDATE_IS_CACHEABLE(iface); @@ -2829,6 +2953,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string zend_do_implement_interfaces(ce, interfaces); } else if (parent && parent->num_interfaces) { zend_do_inherit_interfaces(ce, parent); + } if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) && (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index f6ba5465abebb..a87b71a4bb53d 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -278,6 +278,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type attribute_decl attribute attributes attribute_group namespace_declaration_name %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list %type enum_declaration_statement enum_backing_type enum_case enum_case_expr +%type class_friend_modifiers class_friend_list %type returns_ref function fn is_reference is_variadic variable_modifiers %type method_modifiers non_empty_member_modifiers member_modifier @@ -564,7 +565,7 @@ function_declaration_statement: function returns_ref T_STRING backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $13, $1, $4, - zend_ast_get_str($3), $6, NULL, $11, $8, NULL); CG(extra_fn_flags) = $9; } + zend_ast_get_str($3), $6, NULL, $11, $8, NULL, NULL); CG(extra_fn_flags) = $9; } ; is_reference: @@ -579,11 +580,11 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } + T_STRING class_friend_modifiers extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, NULL, NULL, $5); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } + T_STRING class_friend_modifiers extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, NULL, NULL, $4); } ; class_modifiers: @@ -600,21 +601,32 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } T_STRING backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL, NULL); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } - T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); } + T_STRING class_friend_modifiers interface_extends_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, NULL, $4); } ; enum_declaration_statement: T_ENUM { $$ = CG(zend_lineno); } T_STRING enum_backing_type implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ENUM|ZEND_ACC_FINAL, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ENUM|ZEND_ACC_FINAL, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4, NULL); } ; +class_friend_modifiers: + %empty { $$ = 0; } + | T_FOR class_friend_list { $$ = $2; } +; + +class_friend_list: + class_name { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_FRIEND_LIST, $1); } + | class_friend_list ',' class_name { $$ = zend_ast_list_add($1, $3); } +; + + enum_backing_type: %empty { $$ = NULL; } | ':' type_expr { $$ = $2; } @@ -916,7 +928,7 @@ attributed_class_statement: | method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags method_body backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, - zend_ast_get_str($4), $7, NULL, $11, $9, NULL); CG(extra_fn_flags) = $10; } + zend_ast_get_str($4), $7, NULL, $11, $9, NULL, NULL); CG(extra_fn_flags) = $10; } | enum_case { $$ = $1; } ; @@ -1059,7 +1071,7 @@ anonymous_class: extends_from implements_list backup_doc_comment '{' class_statement_list '}' { zend_ast *decl = zend_ast_create_decl( ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, NULL, - $4, $5, $8, NULL, NULL); + $4, $5, $8, NULL, NULL, NULL); $$ = zend_ast_create(ZEND_AST_NEW, decl, $3); } ; @@ -1205,12 +1217,12 @@ inline_function: backup_fn_flags '{' inner_statement_list '}' backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), - $5, $7, $11, $8, NULL); CG(extra_fn_flags) = $9; } + $5, $7, $11, $8, NULL, NULL); CG(extra_fn_flags) = $9; } | fn returns_ref backup_doc_comment '(' parameter_list ')' return_type T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $3, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $5, NULL, - zend_ast_create(ZEND_AST_RETURN, $11), $7, NULL); + zend_ast_create(ZEND_AST_RETURN, $11), $7, NULL, NULL); ((zend_ast_decl *) $$)->lex_pos = $10; CG(extra_fn_flags) = $9; } ; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 9e27c2fea142b..6d661134966c4 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -346,6 +346,16 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->num_traits > 0) { _destroy_zend_class_traits_info(ce); } + + if (ce->num_friends > 0) { + uint32_t i; + + for (i = 0; i < ce->num_friends; i++) { + zend_string_release_ex(ce->friends[i].name, 0); + zend_string_release_ex(ce->friends[i].lc_name, 0); + } + efree(ce->friends); + } } if (ce->default_properties_table) { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 84ef8bdfbb450..0f3444e00b45b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4156,6 +4156,31 @@ ZEND_METHOD(ReflectionClass, __toString) } /* }}} */ +/* {{{ */ +ZEND_METHOD(ReflectionClass, getFriendNames) +{ + reflection_object *intern; + zend_class_entry *ce; + uint32_t i; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ce); + + array_init(return_value); + + for (i = 0; i < ce->num_friends; i++) { + if (!ce->friends[i].name) { + continue; + } + + add_next_index_str(return_value, + zend_string_copy(ce->friends[i].name)); + } +} /* }}} */ + /* {{{ Returns the class' name */ ZEND_METHOD(ReflectionClass, getName) { diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 8f2ef6ec6f44e..4e5cfc413b09d 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -219,6 +219,9 @@ public function __construct(object|string $objectOrClass) {} public function __toString(): string {} + /** @return array */ + public function getFriendNames() : array {} + /** @tentative-return-type */ public function getName(): string {}