Skip to content

Commit 660ce19

Browse files
committed
Add syntax for optional interfaces
1 parent 65524e5 commit 660ce19

File tree

9 files changed

+50
-16
lines changed

9 files changed

+50
-16
lines changed

Zend/zend.h

100644100755
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ typedef struct _zend_class_name {
8787
zend_string *lc_name;
8888
} zend_class_name;
8989

90+
typedef struct _zend_interface_name {
91+
zend_string *name;
92+
zend_string *lc_name;
93+
bool is_optional;
94+
} zend_interface_name;
95+
9096
typedef struct _zend_trait_method_reference {
9197
zend_string *method_name;
9298
zend_string *class_name;
@@ -210,7 +216,7 @@ struct _zend_class_entry {
210216
/* class_entry or string(s) depending on ZEND_ACC_LINKED */
211217
union {
212218
zend_class_entry **interfaces;
213-
zend_class_name *interface_names;
219+
zend_interface_name *interface_names;
214220
};
215221

216222
zend_class_name *trait_names;

Zend/zend_compile.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8039,12 +8039,13 @@ static void add_stringable_interface(zend_class_entry *ce) {
80398039

80408040
ce->num_interfaces++;
80418041
ce->interface_names =
8042-
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
8042+
erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
80438043
// TODO: Add known interned strings instead?
80448044
ce->interface_names[ce->num_interfaces - 1].name =
80458045
ZSTR_INIT_LITERAL("Stringable", 0);
80468046
ce->interface_names[ce->num_interfaces - 1].lc_name =
80478047
ZSTR_INIT_LITERAL("stringable", 0);
8048+
ce->interface_names[ce->num_interfaces - 1].is_optional = false;
80488049
}
80498050

80508051
static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, bool has_body) /* {{{ */
@@ -8950,16 +8951,17 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */
89508951
{
89518952
zend_ast_list *list = zend_ast_get_list(ast);
89528953
zend_class_entry *ce = CG(active_class_entry);
8953-
zend_class_name *interface_names;
8954+
zend_interface_name *interface_names;
89548955
uint32_t i;
89558956

8956-
interface_names = emalloc(sizeof(zend_class_name) * list->children);
8957+
interface_names = emalloc(sizeof(zend_interface_name) * list->children);
89578958

89588959
for (i = 0; i < list->children; ++i) {
89598960
zend_ast *class_ast = list->child[i];
89608961
interface_names[i].name =
89618962
zend_resolve_const_class_name_reference(class_ast, "interface name");
89628963
interface_names[i].lc_name = zend_string_tolower(interface_names[i].name);
8964+
interface_names[i].is_optional = ZEND_CLASS_NAME_OPTIONAL & class_ast->attr;
89638965
}
89648966

89658967
ce->num_interfaces = list->children;

Zend/zend_compile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
10351035
#define ZEND_NAME_NOT_FQ 1
10361036
#define ZEND_NAME_RELATIVE 2
10371037

1038+
#define ZEND_CLASS_NAME_OPTIONAL 4
1039+
10381040
/* ZEND_FETCH_ flags in class name AST of new const expression must not clash with ZEND_NAME_ flags */
10391041
#define ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT 2
10401042

Zend/zend_enum.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,16 @@ void zend_enum_add_interfaces(zend_class_entry *ce)
180180

181181
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));
182182

183-
ce->interface_names = erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
183+
ce->interface_names = erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
184184

185185
ce->interface_names[num_interfaces_before].name = zend_string_copy(zend_ce_unit_enum->name);
186186
ce->interface_names[num_interfaces_before].lc_name = ZSTR_INIT_LITERAL("unitenum", 0);
187+
ce->interface_names[num_interfaces_before].is_optional = false;
187188

188189
if (ce->enum_backing_type != IS_UNDEF) {
189190
ce->interface_names[num_interfaces_before + 1].name = zend_string_copy(zend_ce_backed_enum->name);
190191
ce->interface_names[num_interfaces_before + 1].lc_name = ZSTR_INIT_LITERAL("backedenum", 0);
192+
ce->interface_names[num_interfaces_before + 1].is_optional = false;
191193
}
192194

193195
ce->default_object_handlers = &zend_enum_object_handlers;

Zend/zend_inheritance.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3513,23 +3513,32 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
35133513
}
35143514
}
35153515

3516+
size_t num_implementable_interfaces = 0;
35163517
if (ce->num_interfaces) {
35173518
for (i = 0; i < ce->num_interfaces; i++) {
35183519
zend_class_entry *iface = zend_fetch_class_by_name(
35193520
ce->interface_names[i].name, ce->interface_names[i].lc_name,
35203521
ZEND_FETCH_CLASS_INTERFACE |
3521-
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
3522+
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION |
3523+
(ce->interface_names[i].is_optional ? ZEND_FETCH_CLASS_SILENT : 0));
3524+
3525+
// Optional interfaces are skipped if they don't exist.
3526+
if (!iface && ce->interface_names[i].is_optional) {
3527+
continue;
3528+
}
3529+
35223530
if (!iface) {
35233531
check_unrecoverable_load_failure(ce);
35243532
free_alloca(traits_and_interfaces, use_heap);
35253533
return NULL;
35263534
}
3527-
traits_and_interfaces[ce->num_traits + i] = iface;
3535+
traits_and_interfaces[ce->num_traits + num_implementable_interfaces++] = iface;
35283536
if (iface) {
35293537
UPDATE_IS_CACHEABLE(iface);
35303538
}
35313539
}
35323540
}
3541+
ce->num_interfaces = num_implementable_interfaces;
35333542

35343543
#ifndef ZEND_WIN32
35353544
if (ce->ce_flags & ZEND_ACC_ENUM) {

Zend/zend_language_parser.y

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
285285
%type <ast> function_name non_empty_member_modifiers
286286
%type <ast> property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body
287287
%type <ast> optional_parameter_list
288+
%type <ast> interface_name_list interface_name
288289

289290
%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
290291
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
@@ -355,10 +356,10 @@ legacy_namespace_name:
355356
;
356357

357358
name:
358-
T_STRING { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
359-
| T_NAME_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
360-
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_FQ; }
361-
| T_NAME_RELATIVE { $$ = $1; $$->attr = ZEND_NAME_RELATIVE; }
359+
T_STRING { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
360+
| T_NAME_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
361+
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_FQ; }
362+
| T_NAME_RELATIVE { $$ = $1; $$->attr |= ZEND_NAME_RELATIVE; }
362363
;
363364

364365
attribute_decl:
@@ -666,12 +667,12 @@ extends_from:
666667

667668
interface_extends_list:
668669
%empty { $$ = NULL; }
669-
| T_EXTENDS class_name_list { $$ = $2; }
670+
| T_EXTENDS interface_name_list { $$ = $2; }
670671
;
671672

672673
implements_list:
673674
%empty { $$ = NULL; }
674-
| T_IMPLEMENTS class_name_list { $$ = $2; }
675+
| T_IMPLEMENTS interface_name_list { $$ = $2; }
675676
;
676677

677678
foreach_variable:
@@ -974,6 +975,11 @@ class_name_list:
974975
| class_name_list ',' class_name { $$ = zend_ast_list_add($1, $3); }
975976
;
976977

978+
interface_name_list:
979+
interface_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); }
980+
| interface_name_list ',' interface_name { $$ = zend_ast_list_add($1, $3); }
981+
;
982+
977983
trait_adaptations:
978984
';' { $$ = NULL; }
979985
| '{' '}' { $$ = NULL; }
@@ -1411,6 +1417,11 @@ class_name:
14111417
| name { $$ = $1; }
14121418
;
14131419

1420+
interface_name:
1421+
class_name { $$ = $1; }
1422+
| '?' name { $$ = $2; $$->attr |= ZEND_CLASS_NAME_OPTIONAL; }
1423+
;
1424+
14141425
class_name_reference:
14151426
class_name { $$ = $1; }
14161427
| new_variable { $$ = $1; }

ext/opcache/zend_file_cache.c

100644100755
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ static void zend_file_cache_serialize_class(zval *zv,
792792

793793
if (ce->num_interfaces) {
794794
uint32_t i;
795-
zend_class_name *interface_names;
795+
zend_interface_name *interface_names;
796796

797797
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
798798

@@ -803,6 +803,7 @@ static void zend_file_cache_serialize_class(zval *zv,
803803
for (i = 0; i < ce->num_interfaces; i++) {
804804
SERIALIZE_STR(interface_names[i].name);
805805
SERIALIZE_STR(interface_names[i].lc_name);
806+
// SERIALIZE_BOOL(interface_names[i].is_optional) ?
806807
}
807808
}
808809

ext/opcache/zend_persist.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,8 +1048,9 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
10481048
for (i = 0; i < ce->num_interfaces; i++) {
10491049
zend_accel_store_interned_string(ce->interface_names[i].name);
10501050
zend_accel_store_interned_string(ce->interface_names[i].lc_name);
1051+
// TODO: should we store .is_optional here? How?
10511052
}
1052-
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
1053+
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
10531054
}
10541055

10551056
if (ce->num_traits) {

ext/opcache/zend_persist_calc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ void zend_persist_class_entry_calc(zend_class_entry *ce)
519519
ADD_INTERNED_STRING(ce->interface_names[i].name);
520520
ADD_INTERNED_STRING(ce->interface_names[i].lc_name);
521521
}
522-
ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces);
522+
ADD_SIZE(sizeof(zend_interface_name) * ce->num_interfaces);
523523
}
524524
}
525525

0 commit comments

Comments
 (0)