Skip to content

Commit 7e7c0d7

Browse files
krakjoeazjezz
authored andcommitted
interfaces
1 parent 85005f7 commit 7e7c0d7

File tree

8 files changed

+142
-19
lines changed

8 files changed

+142
-19
lines changed

Zend/tests/friends/006.phpt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
--TEST--
2+
Circular Friendship Interface
3+
--FILE--
4+
<?php
5+
interface A for A,B {}
6+
?>
7+
--EXPECTF--
8+
Fatal error: Interface A may not be friends with itself in %s/006.php on line %d

Zend/tests/friends/007.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Circular Friendship Parent Interface
3+
--FILE--
4+
<?php
5+
interface A for B {}
6+
7+
interface B for A extends A {}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Interface B may not be friends with parent A in %s/007.php on line %d

Zend/tests/friends/008.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
No Friendship Interface
3+
--FILE--
4+
<?php
5+
interface A for B {}
6+
7+
class C implements A {}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Class C is not a friend of A in %s/008.php on line %d

Zend/tests/friends/009.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Friendship Interfaces
3+
--FILE--
4+
<?php
5+
interface A for B {}
6+
7+
interface B extends A {}
8+
9+
foreach ([A::class, B::class] as $class) {
10+
$reflector = new ReflectionClass($class);
11+
12+
var_dump($reflector->getFriendNames());
13+
}
14+
?>
15+
--EXPECT--
16+
array(1) {
17+
[0]=>
18+
string(1) "B"
19+
}
20+
array(0) {
21+
}

Zend/tests/friends/010.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Friendship Inheritance Interfaces
3+
--FILE--
4+
<?php
5+
interface A for B,C {}
6+
7+
interface B extends A {}
8+
9+
foreach ([A::class, B::class] as $class) {
10+
$reflector = new ReflectionClass($class);
11+
12+
var_dump($reflector->getFriendNames());
13+
}
14+
?>
15+
--EXPECT--
16+
array(2) {
17+
[0]=>
18+
string(1) "B"
19+
[1]=>
20+
string(1) "C"
21+
}
22+
array(1) {
23+
[0]=>
24+
string(1) "C"
25+
}
26+

Zend/zend_compile.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7594,16 +7594,31 @@ static void zend_compile_class_friends(zend_class_entry *ce, zend_ast *ast) {
75947594

75957595
if (zend_string_equals_ci(ce->name, friends[i].lc_name)) {
75967596
zend_error_noreturn(E_COMPILE_ERROR,
7597-
"Class %s may not be friends with itself",
7597+
"%s %s may not be friends with itself",
7598+
(ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class",
75987599
ZSTR_VAL(ce->name));
75997600
}
76007601

76017602
if (ce->parent_name &&
76027603
zend_string_equals_ci(ce->parent_name, friends[i].lc_name)) {
76037604
zend_error_noreturn(E_COMPILE_ERROR,
7604-
"Class %s may not be friends with parent %s",
7605+
"%s %s may not be friends with parent %s",
7606+
(ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class",
76057607
ZSTR_VAL(ce->name), ZSTR_VAL(ce->parent_name));
76067608
}
7609+
7610+
if (ce->num_interfaces) {
7611+
uint32_t ii = 0;
7612+
7613+
for (ii = 0; ii < ce->num_interfaces; ii++) {
7614+
if (zend_string_equals(ce->interface_names[ii].lc_name, friends[i].lc_name)) {
7615+
zend_error_noreturn(E_COMPILE_ERROR,
7616+
"%s %s may not be friends with parent %s",
7617+
(ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class",
7618+
ZSTR_VAL(ce->name), ZSTR_VAL(ce->interface_names[ii].name));
7619+
}
7620+
}
7621+
}
76077622
}
76087623

76097624
ce->num_friends = list->children;
@@ -7691,10 +7706,6 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
76917706
zend_resolve_const_class_name_reference(extends_ast, "class name");
76927707
}
76937708

7694-
if (friends_ast) {
7695-
zend_compile_class_friends(ce, friends_ast);
7696-
}
7697-
76987709
CG(active_class_entry) = ce;
76997710

77007711
if (decl->child[3]) {
@@ -7705,6 +7716,10 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
77057716
zend_compile_implements(implements_ast);
77067717
}
77077718

7719+
if (friends_ast) {
7720+
zend_compile_class_friends(ce, friends_ast);
7721+
}
7722+
77087723
if (ce->ce_flags & ZEND_ACC_ENUM) {
77097724
if (enum_backing_type_ast != NULL) {
77107725
zend_compile_enum_backing_type(ce, enum_backing_type_ast);

Zend/zend_inheritance.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1513,7 +1513,8 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
15131513

15141514
if (!friend) {
15151515
zend_error_noreturn(E_COMPILE_ERROR,
1516-
"Class %s is not a friend of %s",
1516+
"%s %s is not a friend of %s",
1517+
(ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class",
15171518
ZSTR_VAL(ce->name), ZSTR_VAL(parent_ce->name));
15181519
}
15191520

@@ -2834,6 +2835,36 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
28342835
free_alloca(traits_and_interfaces, use_heap);
28352836
return NULL;
28362837
}
2838+
2839+
if (UNEXPECTED(iface->num_friends)) {
2840+
zend_class_name *begin = iface->friends,
2841+
*end = begin + iface->num_friends;
2842+
bool friend = false;
2843+
2844+
while (begin < end) {
2845+
if (!begin->name) {
2846+
begin++;
2847+
continue;
2848+
}
2849+
2850+
if (zend_string_equals_ci(begin->lc_name, ce->name)) {
2851+
friend = true;
2852+
break;
2853+
}
2854+
2855+
begin++;
2856+
}
2857+
2858+
if (!friend) {
2859+
zend_error_noreturn(E_COMPILE_ERROR,
2860+
"%s %s is not a friend of %s",
2861+
(ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : "Class",
2862+
ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
2863+
}
2864+
2865+
do_inherit_friends(ce, iface);
2866+
}
2867+
28372868
traits_and_interfaces[ce->num_traits + i] = iface;
28382869
if (iface) {
28392870
UPDATE_IS_CACHEABLE(iface);
@@ -2920,6 +2951,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
29202951
zend_do_implement_interfaces(ce, interfaces);
29212952
} else if (parent && parent->num_interfaces) {
29222953
zend_do_inherit_interfaces(ce, parent);
2954+
29232955
}
29242956
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT))
29252957
&& (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))

Zend/zend_language_parser.y

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -587,16 +587,6 @@ class_declaration_statement:
587587
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $<num>2, $7, zend_ast_get_str($3), $5, $6, $9, NULL, NULL, $4); }
588588
;
589589

590-
class_friend_modifiers:
591-
%empty { $$ = 0; }
592-
| T_FOR class_friend_list { $$ = $2; }
593-
;
594-
595-
class_friend_list:
596-
class_name { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_FRIEND_LIST, $1); }
597-
| class_friend_list ',' class_name { $$ = zend_ast_list_add($1, $3); }
598-
;
599-
600590
class_modifiers:
601591
class_modifier { $$ = $1; }
602592
| class_modifiers class_modifier
@@ -616,8 +606,8 @@ trait_declaration_statement:
616606

617607
interface_declaration_statement:
618608
T_INTERFACE { $<num>$ = CG(zend_lineno); }
619-
T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}'
620-
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $<num>2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL, NULL); }
609+
T_STRING class_friend_modifiers interface_extends_list backup_doc_comment '{' class_statement_list '}'
610+
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $<num>2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, NULL, $4); }
621611
;
622612

623613
enum_declaration_statement:
@@ -626,6 +616,17 @@ enum_declaration_statement:
626616
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ENUM|ZEND_ACC_FINAL, $<num>2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4, NULL); }
627617
;
628618

619+
class_friend_modifiers:
620+
%empty { $$ = 0; }
621+
| T_FOR class_friend_list { $$ = $2; }
622+
;
623+
624+
class_friend_list:
625+
class_name { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_FRIEND_LIST, $1); }
626+
| class_friend_list ',' class_name { $$ = zend_ast_list_add($1, $3); }
627+
;
628+
629+
629630
enum_backing_type:
630631
%empty { $$ = NULL; }
631632
| ':' type_expr { $$ = $2; }

0 commit comments

Comments
 (0)