Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Optional interfaces #17288

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Zend/zend.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ typedef struct _zend_class_name {
zend_string *lc_name;
} zend_class_name;

typedef struct _zend_interface_name {
zend_string *name;
zend_string *lc_name;
bool is_optional;
} zend_interface_name;

typedef struct _zend_trait_method_reference {
zend_string *method_name;
zend_string *class_name;
Expand Down Expand Up @@ -210,7 +216,7 @@ struct _zend_class_entry {
/* class_entry or string(s) depending on ZEND_ACC_LINKED */
union {
zend_class_entry **interfaces;
zend_class_name *interface_names;
zend_interface_name *interface_names;
};

zend_class_name *trait_names;
Expand Down
8 changes: 5 additions & 3 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8039,12 +8039,13 @@ static void add_stringable_interface(zend_class_entry *ce) {

ce->num_interfaces++;
ce->interface_names =
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
// TODO: Add known interned strings instead?
ce->interface_names[ce->num_interfaces - 1].name =
ZSTR_INIT_LITERAL("Stringable", 0);
ce->interface_names[ce->num_interfaces - 1].lc_name =
ZSTR_INIT_LITERAL("stringable", 0);
ce->interface_names[ce->num_interfaces - 1].is_optional = false;
}

static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, bool has_body) /* {{{ */
Expand Down Expand Up @@ -8950,16 +8951,17 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
zend_class_entry *ce = CG(active_class_entry);
zend_class_name *interface_names;
zend_interface_name *interface_names;
uint32_t i;

interface_names = emalloc(sizeof(zend_class_name) * list->children);
interface_names = emalloc(sizeof(zend_interface_name) * list->children);

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

ce->num_interfaces = list->children;
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define ZEND_NAME_NOT_FQ 1
#define ZEND_NAME_RELATIVE 2

#define ZEND_CLASS_NAME_OPTIONAL 4

/* ZEND_FETCH_ flags in class name AST of new const expression must not clash with ZEND_NAME_ flags */
#define ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT 2

Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,16 @@ void zend_enum_add_interfaces(zend_class_entry *ce)

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

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

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

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

ce->default_object_handlers = &zend_enum_object_handlers;
Expand Down
14 changes: 12 additions & 2 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -3513,18 +3513,26 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
}
}

size_t num_implementable_interfaces = 0;
if (ce->num_interfaces) {
for (i = 0; i < ce->num_interfaces; i++) {
zend_class_entry *iface = zend_fetch_class_by_name(
ce->interface_names[i].name, ce->interface_names[i].lc_name,
ZEND_FETCH_CLASS_INTERFACE |
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION |
(ce->interface_names[i].is_optional ? ZEND_FETCH_CLASS_SILENT : 0));

// Optional interfaces are skipped if they don't exist.
if (!iface && ce->interface_names[i].is_optional) {
continue;
}

if (!iface) {
check_unrecoverable_load_failure(ce);
free_alloca(traits_and_interfaces, use_heap);
return NULL;
}
traits_and_interfaces[ce->num_traits + i] = iface;
traits_and_interfaces[ce->num_traits + num_implementable_interfaces++] = iface;
if (iface) {
UPDATE_IS_CACHEABLE(iface);
}
Expand Down Expand Up @@ -3575,6 +3583,8 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
Z_CE_P(zv) = ce;
}

ce->num_interfaces = num_implementable_interfaces;

if (CG(unlinked_uses)) {
zend_hash_index_del(CG(unlinked_uses), (zend_long)(uintptr_t) ce);
}
Expand Down
23 changes: 17 additions & 6 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> function_name non_empty_member_modifiers
%type <ast> property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body
%type <ast> optional_parameter_list
%type <ast> interface_name_list interface_name

%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
Expand Down Expand Up @@ -355,10 +356,10 @@ legacy_namespace_name:
;

name:
T_STRING { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr = ZEND_NAME_RELATIVE; }
T_STRING { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr |= ZEND_NAME_RELATIVE; }
;

attribute_decl:
Expand Down Expand Up @@ -666,12 +667,12 @@ extends_from:

interface_extends_list:
%empty { $$ = NULL; }
| T_EXTENDS class_name_list { $$ = $2; }
| T_EXTENDS interface_name_list { $$ = $2; }
;

implements_list:
%empty { $$ = NULL; }
| T_IMPLEMENTS class_name_list { $$ = $2; }
| T_IMPLEMENTS interface_name_list { $$ = $2; }
;

foreach_variable:
Expand Down Expand Up @@ -974,6 +975,11 @@ class_name_list:
| class_name_list ',' class_name { $$ = zend_ast_list_add($1, $3); }
;

interface_name_list:
interface_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); }
| interface_name_list ',' interface_name { $$ = zend_ast_list_add($1, $3); }
;

trait_adaptations:
';' { $$ = NULL; }
| '{' '}' { $$ = NULL; }
Expand Down Expand Up @@ -1411,6 +1417,11 @@ class_name:
| name { $$ = $1; }
;

interface_name:
class_name { $$ = $1; }
| '?' name { $$ = $2; $$->attr |= ZEND_CLASS_NAME_OPTIONAL; }
;

class_name_reference:
class_name { $$ = $1; }
| new_variable { $$ = $1; }
Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/zend_file_cache.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ static void zend_file_cache_serialize_class(zval *zv,

if (ce->num_interfaces) {
uint32_t i;
zend_class_name *interface_names;
zend_interface_name *interface_names;

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

Expand Down Expand Up @@ -2022,7 +2022,7 @@ void zend_file_cache_invalidate(zend_string *full_path)
if (ZCG(accel_directives).file_cache_read_only) {
return;
}

char *filename;

filename = zend_file_cache_get_bin_file_path(full_path);
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/zend_persist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
zend_accel_store_interned_string(ce->interface_names[i].name);
zend_accel_store_interned_string(ce->interface_names[i].lc_name);
}
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
}

if (ce->num_traits) {
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/zend_persist_calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ void zend_persist_class_entry_calc(zend_class_entry *ce)
ADD_INTERNED_STRING(ce->interface_names[i].name);
ADD_INTERNED_STRING(ce->interface_names[i].lc_name);
}
ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces);
ADD_SIZE(sizeof(zend_interface_name) * ce->num_interfaces);
}
}

Expand Down
Loading