Skip to content

Commit 8939c4d

Browse files
committed
Get rid of ZEND_ACC_CTOR, ZEND_ACC_DTOR and ZEND_ACC_IMPLEMENTED_ABSTRACT
1 parent 6c1ff61 commit 8939c4d

File tree

20 files changed

+117
-110
lines changed

20 files changed

+117
-110
lines changed

UPGRADING.INTERNALS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PHP 7.4 INTERNALS UPGRADE NOTES
33
1. Internal API changes
44
a. php_sys_symlink() and php_sys_link()
55
b. zend_lookup_class_ex() and zend_fetch_class_by_name()
6+
c. Function flags
67

78
2. Build system changes
89
a. Unix build system changes
@@ -22,6 +23,13 @@ PHP 7.4 INTERNALS UPGRADE NOTES
2223
changed to accept optional lower-case class name as zend_string*,
2324
instead of zval*.
2425

26+
c. Function flags changes
27+
- ZEND_ACC_CTOR and ZEND_ACC_DTOR are removed. It's pissible to check if
28+
method is a constructor/destructor using the following condintion
29+
(func->commpon.scope->constructor == func).
30+
- ZEND_ACC_IMPLEMENTED_ABSTRACT is removed (it was used only internally
31+
during inheritance).
32+
2533
========================
2634
2. Build system changes
2735
========================

Zend/zend_API.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,14 +2410,12 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
24102410
scope->__isset = __isset;
24112411
scope->__debugInfo = __debugInfo;
24122412
if (ctor) {
2413-
ctor->common.fn_flags |= ZEND_ACC_CTOR;
24142413
if (ctor->common.fn_flags & ZEND_ACC_STATIC) {
24152414
zend_error(error_type, "Constructor %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(ctor->common.function_name));
24162415
}
24172416
ctor->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC;
24182417
}
24192418
if (dtor) {
2420-
dtor->common.fn_flags |= ZEND_ACC_DTOR;
24212419
if (dtor->common.fn_flags & ZEND_ACC_STATIC) {
24222420
zend_error(error_type, "Destructor %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(dtor->common.function_name));
24232421
}
@@ -2477,11 +2475,11 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
24772475
}
24782476
}
24792477

2480-
if (ctor && ctor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ctor->common.fn_flags & ZEND_ACC_CTOR) {
2478+
if (ctor && (ctor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
24812479
zend_error_noreturn(E_CORE_ERROR, "Constructor %s::%s() cannot declare a return type", ZSTR_VAL(scope->name), ZSTR_VAL(ctor->common.function_name));
24822480
}
24832481

2484-
if (dtor && dtor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && dtor->common.fn_flags & ZEND_ACC_DTOR) {
2482+
if (dtor && (dtor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
24852483
zend_error_noreturn(E_CORE_ERROR, "Destructor %s::%s() cannot declare a return type", ZSTR_VAL(scope->name), ZSTR_VAL(dtor->common.function_name));
24862484
}
24872485

Zend/zend_builtin_functions.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1300,7 +1300,7 @@ ZEND_FUNCTION(get_class_methods)
13001300
if (!key) {
13011301
ZVAL_STR_COPY(&method_name, mptr->common.function_name);
13021302
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
1303-
} else if ((mptr->common.fn_flags & ZEND_ACC_CTOR) == 0 ||
1303+
} else if (mptr->common.scope->constructor != mptr ||
13041304
mptr->common.scope == ce ||
13051305
zend_binary_strcasecmp(ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(mptr->common.function_name), len) == 0) {
13061306

Zend/zend_compile.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6296,7 +6296,6 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
62966296
}
62976297

62986298
if (ce->constructor) {
6299-
ce->constructor->common.fn_flags |= ZEND_ACC_CTOR;
63006299
if (ce->constructor->common.fn_flags & ZEND_ACC_STATIC) {
63016300
zend_error_noreturn(E_COMPILE_ERROR, "Constructor %s::%s() cannot be static",
63026301
ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
@@ -6308,7 +6307,6 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
63086307
}
63096308
}
63106309
if (ce->destructor) {
6311-
ce->destructor->common.fn_flags |= ZEND_ACC_DTOR;
63126310
if (ce->destructor->common.fn_flags & ZEND_ACC_STATIC) {
63136311
zend_error_noreturn(E_COMPILE_ERROR, "Destructor %s::%s() cannot be static",
63146312
ZSTR_VAL(ce->name), ZSTR_VAL(ce->destructor->common.function_name));

Zend/zend_compile.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,11 @@ typedef struct _zend_oparray_context {
271271
/* Abstarct method | | | */
272272
#define ZEND_ACC_ABSTRACT (1 << 1) /* | X | | */
273273
/* | | | */
274-
/* TODO: used only during inheritance ??? | | | */
275-
#define ZEND_ACC_IMPLEMENTED_ABSTRACT (1 << 3) /* | X | | */
276-
/* | | | */
277274
#define ZEND_ACC_FAKE_CLOSURE (1 << 6) /* | X | | */
278275
/* | | | */
279276
/* method flag used by Closure::__invoke() | | | */
280277
#define ZEND_ACC_USER_ARG_INFO (1 << 7) /* | X | | */
281278
/* | | | */
282-
/* method flags (special method detection) | | | */
283-
#define ZEND_ACC_CTOR (1 << 13) /* | X | | */
284-
#define ZEND_ACC_DTOR (1 << 14) /* | X | | */
285-
/* | | | */
286279
/* "main" op_array with | | | */
287280
/* ZEND_DECLARE_INHERITED_CLASS_DELAYED opcodes | | | */
288281
#define ZEND_ACC_EARLY_BINDING (1 << 15) /* | X | | */

Zend/zend_inheritance.c

Lines changed: 82 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -90,22 +90,24 @@ static zend_function *zend_duplicate_function(zend_function *func, zend_class_en
9090

9191
static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */
9292
{
93-
ZEND_ASSERT(ce->parent != NULL);
93+
zend_class_entry *parent = ce->parent;
94+
95+
ZEND_ASSERT(parent != NULL);
9496

9597
/* You cannot change create_object */
96-
ce->create_object = ce->parent->create_object;
98+
ce->create_object = parent->create_object;
9799

98100
/* Inherit special functions if needed */
99101
if (EXPECTED(!ce->get_iterator)) {
100-
ce->get_iterator = ce->parent->get_iterator;
102+
ce->get_iterator = parent->get_iterator;
101103
}
102-
if (EXPECTED(!ce->iterator_funcs_ptr) && UNEXPECTED(ce->parent->iterator_funcs_ptr)) {
104+
if (EXPECTED(!ce->iterator_funcs_ptr) && UNEXPECTED(parent->iterator_funcs_ptr)) {
103105
if (ce->type == ZEND_INTERNAL_CLASS) {
104106
ce->iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
105-
if (ce->parent->iterator_funcs_ptr->zf_new_iterator) {
107+
if (parent->iterator_funcs_ptr->zf_new_iterator) {
106108
ce->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&ce->function_table, "getiterator", sizeof("getiterator") - 1);
107109
}
108-
if (ce->parent->iterator_funcs_ptr->zf_current) {
110+
if (parent->iterator_funcs_ptr->zf_current) {
109111
ce->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&ce->function_table, "rewind", sizeof("rewind") - 1);
110112
ce->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&ce->function_table, "valid", sizeof("valid") - 1);
111113
ce->iterator_funcs_ptr->zf_key = zend_hash_str_find_ptr(&ce->function_table, "key", sizeof("key") - 1);
@@ -118,52 +120,52 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */
118120
}
119121
}
120122
if (EXPECTED(!ce->__get)) {
121-
ce->__get = ce->parent->__get;
123+
ce->__get = parent->__get;
122124
}
123125
if (EXPECTED(!ce->__set)) {
124-
ce->__set = ce->parent->__set;
126+
ce->__set = parent->__set;
125127
}
126128
if (EXPECTED(!ce->__unset)) {
127-
ce->__unset = ce->parent->__unset;
129+
ce->__unset = parent->__unset;
128130
}
129131
if (EXPECTED(!ce->__isset)) {
130-
ce->__isset = ce->parent->__isset;
132+
ce->__isset = parent->__isset;
131133
}
132134
if (EXPECTED(!ce->__call)) {
133-
ce->__call = ce->parent->__call;
135+
ce->__call = parent->__call;
134136
}
135137
if (EXPECTED(!ce->__callstatic)) {
136-
ce->__callstatic = ce->parent->__callstatic;
138+
ce->__callstatic = parent->__callstatic;
137139
}
138140
if (EXPECTED(!ce->__tostring)) {
139-
ce->__tostring = ce->parent->__tostring;
141+
ce->__tostring = parent->__tostring;
140142
}
141143
if (EXPECTED(!ce->clone)) {
142-
ce->clone = ce->parent->clone;
144+
ce->clone = parent->clone;
143145
}
144146
if (EXPECTED(!ce->serialize)) {
145-
ce->serialize = ce->parent->serialize;
147+
ce->serialize = parent->serialize;
146148
}
147149
if (EXPECTED(!ce->unserialize)) {
148-
ce->unserialize = ce->parent->unserialize;
150+
ce->unserialize = parent->unserialize;
149151
}
150152
if (!ce->destructor) {
151-
ce->destructor = ce->parent->destructor;
153+
ce->destructor = parent->destructor;
152154
}
153155
if (EXPECTED(!ce->__debugInfo)) {
154-
ce->__debugInfo = ce->parent->__debugInfo;
156+
ce->__debugInfo = parent->__debugInfo;
155157
}
156158

157159
if (ce->constructor) {
158-
if (ce->parent->constructor && UNEXPECTED(ce->parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) {
160+
if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) {
159161
zend_error_noreturn(E_ERROR, "Cannot override final %s::%s() with %s::%s()",
160-
ZSTR_VAL(ce->parent->name), ZSTR_VAL(ce->parent->constructor->common.function_name),
162+
ZSTR_VAL(parent->name), ZSTR_VAL(parent->constructor->common.function_name),
161163
ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
162164
}
163165
return;
164166
}
165167

166-
ce->constructor = ce->parent->constructor;
168+
ce->constructor = parent->constructor;
167169
}
168170
/* }}} */
169171

@@ -284,14 +286,14 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
284286
* we still need to do the arg number checks. We are only willing to ignore this for internal
285287
* functions because extensions don't always define arg_info.
286288
*/
287-
if (!proto || (!proto->common.arg_info && proto->common.type != ZEND_USER_FUNCTION)) {
289+
if (!proto->common.arg_info && proto->common.type != ZEND_USER_FUNCTION) {
288290
return 1;
289291
}
290292

291293
/* Checks for constructors only if they are declared in an interface,
292294
* or explicitly marked as abstract
293295
*/
294-
if ((fe->common.fn_flags & ZEND_ACC_CTOR)
296+
if ((fe->common.scope->constructor == fe)
295297
&& ((proto->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
296298
&& (proto->common.fn_flags & ZEND_ACC_ABSTRACT) == 0)) {
297299
return 1;
@@ -584,55 +586,64 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
584586
zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child));
585587
}
586588

587-
/* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */
588-
if (UNEXPECTED((!(child_flags & ZEND_ACC_CTOR) || (parent_flags & (ZEND_ACC_ABSTRACT | ZEND_ACC_IMPLEMENTED_ABSTRACT))) &&
589-
(child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) {
590-
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
591-
}
592-
593589
if ((child_flags & ZEND_ACC_PRIVATE) < (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED))) {
594590
child->common.fn_flags |= ZEND_ACC_CHANGED;
595591
}
596592

597-
if (parent_flags & ZEND_ACC_PRIVATE) {
598-
child->common.prototype = NULL;
599-
} else if (parent_flags & ZEND_ACC_ABSTRACT) {
600-
child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT;
601-
child->common.prototype = parent;
602-
} else if (!(parent->common.fn_flags & ZEND_ACC_CTOR)) {
603-
child->common.prototype = parent->common.prototype ? parent->common.prototype : parent;
604-
} else if (parent->common.prototype && (parent->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE)) {
605-
/* ctors only have a prototype if it comes from an interface */
606-
child->common.prototype = parent->common.prototype ? parent->common.prototype : parent;
607-
/* and if that is the case, we want to check inheritance against it */
608-
parent = child->common.prototype;
609-
}
610-
611-
if (UNEXPECTED(!zend_do_perform_implementation_check(child, parent))) {
612-
int error_level;
613-
const char *error_verb;
614-
zend_string *method_prototype = zend_get_function_declaration(parent);
615-
zend_string *child_prototype = zend_get_function_declaration(child);
616-
617-
if (child->common.prototype && (
618-
child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT
619-
)) {
620-
error_level = E_COMPILE_ERROR;
621-
error_verb = "must";
622-
} else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
623-
(!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
624-
!zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) ||
625-
(ZEND_TYPE_ALLOW_NULL(child->common.arg_info[-1].type) && !ZEND_TYPE_ALLOW_NULL(parent->common.arg_info[-1].type)))) {
626-
error_level = E_COMPILE_ERROR;
627-
error_verb = "must";
628-
} else {
629-
error_level = E_WARNING;
630-
error_verb = "should";
593+
do {
594+
if (!(parent_flags & ZEND_ACC_PRIVATE)) {
595+
if (parent_flags & ZEND_ACC_ABSTRACT) {
596+
child->common.prototype = parent;
597+
} else {
598+
zend_function *proto = parent->common.prototype;
599+
600+
if (parent->common.scope->constructor != parent) {
601+
child->common.prototype = proto ? proto : parent;
602+
} else if (proto) {
603+
if (proto->common.scope->ce_flags & ZEND_ACC_INTERFACE) {
604+
/* ctors only have a prototype if it comes from an interface */
605+
child->common.prototype = proto;
606+
/* and if that is the case, we want to check inheritance against it */
607+
parent = proto;
608+
} else if (!(proto->common.fn_flags & ZEND_ACC_ABSTRACT)) {
609+
break;
610+
}
611+
} else {
612+
break;
613+
}
614+
}
615+
/* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */
616+
if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
617+
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
618+
}
619+
620+
if (UNEXPECTED(!zend_do_perform_implementation_check(child, parent))) {
621+
int error_level;
622+
const char *error_verb;
623+
zend_string *method_prototype = zend_get_function_declaration(parent);
624+
zend_string *child_prototype = zend_get_function_declaration(child);
625+
626+
if (child->common.prototype && (
627+
child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT
628+
)) {
629+
error_level = E_COMPILE_ERROR;
630+
error_verb = "must";
631+
} else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
632+
(!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
633+
!zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) ||
634+
(ZEND_TYPE_ALLOW_NULL(child->common.arg_info[-1].type) && !ZEND_TYPE_ALLOW_NULL(parent->common.arg_info[-1].type)))) {
635+
error_level = E_COMPILE_ERROR;
636+
error_verb = "must";
637+
} else {
638+
error_level = E_WARNING;
639+
error_verb = "should";
640+
}
641+
zend_error(error_level, "Declaration of %s %s be compatible with %s", ZSTR_VAL(child_prototype), error_verb, ZSTR_VAL(method_prototype));
642+
zend_string_efree(child_prototype);
643+
zend_string_efree(method_prototype);
644+
}
631645
}
632-
zend_error(error_level, "Declaration of %s %s be compatible with %s", ZSTR_VAL(child_prototype), error_verb, ZSTR_VAL(method_prototype));
633-
zend_string_efree(child_prototype);
634-
zend_string_efree(method_prototype);
635-
}
646+
} while (0);
636647
}
637648
/* }}} */
638649

@@ -1202,11 +1213,10 @@ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zen
12021213
zend_string *lowercase_name = zend_string_tolower(ce->name);
12031214
lowercase_name = zend_new_interned_string(lowercase_name);
12041215
if (!memcmp(ZSTR_VAL(mname), ZSTR_VAL(lowercase_name), ZSTR_LEN(mname))) {
1205-
if (ce->constructor && (!ce->parent || ce->constructor != ce->parent->constructor)) {
1216+
if (ce->constructor && (!ce->parent || ce->constructor != ce->parent->constructor)) {
12061217
zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ZSTR_VAL(ce->name));
12071218
}
12081219
ce->constructor = fe;
1209-
fe->common.fn_flags |= ZEND_ACC_CTOR;
12101220
}
12111221
zend_string_release_ex(lowercase_name, 0);
12121222
} else if (ZSTR_VAL(mname)[0] != '_' || ZSTR_VAL(mname)[1] != '_') {
@@ -1217,9 +1227,9 @@ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zen
12171227
if (ce->constructor && (!ce->parent || ce->constructor != ce->parent->constructor)) {
12181228
zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ZSTR_VAL(ce->name));
12191229
}
1220-
ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR;
1230+
ce->constructor = fe;
12211231
} else if (zend_string_equals_literal(mname, ZEND_DESTRUCTOR_FUNC_NAME)) {
1222-
ce->destructor = fe; fe->common.fn_flags |= ZEND_ACC_DTOR;
1232+
ce->destructor = fe;
12231233
} else if (zend_string_equals_literal(mname, ZEND_GET_FUNC_NAME)) {
12241234
ce->__get = fe;
12251235
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
@@ -1903,7 +1913,7 @@ static void zend_verify_abstract_class_function(zend_function *fn, zend_abstract
19031913
if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
19041914
ai->afn[ai->cnt] = fn;
19051915
}
1906-
if (fn->common.fn_flags & ZEND_ACC_CTOR) {
1916+
if (fn->common.scope->constructor == fn) {
19071917
if (!ai->ctor) {
19081918
ai->cnt++;
19091919
ai->ctor = 1;

Zend/zend_interfaces.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ ZEND_END_ARG_INFO()
548548

549549
static const zend_function_entry zend_funcs_serializable[] = {
550550
ZEND_ABSTRACT_ME(serializable, serialize, NULL)
551-
ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR)
551+
ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
552552
ZEND_FE_END
553553
};
554554

ext/curl/curl_file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ ZEND_END_ARG_INFO()
152152

153153

154154
static const zend_function_entry curlfile_funcs[] = {
155-
PHP_ME(CURLFile, __construct, arginfo_curlfile_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
155+
PHP_ME(CURLFile, __construct, arginfo_curlfile_create, ZEND_ACC_PUBLIC)
156156
PHP_ME(CURLFile, getFilename, NULL, ZEND_ACC_PUBLIC)
157157
PHP_ME(CURLFile, getMimeType, NULL, ZEND_ACC_PUBLIC)
158158
PHP_ME(CURLFile, setMimeType, arginfo_curlfile_name, ZEND_ACC_PUBLIC)

0 commit comments

Comments
 (0)