summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikic@php.net>2014-09-19 00:01:05 +0200
committerNikita Popov <nikic@php.net>2014-09-19 19:54:37 +0200
commitc343ca4efba7e600f7d20c73fde34dbda8192e48 (patch)
tree55d4aa3f431db584f6d8ae2c34f6ff2406994a65
parent2be8fdcfd6b719cf21e4344c75b46fd5704727e4 (diff)
downloadphp-git-c343ca4efba7e600f7d20c73fde34dbda8192e48.tar.gz
Split inheritance into separate file
This moves handling of inheritance and interface implementation from zend_compile.c into a separate zend_inheritance.c file, as this is not really related to compilation.
-rw-r--r--Zend/Zend.dsp4
-rw-r--r--Zend/ZendTS.dsp4
-rw-r--r--Zend/zend_API.c1
-rw-r--r--Zend/zend_compile.c1545
-rw-r--r--Zend/zend_compile.h8
-rw-r--r--Zend/zend_execute.c1
-rw-r--r--Zend/zend_inheritance.c1574
-rw-r--r--Zend/zend_inheritance.h39
-rw-r--r--configure.in7
9 files changed, 1626 insertions, 1557 deletions
diff --git a/Zend/Zend.dsp b/Zend/Zend.dsp
index 98d368fb16..c1b3f2a9f7 100644
--- a/Zend/Zend.dsp
+++ b/Zend/Zend.dsp
@@ -179,6 +179,10 @@ SOURCE=.\zend_indent.c
# End Source File
# Begin Source File
+SOURCE=.\zend_inheritance.c
+# End Source File
+# Begin Source File
+
SOURCE=.\zend_ini.c
# End Source File
# Begin Source File
diff --git a/Zend/ZendTS.dsp b/Zend/ZendTS.dsp
index 21210f1087..4c6ef92465 100644
--- a/Zend/ZendTS.dsp
+++ b/Zend/ZendTS.dsp
@@ -205,6 +205,10 @@ SOURCE=.\zend_indent.c
# End Source File
# Begin Source File
+SOURCE=.\zend_inheritance.c
+# End Source File
+# Begin Source File
+
SOURCE=.\zend_ini.c
# End Source File
# Begin Source File
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 981e42e41f..41adbeba8f 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -27,6 +27,7 @@
#include "zend_constants.h"
#include "zend_exceptions.h"
#include "zend_closures.h"
+#include "zend_inheritance.h"
#ifdef HAVE_STDARG_H
#include <stdarg.h>
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 4205a942e0..9cb6f0609e 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -30,6 +30,7 @@
#include "zend_virtual_cwd.h"
#include "zend_multibyte.h"
#include "zend_language_scanner.h"
+#include "zend_inheritance.h"
#define CONSTANT_EX(op_array, op) \
(op_array)->literals[op]
@@ -76,29 +77,6 @@ ZEND_API zend_compiler_globals compiler_globals;
ZEND_API zend_executor_globals executor_globals;
#endif
-static zend_property_info *zend_duplicate_property_info(zend_property_info *property_info TSRMLS_DC) /* {{{ */
-{
- zend_property_info* new_property_info;
-
- new_property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
- memcpy(new_property_info, property_info, sizeof(zend_property_info));
- zend_string_addref(new_property_info->name);
- if (new_property_info->doc_comment) {
- zend_string_addref(new_property_info->doc_comment);
- }
- return new_property_info;
-}
-/* }}} */
-
-static zend_property_info *zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */
-{
- zend_property_info* new_property_info = pemalloc(sizeof(zend_property_info), 1);
- memcpy(new_property_info, property_info, sizeof(zend_property_info));
- zend_string_addref(new_property_info->name);
- return new_property_info;
-}
-/* }}} */
-
static void zend_destroy_property_info(zval *zv) /* {{{ */
{
zend_property_info *property_info = Z_PTR_P(zv);
@@ -932,1527 +910,6 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
}
/* }}} */
-static void do_inherit_parent_constructor(zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
- zend_function *function, *new_function;
-
- if (!ce->parent) {
- return;
- }
-
- /* You cannot change create_object */
- ce->create_object = ce->parent->create_object;
-
- /* Inherit special functions if needed */
- if (!ce->get_iterator) {
- ce->get_iterator = ce->parent->get_iterator;
- }
- if (!ce->iterator_funcs.funcs) {
- ce->iterator_funcs.funcs = ce->parent->iterator_funcs.funcs;
- }
- if (!ce->__get) {
- ce->__get = ce->parent->__get;
- }
- if (!ce->__set) {
- ce->__set = ce->parent->__set;
- }
- if (!ce->__unset) {
- ce->__unset = ce->parent->__unset;
- }
- if (!ce->__isset) {
- ce->__isset = ce->parent->__isset;
- }
- if (!ce->__call) {
- ce->__call = ce->parent->__call;
- }
- if (!ce->__callstatic) {
- ce->__callstatic = ce->parent->__callstatic;
- }
- if (!ce->__tostring) {
- ce->__tostring = ce->parent->__tostring;
- }
- if (!ce->clone) {
- ce->clone = ce->parent->clone;
- }
- if(!ce->serialize) {
- ce->serialize = ce->parent->serialize;
- }
- if(!ce->unserialize) {
- ce->unserialize = ce->parent->unserialize;
- }
- if (!ce->destructor) {
- ce->destructor = ce->parent->destructor;
- }
- if (!ce->__debugInfo) {
- ce->__debugInfo = ce->parent->__debugInfo;
- }
- if (ce->constructor) {
- if (ce->parent->constructor && ce->parent->constructor->common.fn_flags & ZEND_ACC_FINAL) {
- zend_error(E_ERROR, "Cannot override final %s::%s() with %s::%s()",
- ce->parent->name->val, ce->parent->constructor->common.function_name->val,
- ce->name->val, ce->constructor->common.function_name->val
- );
- }
- return;
- }
-
- if ((function = zend_hash_str_find_ptr(&ce->parent->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1)) != NULL) {
- /* inherit parent's constructor */
- if (function->type == ZEND_INTERNAL_FUNCTION) {
- new_function = pemalloc(sizeof(zend_internal_function), 1);
- memcpy(new_function, function, sizeof(zend_internal_function));
- } else {
- new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
- memcpy(new_function, function, sizeof(zend_op_array));
- }
- zend_hash_str_update_ptr(&ce->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1, new_function);
- function_add_ref(new_function);
- } else {
- /* Don't inherit the old style constructor if we already have the new style constructor */
- zend_string *lc_class_name;
- zend_string *lc_parent_class_name;
-
- lc_class_name = zend_string_alloc(ce->name->len, 0);
- zend_str_tolower_copy(lc_class_name->val, ce->name->val, ce->name->len);
- if (!zend_hash_exists(&ce->function_table, lc_class_name)) {
- lc_parent_class_name = zend_string_alloc(ce->parent->name->len, 0);
- zend_str_tolower_copy(lc_parent_class_name->val, ce->parent->name->val, ce->parent->name->len);
- if (!zend_hash_exists(&ce->function_table, lc_parent_class_name) &&
- (function = zend_hash_find_ptr(&ce->parent->function_table, lc_parent_class_name)) != NULL) {
- if (function->common.fn_flags & ZEND_ACC_CTOR) {
- /* inherit parent's constructor */
- new_function = pemalloc(sizeof(zend_function), function->type == ZEND_INTERNAL_FUNCTION);
- memcpy(new_function, function, sizeof(zend_function));
- zend_hash_update_ptr(&ce->function_table, lc_parent_class_name, new_function);
- function_add_ref(new_function);
- }
- }
- zend_string_release(lc_parent_class_name);
- }
- zend_string_free(lc_class_name);
- }
- ce->constructor = ce->parent->constructor;
-}
-/* }}} */
-
-char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
-{
- if (fn_flags & ZEND_ACC_PRIVATE) {
- return "private";
- }
- if (fn_flags & ZEND_ACC_PROTECTED) {
- return "protected";
- }
- if (fn_flags & ZEND_ACC_PUBLIC) {
- return "public";
- }
- return "";
-}
-/* }}} */
-
-static zend_function *do_inherit_method(zend_function *old_function TSRMLS_DC) /* {{{ */
-{
- zend_function *new_function;
-
- if (old_function->type == ZEND_INTERNAL_FUNCTION) {
- new_function = pemalloc(sizeof(zend_internal_function), 1);
- memcpy(new_function, old_function, sizeof(zend_internal_function));
- } else {
- new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
- memcpy(new_function, old_function, sizeof(zend_op_array));
- }
- /* The class entry of the derived function intentionally remains the same
- * as that of the parent class. That allows us to know in which context
- * we're running, and handle private method calls properly.
- */
- function_add_ref(new_function);
- return new_function;
-}
-/* }}} */
-
-static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
-{
- uint32_t i, num_args;
-
- /* If it's a user function then arg_info == NULL means we don't have any parameters but
- * we still need to do the arg number checks. We are only willing to ignore this for internal
- * functions because extensions don't always define arg_info.
- */
- if (!proto || (!proto->common.arg_info && proto->common.type != ZEND_USER_FUNCTION)) {
- return 1;
- }
-
- /* Checks for constructors only if they are declared in an interface,
- * or explicitly marked as abstract
- */
- if ((fe->common.fn_flags & ZEND_ACC_CTOR)
- && ((proto->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
- && (proto->common.fn_flags & ZEND_ACC_ABSTRACT) == 0)) {
- return 1;
- }
-
- /* If both methods are private do not enforce a signature */
- if ((fe->common.fn_flags & ZEND_ACC_PRIVATE) && (proto->common.fn_flags & ZEND_ACC_PRIVATE)) {
- return 1;
- }
-
- /* check number of arguments */
- if (proto->common.required_num_args < fe->common.required_num_args
- || proto->common.num_args > fe->common.num_args) {
- return 0;
- }
-
- /* by-ref constraints on return values are covariant */
- if ((proto->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
- && !(fe->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
- return 0;
- }
-
- if ((proto->common.fn_flags & ZEND_ACC_VARIADIC)
- && !(fe->common.fn_flags & ZEND_ACC_VARIADIC)) {
- return 0;
- }
-
- /* For variadic functions any additional (optional) arguments that were added must be
- * checked against the signature of the variadic argument, so in this case we have to
- * go through all the parameters of the function and not just those present in the
- * prototype. */
- num_args = proto->common.num_args;
- if ((proto->common.fn_flags & ZEND_ACC_VARIADIC)
- && fe->common.num_args > proto->common.num_args) {
- num_args = fe->common.num_args;
- }
-
- for (i = 0; i < num_args; i++) {
- zend_arg_info *fe_arg_info = &fe->common.arg_info[i];
-
- zend_arg_info *proto_arg_info;
- if (i < proto->common.num_args) {
- proto_arg_info = &proto->common.arg_info[i];
- } else {
- proto_arg_info = &proto->common.arg_info[proto->common.num_args-1];
- }
-
- if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) {
- /* Only one has a type hint and the other one doesn't */
- return 0;
- }
-
- if (fe_arg_info->class_name) {
- zend_string *fe_class_name, *proto_class_name;
-
- if (!strcasecmp(fe_arg_info->class_name, "parent") && proto->common.scope) {
- fe_class_name = zend_string_copy(proto->common.scope->name);
- } else if (!strcasecmp(fe_arg_info->class_name, "self") && fe->common.scope) {
- fe_class_name = zend_string_copy(fe->common.scope->name);
- } else {
- fe_class_name = zend_string_init(
- fe_arg_info->class_name,
- fe_arg_info->class_name_len, 0);
- }
-
- if (!strcasecmp(proto_arg_info->class_name, "parent") && proto->common.scope && proto->common.scope->parent) {
- proto_class_name = zend_string_copy(proto->common.scope->parent->name);
- } else if (!strcasecmp(proto_arg_info->class_name, "self") && proto->common.scope) {
- proto_class_name = zend_string_copy(proto->common.scope->name);
- } else {
- proto_class_name = zend_string_init(
- proto_arg_info->class_name,
- proto_arg_info->class_name_len, 0);
- }
-
- if (strcasecmp(fe_class_name->val, proto_class_name->val)!=0) {
- const char *colon;
-
- if (fe->common.type != ZEND_USER_FUNCTION) {
- zend_string_release(proto_class_name);
- zend_string_release(fe_class_name);
- return 0;
- } else if (strchr(proto_class_name->val, '\\') != NULL ||
- (colon = zend_memrchr(fe_class_name->val, '\\', fe_class_name->len)) == NULL ||
- strcasecmp(colon+1, proto_class_name->val) != 0) {
- zend_class_entry *fe_ce, *proto_ce;
-
- fe_ce = zend_lookup_class(fe_class_name TSRMLS_CC);
- proto_ce = zend_lookup_class(proto_class_name TSRMLS_CC);
-
- /* Check for class alias */
- if (!fe_ce || !proto_ce ||
- fe_ce->type == ZEND_INTERNAL_CLASS ||
- proto_ce->type == ZEND_INTERNAL_CLASS ||
- fe_ce != proto_ce) {
- zend_string_release(proto_class_name);
- zend_string_release(fe_class_name);
- return 0;
- }
- }
- }
- zend_string_release(proto_class_name);
- zend_string_release(fe_class_name);
- }
- if (fe_arg_info->type_hint != proto_arg_info->type_hint) {
- /* Incompatible type hint */
- return 0;
- }
-
- /* by-ref constraints on arguments are invariant */
- if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) {
- return 0;
- }
- }
-
- return 1;
-}
-/* }}} */
-
-#define REALLOC_BUF_IF_EXCEED(buf, offset, length, size) \
- if (UNEXPECTED(offset - buf + size >= length)) { \
- length += size + 1; \
- buf = erealloc(buf, length); \
- }
-
-static char *zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */
-{
- char *offset, *buf;
- uint32_t length = 1024;
-
- offset = buf = (char *)emalloc(length * sizeof(char));
- if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
- *(offset++) = '&';
- *(offset++) = ' ';
- }
-
- if (fptr->common.scope) {
- memcpy(offset, fptr->common.scope->name->val, fptr->common.scope->name->len);
- offset += fptr->common.scope->name->len;
- *(offset++) = ':';
- *(offset++) = ':';
- }
-
- {
- size_t name_len = fptr->common.function_name->len;
- REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len);
- memcpy(offset, fptr->common.function_name->val, name_len);
- offset += name_len;
- }
-
- *(offset++) = '(';
- if (fptr->common.arg_info) {
- uint32_t i, required;
- zend_arg_info *arg_info = fptr->common.arg_info;
-
- required = fptr->common.required_num_args;
- for (i = 0; i < fptr->common.num_args;) {
- if (arg_info->class_name) {
- const char *class_name;
- uint32_t class_name_len;
- if (!strcasecmp(arg_info->class_name, "self") && fptr->common.scope ) {
- class_name = fptr->common.scope->name->val;
- class_name_len = fptr->common.scope->name->len;
- } else if (!strcasecmp(arg_info->class_name, "parent") && fptr->common.scope->parent) {
- class_name = fptr->common.scope->parent->name->val;
- class_name_len = fptr->common.scope->parent->name->len;
- } else {
- class_name = arg_info->class_name;
- class_name_len = arg_info->class_name_len;
- }
- REALLOC_BUF_IF_EXCEED(buf, offset, length, class_name_len);
- memcpy(offset, class_name, class_name_len);
- offset += class_name_len;
- *(offset++) = ' ';
- } else if (arg_info->type_hint) {
- uint32_t type_name_len;
- char *type_name = zend_get_type_by_const(arg_info->type_hint);
- type_name_len = strlen(type_name);
- REALLOC_BUF_IF_EXCEED(buf, offset, length, type_name_len);
- memcpy(offset, type_name, type_name_len);
- offset += type_name_len;
- *(offset++) = ' ';
- }
-
- if (arg_info->pass_by_reference) {
- *(offset++) = '&';
- }
-
- if (arg_info->is_variadic) {
- *(offset++) = '.';
- *(offset++) = '.';
- *(offset++) = '.';
- }
-
- *(offset++) = '$';
-
- if (arg_info->name) {
- REALLOC_BUF_IF_EXCEED(buf, offset, length, arg_info->name_len);
- memcpy(offset, arg_info->name, arg_info->name_len);
- offset += arg_info->name_len;
- } else {
- uint32_t idx = i;
- memcpy(offset, "param", 5);
- offset += 5;
- do {
- *(offset++) = (char) (idx % 10) + '0';
- idx /= 10;
- } while (idx > 0);
- }
- if (i >= required && !arg_info->is_variadic) {
- *(offset++) = ' ';
- *(offset++) = '=';
- *(offset++) = ' ';
- if (fptr->type == ZEND_USER_FUNCTION) {
- zend_op *precv = NULL;
- {
- uint32_t idx = i;
- zend_op *op = ((zend_op_array *)fptr)->opcodes;
- zend_op *end = op + ((zend_op_array *)fptr)->last;
-
- ++idx;
- while (op < end) {
- if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT)
- && op->op1.num == (zend_ulong)idx)
- {
- precv = op;
- }
- ++op;
- }
- }
- if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
- zval *zv = precv->op2.zv;
-
- if (Z_TYPE_P(zv) == IS_CONSTANT) {
- REALLOC_BUF_IF_EXCEED(buf, offset, length, Z_STRLEN_P(zv));
- memcpy(offset, Z_STRVAL_P(zv), Z_STRLEN_P(zv));
- offset += Z_STRLEN_P(zv);
- } else if (Z_TYPE_P(zv) == IS_FALSE) {
- memcpy(offset, "false", 5);
- offset += 5;
- } else if (Z_TYPE_P(zv) == IS_TRUE) {
- memcpy(offset, "true", 4);
- offset += 4;
- } else if (Z_TYPE_P(zv) == IS_NULL) {
- memcpy(offset, "NULL", 4);
- offset += 4;
- } else if (Z_TYPE_P(zv) == IS_STRING) {
- *(offset++) = '\'';
- REALLOC_BUF_IF_EXCEED(buf, offset, length, MIN(Z_STRLEN_P(zv), 10));
- memcpy(offset, Z_STRVAL_P(zv), MIN(Z_STRLEN_P(zv), 10));
- offset += MIN(Z_STRLEN_P(zv), 10);
- if (Z_STRLEN_P(zv) > 10) {
- *(offset++) = '.';
- *(offset++) = '.';
- *(offset++) = '.';
- }
- *(offset++) = '\'';
- } else if (Z_TYPE_P(zv) == IS_ARRAY) {
- memcpy(offset, "Array", 5);
- offset += 5;
- } else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
- memcpy(offset, "<expression>", 12);
- offset += 12;
- } else {
- zend_string *str = zval_get_string(zv);
- REALLOC_BUF_IF_EXCEED(buf, offset, length, str->len);
- memcpy(offset, str->val, str->len);
- offset += str->len;
- zend_string_release(str);
- }
- }
- } else {
- memcpy(offset, "NULL", 4);
- offset += 4;
- }
- }
-
- if (++i < fptr->common.num_args) {
- *(offset++) = ',';
- *(offset++) = ' ';
- }
- arg_info++;
- REALLOC_BUF_IF_EXCEED(buf, offset, length, 32);
- }
- }
- *(offset++) = ')';
- *offset = '\0';
-
- return buf;
-}
-/* }}} */
-
-static void do_inheritance_check_on_method(zend_function *child, zend_function *parent TSRMLS_DC) /* {{{ */
-{
- uint32_t child_flags;
- uint32_t parent_flags = parent->common.fn_flags;
-
- if ((parent->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
- && parent->common.fn_flags & ZEND_ACC_ABSTRACT
- && parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope)
- && child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)",
- parent->common.scope->name->val,
- child->common.function_name->val,
- child->common.prototype ? child->common.prototype->common.scope->name->val : child->common.scope->name->val);
- }
-
- if (parent_flags & ZEND_ACC_FINAL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot override final method %s::%s()", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val);
- }
-
- child_flags = child->common.fn_flags;
- /* You cannot change from static to non static and vice versa.
- */
- if ((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC)) {
- if (child->common.fn_flags & ZEND_ACC_STATIC) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non static method %s::%s() static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child));
- } else {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot make static method %s::%s() non static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child));
- }
- }
-
- /* Disallow making an inherited method abstract. */
- if ((child_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_ABSTRACT)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child));
- }
-
- if (parent_flags & ZEND_ACC_CHANGED) {
- child->common.fn_flags |= ZEND_ACC_CHANGED;
- } else {
- /* Prevent derived classes from restricting access that was available in parent classes
- */
- if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
- } else if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
- && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
- child->common.fn_flags |= ZEND_ACC_CHANGED;
- }
- }
-
- if (parent_flags & ZEND_ACC_PRIVATE) {
- child->common.prototype = NULL;
- } else if (parent_flags & ZEND_ACC_ABSTRACT) {
- child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT;
- child->common.prototype = parent;
- } else if (!(parent->common.fn_flags & ZEND_ACC_CTOR) || (parent->common.prototype && (parent->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE))) {
- /* ctors only have a prototype if it comes from an interface */
- child->common.prototype = parent->common.prototype ? parent->common.prototype : parent;
- }
-
- if (child->common.prototype && (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) {
- if (!zend_do_perform_implementation_check(child, child->common.prototype TSRMLS_CC)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, zend_get_function_declaration(child->common.prototype TSRMLS_CC));
- }
- } else if (EG(error_reporting) & E_STRICT || Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */
- if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) {
- char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
- zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, method_prototype);
- efree(method_prototype);
- }
- }
-}
-/* }}} */
-
-static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_function *parent, zend_string *key, zend_class_entry *child_ce) /* {{{ */
-{
- uint32_t parent_flags = parent->common.fn_flags;
- zend_function *child;
- TSRMLS_FETCH();
-
- if ((child = zend_hash_find_ptr(child_function_table, key)) == NULL) {
- if (parent_flags & (ZEND_ACC_ABSTRACT)) {
- child_ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
- }
- return 1; /* method doesn't exist in child, copy from parent */
- }
-
- do_inheritance_check_on_method(child, parent TSRMLS_CC);
-
- return 0;
-}
-/* }}} */
-
-static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_property_info *parent_info, zend_string *key, zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
- zend_property_info *child_info;
- zend_class_entry *parent_ce = ce->parent;
-
- if (parent_info->flags & (ZEND_ACC_PRIVATE|ZEND_ACC_SHADOW)) {
- if ((child_info = zend_hash_find_ptr(&ce->properties_info, key)) != NULL) {
- child_info->flags |= ZEND_ACC_CHANGED;
- } else {
- if(ce->type & ZEND_INTERNAL_CLASS) {
- child_info = zend_duplicate_property_info_internal(parent_info);
- } else {
- child_info = zend_duplicate_property_info(parent_info TSRMLS_CC);
- }
- zend_hash_update_ptr(&ce->properties_info, key, child_info);
- child_info->flags &= ~ZEND_ACC_PRIVATE; /* it's not private anymore */
- child_info->flags |= ZEND_ACC_SHADOW; /* but it's a shadow of private */
- }
- return 0; /* don't copy access information to child */
- }
-
- if ((child_info = zend_hash_find_ptr(&ce->properties_info, key)) != NULL) {
- if ((parent_info->flags & ZEND_ACC_STATIC) != (child_info->flags & ZEND_ACC_STATIC)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s%s::$%s as %s%s::$%s",
- (parent_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", parent_ce->name->val, key->val,
- (child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ce->name->val, key->val);
-
- }
-
- if(parent_info->flags & ZEND_ACC_CHANGED) {
- child_info->flags |= ZEND_ACC_CHANGED;
- }
-
- if ((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ce->name->val, key->val, zend_visibility_string(parent_info->flags), parent_ce->name->val, (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
- } else if ((child_info->flags & ZEND_ACC_STATIC) == 0) {
- zval_ptr_dtor(&(ce->default_properties_table[parent_info->offset]));
- ce->default_properties_table[parent_info->offset] = ce->default_properties_table[child_info->offset];
- ZVAL_UNDEF(&ce->default_properties_table[child_info->offset]);
- child_info->offset = parent_info->offset;
- }
- return 0; /* Don't copy from parent */
- } else {
- return 1; /* Copy from parent */
- }
-}
-/* }}} */
-
-static inline void do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */
-{
- if (!(ce->ce_flags & ZEND_ACC_INTERFACE) && iface->interface_gets_implemented && iface->interface_gets_implemented(iface, ce TSRMLS_CC) == FAILURE) {
- zend_error(E_CORE_ERROR, "Class %s could not implement interface %s", ce->name->val, iface->name->val);
- }
- if (ce == iface) {
- zend_error(E_ERROR, "Interface %s cannot implement itself", ce->name->val);
- }
-}
-/* }}} */
-
-ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_entry *iface TSRMLS_DC) /* {{{ */
-{
- /* expects interface to be contained in ce's interface list already */
- uint32_t i, ce_num, if_num = iface->num_interfaces;
- zend_class_entry *entry;
-
- if (if_num==0) {
- return;
- }
- ce_num = ce->num_interfaces;
-
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num));
- } else {
- ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num));
- }
-
- /* Inherit the interfaces, only if they're not already inherited by the class */
- while (if_num--) {
- entry = iface->interfaces[if_num];
- for (i = 0; i < ce_num; i++) {
- if (ce->interfaces[i] == entry) {
- break;
- }
- }
- if (i == ce_num) {
- ce->interfaces[ce->num_interfaces++] = entry;
- }
- }
-
- /* and now call the implementing handlers */
- while (ce_num < ce->num_interfaces) {
- do_implement_interface(ce, ce->interfaces[ce_num++] TSRMLS_CC);
- }
-}
-/* }}} */
-
-#ifdef ZTS
-# define zval_property_ctor(parent_ce, ce) \
- (((parent_ce)->type != (ce)->type) ? ZVAL_COPY_CTOR : zval_add_ref)
-#else
-# define zval_property_ctor(parent_ce, ce) \
- zval_add_ref
-#endif
-
-static void do_inherit_class_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */
-{
- if (!Z_ISREF_P(zv)) {
- if (parent_ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_NEW_PERSISTENT_REF(zv, zv);
- } else {
- ZVAL_NEW_REF(zv, zv);
- }
- }
- if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- if (zend_hash_add(&ce->constants_table, name, zv)) {
- Z_ADDREF_P(zv);
- }
-}
-/* }}} */
-
-ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */
-{
- zend_property_info *property_info;
- zend_function *func;
- zend_string *key;
- zval *zv;
-
- if ((ce->ce_flags & ZEND_ACC_INTERFACE)
- && !(parent_ce->ce_flags & ZEND_ACC_INTERFACE)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Interface %s may not inherit from class (%s)", ce->name->val, parent_ce->name->val);
- }
- if (parent_ce->ce_flags & ZEND_ACC_FINAL_CLASS) {
- zend_error_noreturn(E_COMPILE_ERROR, "Class %s may not inherit from final class (%s)", ce->name->val, parent_ce->name->val);
- }
-
- ce->parent = parent_ce;
- /* Copy serialize/unserialize callbacks */
- if (!ce->serialize) {
- ce->serialize = parent_ce->serialize;
- }
- if (!ce->unserialize) {
- ce->unserialize = parent_ce->unserialize;
- }
-
- /* Inherit interfaces */
- zend_do_inherit_interfaces(ce, parent_ce TSRMLS_CC);
-
- /* Inherit properties */
- if (parent_ce->default_properties_count) {
- int i = ce->default_properties_count + parent_ce->default_properties_count;
-
- ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval) * i, ce->type == ZEND_INTERNAL_CLASS);
- if (ce->default_properties_count) {
- while (i-- > parent_ce->default_properties_count) {
- ce->default_properties_table[i] = ce->default_properties_table[i - parent_ce->default_properties_count];
- }
- }
- for (i = 0; i < parent_ce->default_properties_count; i++) {
-#ifdef ZTS
- if (parent_ce->type != ce->type) {
- ZVAL_DUP(&ce->default_properties_table[i], &parent_ce->default_properties_table[i]);
- if (Z_OPT_CONSTANT(ce->default_properties_table[i])) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- continue;
- }
-#endif
-
- ZVAL_COPY(&ce->default_properties_table[i], &parent_ce->default_properties_table[i]);
- if (Z_OPT_CONSTANT(ce->default_properties_table[i])) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- }
- ce->default_properties_count += parent_ce->default_properties_count;
- }
-
- if (parent_ce->type != ce->type) {
- /* User class extends internal class */
- zend_update_class_constants(parent_ce TSRMLS_CC);
- if (parent_ce->default_static_members_count) {
- int i = ce->default_static_members_count + parent_ce->default_static_members_count;
-
- ce->default_static_members_table = erealloc(ce->default_static_members_table, sizeof(zval) * i);
- if (ce->default_static_members_count) {
- while (i-- > parent_ce->default_static_members_count) {
- ce->default_static_members_table[i] = ce->default_static_members_table[i - parent_ce->default_static_members_count];
- }
- }
- for (i = 0; i < parent_ce->default_static_members_count; i++) {
- ZVAL_MAKE_REF(&CE_STATIC_MEMBERS(parent_ce)[i]);
- ce->default_static_members_table[i] = CE_STATIC_MEMBERS(parent_ce)[i];
- Z_ADDREF(ce->default_static_members_table[i]);
- if (Z_CONSTANT_P(Z_REFVAL(ce->default_static_members_table[i]))) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- }
- ce->default_static_members_count += parent_ce->default_static_members_count;
- ce->static_members_table = ce->default_static_members_table;
- }
- } else {
- if (parent_ce->default_static_members_count) {
- int i = ce->default_static_members_count + parent_ce->default_static_members_count;
-
- ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval) * i, ce->type == ZEND_INTERNAL_CLASS);
- if (ce->default_static_members_count) {
- while (i-- > parent_ce->default_static_members_count) {
- ce->default_static_members_table[i] = ce->default_static_members_table[i - parent_ce->default_static_members_count];
- }
- }
- for (i = 0; i < parent_ce->default_static_members_count; i++) {
- ZVAL_MAKE_REF(&parent_ce->default_static_members_table[i]);
- ce->default_static_members_table[i] = parent_ce->default_static_members_table[i];
- Z_ADDREF(ce->default_static_members_table[i]);
- if (Z_CONSTANT_P(Z_REFVAL(ce->default_static_members_table[i]))) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- }
- ce->default_static_members_count += parent_ce->default_static_members_count;
- if (ce->type == ZEND_USER_CLASS) {
- ce->static_members_table = ce->default_static_members_table;
- }
- }
- }
-
- ZEND_HASH_FOREACH_PTR(&ce->properties_info, property_info) {
- if (property_info->ce == ce) {
- if (property_info->flags & ZEND_ACC_STATIC) {
- property_info->offset += parent_ce->default_static_members_count;
- } else {
- property_info->offset += parent_ce->default_properties_count;
- }
- }
- } ZEND_HASH_FOREACH_END();
-
- ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->properties_info, key, property_info) {
- if (do_inherit_property_access_check(&ce->properties_info, property_info, key, ce TSRMLS_CC)) {
- if (ce->type & ZEND_INTERNAL_CLASS) {
- property_info = zend_duplicate_property_info_internal(property_info);
- } else {
- property_info = zend_duplicate_property_info(property_info TSRMLS_CC);
- }
- zend_hash_add_new_ptr(&ce->properties_info, key, property_info);
- }
- } ZEND_HASH_FOREACH_END();
-
- ZEND_HASH_FOREACH_STR_KEY_VAL(&parent_ce->constants_table, key, zv) {
- do_inherit_class_constant(key, zv, ce, parent_ce TSRMLS_CC);
- } ZEND_HASH_FOREACH_END();
-
- ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
- if (do_inherit_method_check(&ce->function_table, func, key, ce)) {
- zend_function *new_func = do_inherit_method(func TSRMLS_CC);
- zend_hash_add_new_ptr(&ce->function_table, key, new_func);
- }
- } ZEND_HASH_FOREACH_END();
-
- do_inherit_parent_constructor(ce TSRMLS_CC);
-
- if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) {
- ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
- } else if (!(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
- /* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */
- zend_verify_abstract_class(ce TSRMLS_CC);
- }
- ce->ce_flags |= parent_ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS;
-}
-/* }}} */
-
-static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zval *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
-{
- zval *old_constant;
-
- if ((old_constant = zend_hash_find(child_constants_table, name)) != NULL) {
- if (!Z_ISREF_P(old_constant) ||
- !Z_ISREF_P(parent_constant) ||
- Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", name->val, iface->name->val);
- }
- return 0;
- }
- return 1;
-}
-/* }}} */
-
-static void do_inherit_iface_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */
-{
- if (do_inherit_constant_check(&ce->constants_table, zv, name, iface)) {
- ZVAL_MAKE_REF(zv);
- Z_ADDREF_P(zv);
- if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- zend_hash_update(&ce->constants_table, name, zv);
- }
-}
-/* }}} */
-
-ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */
-{
- uint32_t i, ignore = 0;
- uint32_t current_iface_num = ce->num_interfaces;
- uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0;
- zend_function *func;
- zend_string *key;
- zval *zv;
-
- for (i = 0; i < ce->num_interfaces; i++) {
- if (ce->interfaces[i] == NULL) {
- memmove(ce->interfaces + i, ce->interfaces + i + 1, sizeof(zend_class_entry*) * (--ce->num_interfaces - i));
- i--;
- } else if (ce->interfaces[i] == iface) {
- if (i < parent_iface_num) {
- ignore = 1;
- } else {
- zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot implement previously implemented interface %s", ce->name->val, iface->name->val);
- }
- }
- }
- if (ignore) {
- /* Check for attempt to redeclare interface constants */
- ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, zv) {
- do_inherit_constant_check(&iface->constants_table, zv, key, iface);
- } ZEND_HASH_FOREACH_END();
- } else {
- if (ce->num_interfaces >= current_iface_num) {
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num));
- } else {
- ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num));
- }
- }
- ce->interfaces[ce->num_interfaces++] = iface;
-
- ZEND_HASH_FOREACH_STR_KEY_VAL(&iface->constants_table, key, zv) {
- do_inherit_iface_constant(key, zv, ce, iface TSRMLS_CC);
- } ZEND_HASH_FOREACH_END();
-
- ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) {
- if (do_inherit_method_check(&ce->function_table, func, key, ce)) {
- zend_function *new_func = do_inherit_method(func TSRMLS_CC);
- zend_hash_add_new_ptr(&ce->function_table, key, new_func);
- }
- } ZEND_HASH_FOREACH_END();
-
- do_implement_interface(ce, iface TSRMLS_CC);
- zend_do_inherit_interfaces(ce, iface TSRMLS_CC);
- }
-}
-/* }}} */
-
-ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */
-{
- uint32_t i, ignore = 0;
- uint32_t current_trait_num = ce->num_traits;
- uint32_t parent_trait_num = ce->parent ? ce->parent->num_traits : 0;
-
- for (i = 0; i < ce->num_traits; i++) {
- if (ce->traits[i] == NULL) {
- memmove(ce->traits + i, ce->traits + i + 1, sizeof(zend_class_entry*) * (--ce->num_traits - i));
- i--;
- } else if (ce->traits[i] == trait) {
- if (i < parent_trait_num) {
- ignore = 1;
- }
- }
- }
- if (!ignore) {
- if (ce->num_traits >= current_trait_num) {
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ce->traits = (zend_class_entry **) realloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num));
- } else {
- ce->traits = (zend_class_entry **) erealloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num));
- }
- }
- ce->traits[ce->num_traits++] = trait;
- }
-}
-/* }}} */
-
-static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_function *other_fn TSRMLS_DC) /* {{{ */
-{
- uint32_t fn_flags = fn->common.scope->ce_flags;
- uint32_t other_flags = other_fn->common.scope->ce_flags;
-
- return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC)
- && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC))
- && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) ==
- (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */
-}
-/* }}} */
-
-static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zend_function* fe TSRMLS_DC) /* {{{ */
-{
- if (!strncmp(mname->val, ZEND_CLONE_FUNC_NAME, mname->len)) {
- ce->clone = fe; fe->common.fn_flags |= ZEND_ACC_CLONE;
- } else if (!strncmp(mname->val, ZEND_CONSTRUCTOR_FUNC_NAME, mname->len)) {
- if (ce->constructor) {
- zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name->val);
- }
- ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR;
- } else if (!strncmp(mname->val, ZEND_DESTRUCTOR_FUNC_NAME, mname->len)) {
- ce->destructor = fe; fe->common.fn_flags |= ZEND_ACC_DTOR;
- } else if (!strncmp(mname->val, ZEND_GET_FUNC_NAME, mname->len)) {
- ce->__get = fe;
- } else if (!strncmp(mname->val, ZEND_SET_FUNC_NAME, mname->len)) {
- ce->__set = fe;
- } else if (!strncmp(mname->val, ZEND_CALL_FUNC_NAME, mname->len)) {
- ce->__call = fe;
- } else if (!strncmp(mname->val, ZEND_UNSET_FUNC_NAME, mname->len)) {
- ce->__unset = fe;
- } else if (!strncmp(mname->val, ZEND_ISSET_FUNC_NAME, mname->len)) {
- ce->__isset = fe;
- } else if (!strncmp(mname->val, ZEND_CALLSTATIC_FUNC_NAME, mname->len)) {
- ce->__callstatic = fe;
- } else if (!strncmp(mname->val, ZEND_TOSTRING_FUNC_NAME, mname->len)) {
- ce->__tostring = fe;
- } else if (!strncmp(mname->val, ZEND_DEBUGINFO_FUNC_NAME, mname->len)) {
- ce->__debugInfo = fe;
- } else if (ce->name->len == mname->len) {
- zend_string *lowercase_name = zend_string_alloc(ce->name->len, 0);
- zend_str_tolower_copy(lowercase_name->val, ce->name->val, ce->name->len);
- lowercase_name = zend_new_interned_string(lowercase_name TSRMLS_CC);
- if (!memcmp(mname->val, lowercase_name->val, mname->len)) {
- if (ce->constructor) {
- zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name->val);
- }
- ce->constructor = fe;
- fe->common.fn_flags |= ZEND_ACC_CTOR;
- }
- zend_string_release(lowercase_name);
- }
-}
-/* }}} */
-
-static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_string *key, zend_function *fn, HashTable **overriden TSRMLS_DC) /* {{{ */
-{
- zend_function *existing_fn = NULL;
- zend_function *new_fn;
-
- if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) {
- if (existing_fn->common.scope == ce) {
- /* members from the current class override trait methods */
- /* use temporary *overriden HashTable to detect hidden conflict */
- if (*overriden) {
- if ((existing_fn = zend_hash_find_ptr(*overriden, key)) != NULL) {
- if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
- /* Make sure the trait method is compatible with previosly declared abstract method */
- if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- zend_get_function_declaration(fn TSRMLS_CC),
- zend_get_function_declaration(existing_fn TSRMLS_CC));
- }
- } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
- /* Make sure the abstract declaration is compatible with previous declaration */
- if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- zend_get_function_declaration(fn TSRMLS_CC),
- zend_get_function_declaration(existing_fn TSRMLS_CC));
- }
- return;
- }
- }
- } else {
- ALLOC_HASHTABLE(*overriden);
- zend_hash_init_ex(*overriden, 8, NULL, ptr_dtor, 0, 0);
- }
- zend_hash_update_mem(*overriden, key, fn, sizeof(zend_function));
- return;
- } else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
- /* Make sure the trait method is compatible with previosly declared abstract method */
- if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- zend_get_function_declaration(fn TSRMLS_CC),
- zend_get_function_declaration(existing_fn TSRMLS_CC));
- }
- } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
- /* Make sure the abstract declaration is compatible with previous declaration */
- if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- zend_get_function_declaration(fn TSRMLS_CC),
- zend_get_function_declaration(existing_fn TSRMLS_CC));
- }
- return;
- } else if ((existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
- /* two traits can't define the same non-abstract method */
-#if 1
- zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s",
- name, ce->name->val);
-#else /* TODO: better error message */
- zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s",
- fn->common.scope->name->val, fn->common.function_name->val,
- ce->name->val, name,
- existing_fn->common.scope->name->val, existing_fn->common.function_name->val);
-#endif
- } else {
- /* inherited members are overridden by members inserted by traits */
- /* check whether the trait method fulfills the inheritance requirements */
- do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC);
- }
- }
-
- function_add_ref(fn);
- new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
- memcpy(new_fn, fn, sizeof(zend_op_array));
- fn = zend_hash_update_ptr(&ce->function_table, key, new_fn);
- zend_add_magic_methods(ce, key, fn TSRMLS_CC);
-}
-/* }}} */
-
-static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /* {{{ */
-{
- if ((fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
-
- fn->common.scope = ce;
-
- if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
- ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
- }
- if (fn->op_array.static_variables) {
- ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS;
- }
- }
-}
-/* }}} */
-
-static int zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable **overriden, HashTable *exclude_table TSRMLS_DC) /* {{{ */
-{
- zend_trait_alias *alias, **alias_ptr;
- zend_string *lcname;
- zend_function fn_copy;
-
- /* apply aliases which are qualified with a class name, there should not be any ambiguity */
- if (ce->trait_aliases) {
- alias_ptr = ce->trait_aliases;
- alias = *alias_ptr;
- while (alias) {
- /* Scope unset or equal to the function we compare to, and the alias applies to fn */
- if (alias->alias != NULL
- && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce)
- && alias->trait_method->method_name->len == fnname->len
- && (zend_binary_strcasecmp(alias->trait_method->method_name->val, alias->trait_method->method_name->len, fnname->val, fnname->len) == 0)) {
- fn_copy = *fn;
-
- /* if it is 0, no modifieres has been changed */
- if (alias->modifiers) {
- fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
- }
-
- lcname = zend_string_alloc(alias->alias->len, 0);
- zend_str_tolower_copy(lcname->val, alias->alias->val, alias->alias->len);
- zend_add_trait_method(ce, alias->alias->val, lcname, &fn_copy, overriden TSRMLS_CC);
- zend_string_release(lcname);
-
- /* Record the trait from which this alias was resolved. */
- if (!alias->trait_method->ce) {
- alias->trait_method->ce = fn->common.scope;
- }
- }
- alias_ptr++;
- alias = *alias_ptr;
- }
- }
-
- if (exclude_table == NULL || zend_hash_find(exclude_table, fnname) == NULL) {
- /* is not in hashtable, thus, function is not to be excluded */
- fn_copy = *fn;
-
- /* apply aliases which have not alias name, just setting visibility */
- if (ce->trait_aliases) {
- alias_ptr = ce->trait_aliases;
- alias = *alias_ptr;
- while (alias) {
- /* Scope unset or equal to the function we compare to, and the alias applies to fn */
- if (alias->alias == NULL && alias->modifiers != 0
- && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce)
- && (alias->trait_method->method_name->len == fnname->len)
- && (zend_binary_strcasecmp(alias->trait_method->method_name->val, alias->trait_method->method_name->len, fnname->val, fnname->len) == 0)) {
-
- fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
-
- /** Record the trait from which this alias was resolved. */
- if (!alias->trait_method->ce) {
- alias->trait_method->ce = fn->common.scope;
- }
- }
- alias_ptr++;
- alias = *alias_ptr;
- }
- }
-
- zend_add_trait_method(ce, fn->common.function_name->val, fnname, &fn_copy, overriden TSRMLS_CC);
- }
-
- return ZEND_HASH_APPLY_KEEP;
-}
-/* }}} */
-
-static void zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */
-{
- uint32_t i;
-
- if ((trait->ce_flags & ZEND_ACC_TRAIT) != ZEND_ACC_TRAIT) {
- zend_error_noreturn(E_COMPILE_ERROR, "Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements", trait->name->val);
- }
-
- for (i = 0; i < ce->num_traits; i++) {
- if (ce->traits[i] == trait) {
- return;
- }
- }
- zend_error_noreturn(E_COMPILE_ERROR, "Required Trait %s wasn't added to %s", trait->name->val, ce->name->val);
-}
-/* }}} */
-
-static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
- size_t i, j = 0;
- zend_trait_precedence *cur_precedence;
- zend_trait_method_reference *cur_method_ref;
- zend_string *lcname;
- zend_bool method_exists;
-
- /* resolve class references */
- if (ce->trait_precedences) {
- i = 0;
- while ((cur_precedence = ce->trait_precedences[i])) {
- /** Resolve classes for all precedence operations. */
- if (cur_precedence->exclude_from_classes) {
- cur_method_ref = cur_precedence->trait_method;
- if (!(cur_precedence->trait_method->ce = zend_fetch_class(cur_method_ref->class_name,
- ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
- zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name->val);
- }
- zend_check_trait_usage(ce, cur_precedence->trait_method->ce TSRMLS_CC);
-
- /** Ensure that the prefered method is actually available. */
- lcname = zend_string_alloc(cur_method_ref->method_name->len, 0);
- zend_str_tolower_copy(lcname->val,
- cur_method_ref->method_name->val,
- cur_method_ref->method_name->len);
- method_exists = zend_hash_exists(&cur_method_ref->ce->function_table,
- lcname);
- zend_string_free(lcname);
- if (!method_exists) {
- zend_error_noreturn(E_COMPILE_ERROR,
- "A precedence rule was defined for %s::%s but this method does not exist",
- cur_method_ref->ce->name->val,
- cur_method_ref->method_name->val);
- }
-
- /** With the other traits, we are more permissive.
- We do not give errors for those. This allows to be more
- defensive in such definitions.
- However, we want to make sure that the insteadof declaration
- is consistent in itself.
- */
- j = 0;
- while (cur_precedence->exclude_from_classes[j].class_name) {
- zend_string* class_name = cur_precedence->exclude_from_classes[j].class_name;
-
- if (!(cur_precedence->exclude_from_classes[j].ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
- zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", class_name->val);
- }
- zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j].ce TSRMLS_CC);
-
- /* make sure that the trait method is not from a class mentioned in
- exclude_from_classes, for consistency */
- if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i].ce) {
- zend_error_noreturn(E_COMPILE_ERROR,
- "Inconsistent insteadof definition. "
- "The method %s is to be used from %s, but %s is also on the exclude list",
- cur_method_ref->method_name->val,
- cur_precedence->trait_method->ce->name->val,
- cur_precedence->trait_method->ce->name->val);
- }
-
- zend_string_release(class_name);
- j++;
- }
- }
- i++;
- }
- }
-
- if (ce->trait_aliases) {
- i = 0;
- while (ce->trait_aliases[i]) {
- /** For all aliases with an explicit class name, resolve the class now. */
- if (ce->trait_aliases[i]->trait_method->class_name) {
- cur_method_ref = ce->trait_aliases[i]->trait_method;
- if (!(cur_method_ref->ce = zend_fetch_class(cur_method_ref->class_name, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
- zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name->val);
- }
- zend_check_trait_usage(ce, cur_method_ref->ce TSRMLS_CC);
-
- /** And, ensure that the referenced method is resolvable, too. */
- lcname = zend_string_alloc(cur_method_ref->method_name->len, 0);
- zend_str_tolower_copy(lcname->val,
- cur_method_ref->method_name->val,
- cur_method_ref->method_name->len);
- method_exists = zend_hash_exists(&cur_method_ref->ce->function_table,
- lcname);
- zend_string_free(lcname);
-
- if (!method_exists) {
- zend_error_noreturn(E_COMPILE_ERROR, "An alias was defined for %s::%s but this method does not exist", cur_method_ref->ce->name->val, cur_method_ref->method_name->val);
- }
- }
- i++;
- }
- }
-}
-/* }}} */
-
-static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_trait_precedence **precedences, zend_class_entry *trait) /* {{{ */
-{
- size_t i = 0, j;
-
- if (!precedences) {
- return;
- }
- while (precedences[i]) {
- if (precedences[i]->exclude_from_classes) {
- j = 0;
- while (precedences[i]->exclude_from_classes[j].ce) {
- if (precedences[i]->exclude_from_classes[j].ce == trait) {
- zend_string *lcname = zend_string_alloc(precedences[i]->trait_method->method_name->len, 0);
-
- zend_str_tolower_copy(lcname->val,
- precedences[i]->trait_method->method_name->val,
- precedences[i]->trait_method->method_name->len);
- if (zend_hash_add_empty_element(exclude_table, lcname) == NULL) {
- zend_string_release(lcname);
- zend_error_noreturn(E_COMPILE_ERROR, "Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times", precedences[i]->trait_method->method_name->val, trait->name->val);
- }
- zend_string_release(lcname);
- }
- ++j;
- }
- }
- ++i;
- }
-}
-/* }}} */
-
-static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
- uint32_t i;
- HashTable *overriden = NULL;
- zend_string *key;
- zend_function *fn;
-
- for (i = 0; i < ce->num_traits; i++) {
- if (ce->trait_precedences) {
- HashTable exclude_table;
-
- /* TODO: revisit this start size, may be its not optimal */
- zend_hash_init_ex(&exclude_table, 8, NULL, NULL, 0, 0);
-
- zend_traits_compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]);
-
- /* copies functions, applies defined aliasing, and excludes unused trait methods */
- ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->traits[i]->function_table, key, fn) {
- zend_traits_copy_functions(key, fn, ce, &overriden, &exclude_table TSRMLS_CC);
- } ZEND_HASH_FOREACH_END();
-
- zend_hash_destroy(&exclude_table);
- } else {
- ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->traits[i]->function_table, key, fn) {
- zend_traits_copy_functions(key, fn, ce, &overriden, NULL TSRMLS_CC);
- } ZEND_HASH_FOREACH_END();
- }
- }
-
- ZEND_HASH_FOREACH_PTR(&ce->function_table, fn) {
- zend_fixup_trait_method(fn, ce);
- } ZEND_HASH_FOREACH_END();
-
- if (overriden) {
- zend_hash_destroy(overriden);
- FREE_HASHTABLE(overriden);
- }
-}
-/* }}} */
-
-static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t current_trait, zend_string *prop_name, zend_class_entry *coliding_ce) /* {{{ */
-{
- size_t i;
-
- if (coliding_ce == ce) {
- for (i = 0; i < current_trait; i++) {
- if (zend_hash_exists(&ce->traits[i]->properties_info, prop_name)) {
- return ce->traits[i];
- }
- }
- }
-
- return coliding_ce;
-}
-/* }}} */
-
-static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
- size_t i;
- zend_property_info *property_info;
- zend_property_info *coliding_prop;
- zval compare_result;
- zend_string* prop_name;
- const char* class_name_unused;
- zend_bool not_compatible;
- zval* prop_value;
- uint32_t flags;
- zend_string *doc_comment;
-
- /* In the following steps the properties are inserted into the property table
- * for that, a very strict approach is applied:
- * - check for compatibility, if not compatible with any property in class -> fatal
- * - if compatible, then strict notice
- */
- for (i = 0; i < ce->num_traits; i++) {
- ZEND_HASH_FOREACH_PTR(&ce->traits[i]->properties_info, property_info) {
- /* first get the unmangeld name if necessary,
- * then check whether the property is already there
- */
- flags = property_info->flags;
- if ((flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
- prop_name = zend_string_copy(property_info->name);
- } else {
- const char *pname;
- size_t pname_len;
-
- /* for private and protected we need to unmangle the names */
- zend_unmangle_property_name_ex(property_info->name,
- &class_name_unused, &pname, &pname_len);
- prop_name = zend_string_init(pname, pname_len, 0);
- }
-
- /* next: check for conflicts with current class */
- if ((coliding_prop = zend_hash_find_ptr(&ce->properties_info, prop_name)) != NULL) {
- if (coliding_prop->flags & ZEND_ACC_SHADOW) {
- zend_hash_del(&ce->properties_info, prop_name);
- flags |= ZEND_ACC_CHANGED;
- } else {
- if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))
- == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) {
- /* flags are identical, now the value needs to be checked */
- if (flags & ZEND_ACC_STATIC) {
- not_compatible = (FAILURE == compare_function(&compare_result,
- &ce->default_static_members_table[coliding_prop->offset],
- &ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC))
- || (Z_LVAL(compare_result) != 0);
- } else {
- not_compatible = (FAILURE == compare_function(&compare_result,
- &ce->default_properties_table[coliding_prop->offset],
- &ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC))
- || (Z_LVAL(compare_result) != 0);
- }
- } else {
- /* the flags are not identical, thus, we assume properties are not compatible */
- not_compatible = 1;
- }
-
- if (not_compatible) {
- zend_error_noreturn(E_COMPILE_ERROR,
- "%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
- find_first_definition(ce, i, prop_name, coliding_prop->ce)->name->val,
- property_info->ce->name->val,
- prop_name->val,
- ce->name->val);
- } else {
- zend_error(E_STRICT,
- "%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed",
- find_first_definition(ce, i, prop_name, coliding_prop->ce)->name->val,
- property_info->ce->name->val,
- prop_name->val,
- ce->name->val);
- zend_string_release(prop_name);
- continue;
- }
- }
- }
-
- /* property not found, so lets add it */
- if (flags & ZEND_ACC_STATIC) {
- prop_value = &ce->traits[i]->default_static_members_table[property_info->offset];
- } else {
- prop_value = &ce->traits[i]->default_properties_table[property_info->offset];
- }
- if (Z_REFCOUNTED_P(prop_value)) Z_ADDREF_P(prop_value);
-
- doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL;
- zend_declare_property_ex(ce, prop_name,
- prop_value, flags,
- doc_comment TSRMLS_CC);
- zend_string_release(prop_name);
- } ZEND_HASH_FOREACH_END();
- }
-}
-/* }}} */
-
-static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
- int i = 0;
- zend_trait_alias* cur_alias;
- zend_string* lc_method_name;
-
- if (ce->trait_aliases) {
- while (ce->trait_aliases[i]) {
- cur_alias = ce->trait_aliases[i];
- /** The trait for this alias has not been resolved, this means, this
- alias was not applied. Abort with an error. */
- if (!cur_alias->trait_method->ce) {
- if (cur_alias->alias) {
- /** Plain old inconsistency/typo/bug */
- zend_error_noreturn(E_COMPILE_ERROR,
- "An alias (%s) was defined for method %s(), but this method does not exist",
- cur_alias->alias->val,
- cur_alias->trait_method->method_name->val);
- } else {
- /** Here are two possible cases:
- 1) this is an attempt to modifiy the visibility
- of a method introduce as part of another alias.
- Since that seems to violate the DRY principle,
- we check against it and abort.
- 2) it is just a plain old inconsitency/typo/bug
- as in the case where alias is set. */
-
- lc_method_name = zend_string_alloc(cur_alias->trait_method->method_name->len, 0);
- zend_str_tolower_copy(
- lc_method_name->val,
- cur_alias->trait_method->method_name->val,
- cur_alias->trait_method->method_name->len);
- if (zend_hash_exists(&ce->function_table,
- lc_method_name)) {
- zend_string_free(lc_method_name);
- zend_error_noreturn(E_COMPILE_ERROR,
- "The modifiers for the trait alias %s() need to be changed in the same statment in which the alias is defined. Error",
- cur_alias->trait_method->method_name->val);
- } else {
- zend_string_free(lc_method_name);
- zend_error_noreturn(E_COMPILE_ERROR,
- "The modifiers of the trait method %s() are changed, but this method does not exist. Error",
- cur_alias->trait_method->method_name->val);
-
- }
- }
- }
- i++;
- }
- }
-}
-/* }}} */
-
-ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */
-{
-
- if (ce->num_traits <= 0) {
- return;
- }
-
- /* complete initialization of trait strutures in ce */
- zend_traits_init_trait_structures(ce TSRMLS_CC);
-
- /* first care about all methods to be flattened into the class */
- zend_do_traits_method_binding(ce TSRMLS_CC);
-
- /* Aliases which have not been applied indicate typos/bugs. */
- zend_do_check_for_inconsistent_traits_aliasing(ce TSRMLS_CC);
-
- /* then flatten the properties into it, to, mostly to notfiy developer about problems */
- zend_do_traits_property_binding(ce TSRMLS_CC);
-
- /* verify that all abstract methods from traits have been implemented */
- zend_verify_abstract_class(ce TSRMLS_CC);
-
- /* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */
- if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
- ce->ce_flags -= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
- }
-}
-/* }}} */
-
ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time TSRMLS_DC) /* {{{ */
{
zend_function *function, *new_function;
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 10143d9efb..3ba37c3b0e 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -449,14 +449,6 @@ void zend_do_free(znode *op1 TSRMLS_DC);
ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time TSRMLS_DC);
ZEND_API zend_class_entry *do_bind_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC);
ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time TSRMLS_DC);
-ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_entry *iface TSRMLS_DC);
-ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC);
-
-ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC);
-ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC);
-
-ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC);
-void zend_do_early_binding(TSRMLS_D);
ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array TSRMLS_DC);
/* Functions for a null terminated pointer list, used for traits parsing and compilation */
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 8052f903ca..700b986d61 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -38,6 +38,7 @@
#include "zend_generators.h"
#include "zend_vm.h"
#include "zend_dtrace.h"
+#include "zend_inheritance.h"
/* Virtual current working directory support */
#include "zend_virtual_cwd.h"
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
new file mode 100644
index 0000000000..62cb3cc37b
--- /dev/null
+++ b/Zend/zend_inheritance.c
@@ -0,0 +1,1574 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2014 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "zend_API.h"
+#include "zend_compile.h"
+#include "zend_execute.h"
+
+static void ptr_dtor(zval *zv) /* {{{ */
+{
+ efree(Z_PTR_P(zv));
+}
+/* }}} */
+
+static zend_property_info *zend_duplicate_property_info(zend_property_info *property_info TSRMLS_DC) /* {{{ */
+{
+ zend_property_info* new_property_info;
+
+ new_property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
+ memcpy(new_property_info, property_info, sizeof(zend_property_info));
+ zend_string_addref(new_property_info->name);
+ if (new_property_info->doc_comment) {
+ zend_string_addref(new_property_info->doc_comment);
+ }
+ return new_property_info;
+}
+/* }}} */
+
+static zend_property_info *zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */
+{
+ zend_property_info* new_property_info = pemalloc(sizeof(zend_property_info), 1);
+ memcpy(new_property_info, property_info, sizeof(zend_property_info));
+ zend_string_addref(new_property_info->name);
+ return new_property_info;
+}
+/* }}} */
+
+static void do_inherit_parent_constructor(zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ zend_function *function, *new_function;
+
+ if (!ce->parent) {
+ return;
+ }
+
+ /* You cannot change create_object */
+ ce->create_object = ce->parent->create_object;
+
+ /* Inherit special functions if needed */
+ if (!ce->get_iterator) {
+ ce->get_iterator = ce->parent->get_iterator;
+ }
+ if (!ce->iterator_funcs.funcs) {
+ ce->iterator_funcs.funcs = ce->parent->iterator_funcs.funcs;
+ }
+ if (!ce->__get) {
+ ce->__get = ce->parent->__get;
+ }
+ if (!ce->__set) {
+ ce->__set = ce->parent->__set;
+ }
+ if (!ce->__unset) {
+ ce->__unset = ce->parent->__unset;
+ }
+ if (!ce->__isset) {
+ ce->__isset = ce->parent->__isset;
+ }
+ if (!ce->__call) {
+ ce->__call = ce->parent->__call;
+ }
+ if (!ce->__callstatic) {
+ ce->__callstatic = ce->parent->__callstatic;
+ }
+ if (!ce->__tostring) {
+ ce->__tostring = ce->parent->__tostring;
+ }
+ if (!ce->clone) {
+ ce->clone = ce->parent->clone;
+ }
+ if(!ce->serialize) {
+ ce->serialize = ce->parent->serialize;
+ }
+ if(!ce->unserialize) {
+ ce->unserialize = ce->parent->unserialize;
+ }
+ if (!ce->destructor) {
+ ce->destructor = ce->parent->destructor;
+ }
+ if (!ce->__debugInfo) {
+ ce->__debugInfo = ce->parent->__debugInfo;
+ }
+ if (ce->constructor) {
+ if (ce->parent->constructor && ce->parent->constructor->common.fn_flags & ZEND_ACC_FINAL) {
+ zend_error(E_ERROR, "Cannot override final %s::%s() with %s::%s()",
+ ce->parent->name->val, ce->parent->constructor->common.function_name->val,
+ ce->name->val, ce->constructor->common.function_name->val
+ );
+ }
+ return;
+ }
+
+ if ((function = zend_hash_str_find_ptr(&ce->parent->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1)) != NULL) {
+ /* inherit parent's constructor */
+ if (function->type == ZEND_INTERNAL_FUNCTION) {
+ new_function = pemalloc(sizeof(zend_internal_function), 1);
+ memcpy(new_function, function, sizeof(zend_internal_function));
+ } else {
+ new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_function, function, sizeof(zend_op_array));
+ }
+ zend_hash_str_update_ptr(&ce->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1, new_function);
+ function_add_ref(new_function);
+ } else {
+ /* Don't inherit the old style constructor if we already have the new style constructor */
+ zend_string *lc_class_name;
+ zend_string *lc_parent_class_name;
+
+ lc_class_name = zend_string_alloc(ce->name->len, 0);
+ zend_str_tolower_copy(lc_class_name->val, ce->name->val, ce->name->len);
+ if (!zend_hash_exists(&ce->function_table, lc_class_name)) {
+ lc_parent_class_name = zend_string_alloc(ce->parent->name->len, 0);
+ zend_str_tolower_copy(lc_parent_class_name->val, ce->parent->name->val, ce->parent->name->len);
+ if (!zend_hash_exists(&ce->function_table, lc_parent_class_name) &&
+ (function = zend_hash_find_ptr(&ce->parent->function_table, lc_parent_class_name)) != NULL) {
+ if (function->common.fn_flags & ZEND_ACC_CTOR) {
+ /* inherit parent's constructor */
+ new_function = pemalloc(sizeof(zend_function), function->type == ZEND_INTERNAL_FUNCTION);
+ memcpy(new_function, function, sizeof(zend_function));
+ zend_hash_update_ptr(&ce->function_table, lc_parent_class_name, new_function);
+ function_add_ref(new_function);
+ }
+ }
+ zend_string_release(lc_parent_class_name);
+ }
+ zend_string_free(lc_class_name);
+ }
+ ce->constructor = ce->parent->constructor;
+}
+/* }}} */
+
+char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
+{
+ if (fn_flags & ZEND_ACC_PRIVATE) {
+ return "private";
+ }
+ if (fn_flags & ZEND_ACC_PROTECTED) {
+ return "protected";
+ }
+ if (fn_flags & ZEND_ACC_PUBLIC) {
+ return "public";
+ }
+ return "";
+}
+/* }}} */
+
+static zend_function *do_inherit_method(zend_function *old_function TSRMLS_DC) /* {{{ */
+{
+ zend_function *new_function;
+
+ if (old_function->type == ZEND_INTERNAL_FUNCTION) {
+ new_function = pemalloc(sizeof(zend_internal_function), 1);
+ memcpy(new_function, old_function, sizeof(zend_internal_function));
+ } else {
+ new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_function, old_function, sizeof(zend_op_array));
+ }
+ /* The class entry of the derived function intentionally remains the same
+ * as that of the parent class. That allows us to know in which context
+ * we're running, and handle private method calls properly.
+ */
+ function_add_ref(new_function);
+ return new_function;
+}
+/* }}} */
+
+static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
+{
+ uint32_t i, num_args;
+
+ /* If it's a user function then arg_info == NULL means we don't have any parameters but
+ * we still need to do the arg number checks. We are only willing to ignore this for internal
+ * functions because extensions don't always define arg_info.
+ */
+ if (!proto || (!proto->common.arg_info && proto->common.type != ZEND_USER_FUNCTION)) {
+ return 1;
+ }
+
+ /* Checks for constructors only if they are declared in an interface,
+ * or explicitly marked as abstract
+ */
+ if ((fe->common.fn_flags & ZEND_ACC_CTOR)
+ && ((proto->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
+ && (proto->common.fn_flags & ZEND_ACC_ABSTRACT) == 0)) {
+ return 1;
+ }
+
+ /* If both methods are private do not enforce a signature */
+ if ((fe->common.fn_flags & ZEND_ACC_PRIVATE) && (proto->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ return 1;
+ }
+
+ /* check number of arguments */
+ if (proto->common.required_num_args < fe->common.required_num_args
+ || proto->common.num_args > fe->common.num_args) {
+ return 0;
+ }
+
+ /* by-ref constraints on return values are covariant */
+ if ((proto->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ && !(fe->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+ return 0;
+ }
+
+ if ((proto->common.fn_flags & ZEND_ACC_VARIADIC)
+ && !(fe->common.fn_flags & ZEND_ACC_VARIADIC)) {
+ return 0;
+ }
+
+ /* For variadic functions any additional (optional) arguments that were added must be
+ * checked against the signature of the variadic argument, so in this case we have to
+ * go through all the parameters of the function and not just those present in the
+ * prototype. */
+ num_args = proto->common.num_args;
+ if ((proto->common.fn_flags & ZEND_ACC_VARIADIC)
+ && fe->common.num_args > proto->common.num_args) {
+ num_args = fe->common.num_args;
+ }
+
+ for (i = 0; i < num_args; i++) {
+ zend_arg_info *fe_arg_info = &fe->common.arg_info[i];
+
+ zend_arg_info *proto_arg_info;
+ if (i < proto->common.num_args) {
+ proto_arg_info = &proto->common.arg_info[i];
+ } else {
+ proto_arg_info = &proto->common.arg_info[proto->common.num_args-1];
+ }
+
+ if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) {
+ /* Only one has a type hint and the other one doesn't */
+ return 0;
+ }
+
+ if (fe_arg_info->class_name) {
+ zend_string *fe_class_name, *proto_class_name;
+
+ if (!strcasecmp(fe_arg_info->class_name, "parent") && proto->common.scope) {
+ fe_class_name = zend_string_copy(proto->common.scope->name);
+ } else if (!strcasecmp(fe_arg_info->class_name, "self") && fe->common.scope) {
+ fe_class_name = zend_string_copy(fe->common.scope->name);
+ } else {
+ fe_class_name = zend_string_init(
+ fe_arg_info->class_name,
+ fe_arg_info->class_name_len, 0);
+ }
+
+ if (!strcasecmp(proto_arg_info->class_name, "parent") && proto->common.scope && proto->common.scope->parent) {
+ proto_class_name = zend_string_copy(proto->common.scope->parent->name);
+ } else if (!strcasecmp(proto_arg_info->class_name, "self") && proto->common.scope) {
+ proto_class_name = zend_string_copy(proto->common.scope->name);
+ } else {
+ proto_class_name = zend_string_init(
+ proto_arg_info->class_name,
+ proto_arg_info->class_name_len, 0);
+ }
+
+ if (strcasecmp(fe_class_name->val, proto_class_name->val)!=0) {
+ const char *colon;
+
+ if (fe->common.type != ZEND_USER_FUNCTION) {
+ zend_string_release(proto_class_name);
+ zend_string_release(fe_class_name);
+ return 0;
+ } else if (strchr(proto_class_name->val, '\\') != NULL ||
+ (colon = zend_memrchr(fe_class_name->val, '\\', fe_class_name->len)) == NULL ||
+ strcasecmp(colon+1, proto_class_name->val) != 0) {
+ zend_class_entry *fe_ce, *proto_ce;
+
+ fe_ce = zend_lookup_class(fe_class_name TSRMLS_CC);
+ proto_ce = zend_lookup_class(proto_class_name TSRMLS_CC);
+
+ /* Check for class alias */
+ if (!fe_ce || !proto_ce ||
+ fe_ce->type == ZEND_INTERNAL_CLASS ||
+ proto_ce->type == ZEND_INTERNAL_CLASS ||
+ fe_ce != proto_ce) {
+ zend_string_release(proto_class_name);
+ zend_string_release(fe_class_name);
+ return 0;
+ }
+ }
+ }
+ zend_string_release(proto_class_name);
+ zend_string_release(fe_class_name);
+ }
+ if (fe_arg_info->type_hint != proto_arg_info->type_hint) {
+ /* Incompatible type hint */
+ return 0;
+ }
+
+ /* by-ref constraints on arguments are invariant */
+ if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+/* }}} */
+
+#define REALLOC_BUF_IF_EXCEED(buf, offset, length, size) \
+ if (UNEXPECTED(offset - buf + size >= length)) { \
+ length += size + 1; \
+ buf = erealloc(buf, length); \
+ }
+
+static char *zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */
+{
+ char *offset, *buf;
+ uint32_t length = 1024;
+
+ offset = buf = (char *)emalloc(length * sizeof(char));
+ if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ *(offset++) = '&';
+ *(offset++) = ' ';
+ }
+
+ if (fptr->common.scope) {
+ memcpy(offset, fptr->common.scope->name->val, fptr->common.scope->name->len);
+ offset += fptr->common.scope->name->len;
+ *(offset++) = ':';
+ *(offset++) = ':';
+ }
+
+ {
+ size_t name_len = fptr->common.function_name->len;
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len);
+ memcpy(offset, fptr->common.function_name->val, name_len);
+ offset += name_len;
+ }
+
+ *(offset++) = '(';
+ if (fptr->common.arg_info) {
+ uint32_t i, required;
+ zend_arg_info *arg_info = fptr->common.arg_info;
+
+ required = fptr->common.required_num_args;
+ for (i = 0; i < fptr->common.num_args;) {
+ if (arg_info->class_name) {
+ const char *class_name;
+ uint32_t class_name_len;
+ if (!strcasecmp(arg_info->class_name, "self") && fptr->common.scope ) {
+ class_name = fptr->common.scope->name->val;
+ class_name_len = fptr->common.scope->name->len;
+ } else if (!strcasecmp(arg_info->class_name, "parent") && fptr->common.scope->parent) {
+ class_name = fptr->common.scope->parent->name->val;
+ class_name_len = fptr->common.scope->parent->name->len;
+ } else {
+ class_name = arg_info->class_name;
+ class_name_len = arg_info->class_name_len;
+ }
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, class_name_len);
+ memcpy(offset, class_name, class_name_len);
+ offset += class_name_len;
+ *(offset++) = ' ';
+ } else if (arg_info->type_hint) {
+ uint32_t type_name_len;
+ char *type_name = zend_get_type_by_const(arg_info->type_hint);
+ type_name_len = strlen(type_name);
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, type_name_len);
+ memcpy(offset, type_name, type_name_len);
+ offset += type_name_len;
+ *(offset++) = ' ';
+ }
+
+ if (arg_info->pass_by_reference) {
+ *(offset++) = '&';
+ }
+
+ if (arg_info->is_variadic) {
+ *(offset++) = '.';
+ *(offset++) = '.';
+ *(offset++) = '.';
+ }
+
+ *(offset++) = '$';
+
+ if (arg_info->name) {
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, arg_info->name_len);
+ memcpy(offset, arg_info->name, arg_info->name_len);
+ offset += arg_info->name_len;
+ } else {
+ uint32_t idx = i;
+ memcpy(offset, "param", 5);
+ offset += 5;
+ do {
+ *(offset++) = (char) (idx % 10) + '0';
+ idx /= 10;
+ } while (idx > 0);
+ }
+ if (i >= required && !arg_info->is_variadic) {
+ *(offset++) = ' ';
+ *(offset++) = '=';
+ *(offset++) = ' ';
+ if (fptr->type == ZEND_USER_FUNCTION) {
+ zend_op *precv = NULL;
+ {
+ uint32_t idx = i;
+ zend_op *op = ((zend_op_array *)fptr)->opcodes;
+ zend_op *end = op + ((zend_op_array *)fptr)->last;
+
+ ++idx;
+ while (op < end) {
+ if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT)
+ && op->op1.num == (zend_ulong)idx)
+ {
+ precv = op;
+ }
+ ++op;
+ }
+ }
+ if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
+ zval *zv = precv->op2.zv;
+
+ if (Z_TYPE_P(zv) == IS_CONSTANT) {
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, Z_STRLEN_P(zv));
+ memcpy(offset, Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ offset += Z_STRLEN_P(zv);
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ memcpy(offset, "false", 5);
+ offset += 5;
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ memcpy(offset, "true", 4);
+ offset += 4;
+ } else if (Z_TYPE_P(zv) == IS_NULL) {
+ memcpy(offset, "NULL", 4);
+ offset += 4;
+ } else if (Z_TYPE_P(zv) == IS_STRING) {
+ *(offset++) = '\'';
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, MIN(Z_STRLEN_P(zv), 10));
+ memcpy(offset, Z_STRVAL_P(zv), MIN(Z_STRLEN_P(zv), 10));
+ offset += MIN(Z_STRLEN_P(zv), 10);
+ if (Z_STRLEN_P(zv) > 10) {
+ *(offset++) = '.';
+ *(offset++) = '.';
+ *(offset++) = '.';
+ }
+ *(offset++) = '\'';
+ } else if (Z_TYPE_P(zv) == IS_ARRAY) {
+ memcpy(offset, "Array", 5);
+ offset += 5;
+ } else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
+ memcpy(offset, "<expression>", 12);
+ offset += 12;
+ } else {
+ zend_string *str = zval_get_string(zv);
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, str->len);
+ memcpy(offset, str->val, str->len);
+ offset += str->len;
+ zend_string_release(str);
+ }
+ }
+ } else {
+ memcpy(offset, "NULL", 4);
+ offset += 4;
+ }
+ }
+
+ if (++i < fptr->common.num_args) {
+ *(offset++) = ',';
+ *(offset++) = ' ';
+ }
+ arg_info++;
+ REALLOC_BUF_IF_EXCEED(buf, offset, length, 32);
+ }
+ }
+ *(offset++) = ')';
+ *offset = '\0';
+
+ return buf;
+}
+/* }}} */
+
+static void do_inheritance_check_on_method(zend_function *child, zend_function *parent TSRMLS_DC) /* {{{ */
+{
+ uint32_t child_flags;
+ uint32_t parent_flags = parent->common.fn_flags;
+
+ if ((parent->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
+ && parent->common.fn_flags & ZEND_ACC_ABSTRACT
+ && parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope)
+ && child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)",
+ parent->common.scope->name->val,
+ child->common.function_name->val,
+ child->common.prototype ? child->common.prototype->common.scope->name->val : child->common.scope->name->val);
+ }
+
+ if (parent_flags & ZEND_ACC_FINAL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot override final method %s::%s()", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val);
+ }
+
+ child_flags = child->common.fn_flags;
+ /* You cannot change from static to non static and vice versa.
+ */
+ if ((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC)) {
+ if (child->common.fn_flags & ZEND_ACC_STATIC) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non static method %s::%s() static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child));
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot make static method %s::%s() non static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child));
+ }
+ }
+
+ /* Disallow making an inherited method abstract. */
+ if ((child_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_ABSTRACT)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child));
+ }
+
+ if (parent_flags & ZEND_ACC_CHANGED) {
+ child->common.fn_flags |= ZEND_ACC_CHANGED;
+ } else {
+ /* Prevent derived classes from restricting access that was available in parent classes
+ */
+ if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
+ } else if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
+ && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
+ child->common.fn_flags |= ZEND_ACC_CHANGED;
+ }
+ }
+
+ if (parent_flags & ZEND_ACC_PRIVATE) {
+ child->common.prototype = NULL;
+ } else if (parent_flags & ZEND_ACC_ABSTRACT) {
+ child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT;
+ child->common.prototype = parent;
+ } else if (!(parent->common.fn_flags & ZEND_ACC_CTOR) || (parent->common.prototype && (parent->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE))) {
+ /* ctors only have a prototype if it comes from an interface */
+ child->common.prototype = parent->common.prototype ? parent->common.prototype : parent;
+ }
+
+ if (child->common.prototype && (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) {
+ if (!zend_do_perform_implementation_check(child, child->common.prototype TSRMLS_CC)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, zend_get_function_declaration(child->common.prototype TSRMLS_CC));
+ }
+ } else if (EG(error_reporting) & E_STRICT || Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */
+ if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) {
+ char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
+ zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, method_prototype);
+ efree(method_prototype);
+ }
+ }
+}
+/* }}} */
+
+static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_function *parent, zend_string *key, zend_class_entry *child_ce) /* {{{ */
+{
+ uint32_t parent_flags = parent->common.fn_flags;
+ zend_function *child;
+ TSRMLS_FETCH();
+
+ if ((child = zend_hash_find_ptr(child_function_table, key)) == NULL) {
+ if (parent_flags & (ZEND_ACC_ABSTRACT)) {
+ child_ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
+ }
+ return 1; /* method doesn't exist in child, copy from parent */
+ }
+
+ do_inheritance_check_on_method(child, parent TSRMLS_CC);
+
+ return 0;
+}
+/* }}} */
+
+static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_property_info *parent_info, zend_string *key, zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ zend_property_info *child_info;
+ zend_class_entry *parent_ce = ce->parent;
+
+ if (parent_info->flags & (ZEND_ACC_PRIVATE|ZEND_ACC_SHADOW)) {
+ if ((child_info = zend_hash_find_ptr(&ce->properties_info, key)) != NULL) {
+ child_info->flags |= ZEND_ACC_CHANGED;
+ } else {
+ if(ce->type & ZEND_INTERNAL_CLASS) {
+ child_info = zend_duplicate_property_info_internal(parent_info);
+ } else {
+ child_info = zend_duplicate_property_info(parent_info TSRMLS_CC);
+ }
+ zend_hash_update_ptr(&ce->properties_info, key, child_info);
+ child_info->flags &= ~ZEND_ACC_PRIVATE; /* it's not private anymore */
+ child_info->flags |= ZEND_ACC_SHADOW; /* but it's a shadow of private */
+ }
+ return 0; /* don't copy access information to child */
+ }
+
+ if ((child_info = zend_hash_find_ptr(&ce->properties_info, key)) != NULL) {
+ if ((parent_info->flags & ZEND_ACC_STATIC) != (child_info->flags & ZEND_ACC_STATIC)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s%s::$%s as %s%s::$%s",
+ (parent_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", parent_ce->name->val, key->val,
+ (child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ce->name->val, key->val);
+
+ }
+
+ if(parent_info->flags & ZEND_ACC_CHANGED) {
+ child_info->flags |= ZEND_ACC_CHANGED;
+ }
+
+ if ((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ce->name->val, key->val, zend_visibility_string(parent_info->flags), parent_ce->name->val, (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
+ } else if ((child_info->flags & ZEND_ACC_STATIC) == 0) {
+ zval_ptr_dtor(&(ce->default_properties_table[parent_info->offset]));
+ ce->default_properties_table[parent_info->offset] = ce->default_properties_table[child_info->offset];
+ ZVAL_UNDEF(&ce->default_properties_table[child_info->offset]);
+ child_info->offset = parent_info->offset;
+ }
+ return 0; /* Don't copy from parent */
+ } else {
+ return 1; /* Copy from parent */
+ }
+}
+/* }}} */
+
+static inline void do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */
+{
+ if (!(ce->ce_flags & ZEND_ACC_INTERFACE) && iface->interface_gets_implemented && iface->interface_gets_implemented(iface, ce TSRMLS_CC) == FAILURE) {
+ zend_error(E_CORE_ERROR, "Class %s could not implement interface %s", ce->name->val, iface->name->val);
+ }
+ if (ce == iface) {
+ zend_error(E_ERROR, "Interface %s cannot implement itself", ce->name->val);
+ }
+}
+/* }}} */
+
+ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_entry *iface TSRMLS_DC) /* {{{ */
+{
+ /* expects interface to be contained in ce's interface list already */
+ uint32_t i, ce_num, if_num = iface->num_interfaces;
+ zend_class_entry *entry;
+
+ if (if_num==0) {
+ return;
+ }
+ ce_num = ce->num_interfaces;
+
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num));
+ } else {
+ ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num));
+ }
+
+ /* Inherit the interfaces, only if they're not already inherited by the class */
+ while (if_num--) {
+ entry = iface->interfaces[if_num];
+ for (i = 0; i < ce_num; i++) {
+ if (ce->interfaces[i] == entry) {
+ break;
+ }
+ }
+ if (i == ce_num) {
+ ce->interfaces[ce->num_interfaces++] = entry;
+ }
+ }
+
+ /* and now call the implementing handlers */
+ while (ce_num < ce->num_interfaces) {
+ do_implement_interface(ce, ce->interfaces[ce_num++] TSRMLS_CC);
+ }
+}
+/* }}} */
+
+#ifdef ZTS
+# define zval_property_ctor(parent_ce, ce) \
+ (((parent_ce)->type != (ce)->type) ? ZVAL_COPY_CTOR : zval_add_ref)
+#else
+# define zval_property_ctor(parent_ce, ce) \
+ zval_add_ref
+#endif
+
+static void do_inherit_class_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */
+{
+ if (!Z_ISREF_P(zv)) {
+ if (parent_ce->type == ZEND_INTERNAL_CLASS) {
+ ZVAL_NEW_PERSISTENT_REF(zv, zv);
+ } else {
+ ZVAL_NEW_REF(zv, zv);
+ }
+ }
+ if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ if (zend_hash_add(&ce->constants_table, name, zv)) {
+ Z_ADDREF_P(zv);
+ }
+}
+/* }}} */
+
+ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */
+{
+ zend_property_info *property_info;
+ zend_function *func;
+ zend_string *key;
+ zval *zv;
+
+ if ((ce->ce_flags & ZEND_ACC_INTERFACE)
+ && !(parent_ce->ce_flags & ZEND_ACC_INTERFACE)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Interface %s may not inherit from class (%s)", ce->name->val, parent_ce->name->val);
+ }
+ if (parent_ce->ce_flags & ZEND_ACC_FINAL_CLASS) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Class %s may not inherit from final class (%s)", ce->name->val, parent_ce->name->val);
+ }
+
+ ce->parent = parent_ce;
+ /* Copy serialize/unserialize callbacks */
+ if (!ce->serialize) {
+ ce->serialize = parent_ce->serialize;
+ }
+ if (!ce->unserialize) {
+ ce->unserialize = parent_ce->unserialize;
+ }
+
+ /* Inherit interfaces */
+ zend_do_inherit_interfaces(ce, parent_ce TSRMLS_CC);
+
+ /* Inherit properties */
+ if (parent_ce->default_properties_count) {
+ int i = ce->default_properties_count + parent_ce->default_properties_count;
+
+ ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval) * i, ce->type == ZEND_INTERNAL_CLASS);
+ if (ce->default_properties_count) {
+ while (i-- > parent_ce->default_properties_count) {
+ ce->default_properties_table[i] = ce->default_properties_table[i - parent_ce->default_properties_count];
+ }
+ }
+ for (i = 0; i < parent_ce->default_properties_count; i++) {
+#ifdef ZTS
+ if (parent_ce->type != ce->type) {
+ ZVAL_DUP(&ce->default_properties_table[i], &parent_ce->default_properties_table[i]);
+ if (Z_OPT_CONSTANT(ce->default_properties_table[i])) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ continue;
+ }
+#endif
+
+ ZVAL_COPY(&ce->default_properties_table[i], &parent_ce->default_properties_table[i]);
+ if (Z_OPT_CONSTANT(ce->default_properties_table[i])) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ }
+ ce->default_properties_count += parent_ce->default_properties_count;
+ }
+
+ if (parent_ce->type != ce->type) {
+ /* User class extends internal class */
+ zend_update_class_constants(parent_ce TSRMLS_CC);
+ if (parent_ce->default_static_members_count) {
+ int i = ce->default_static_members_count + parent_ce->default_static_members_count;
+
+ ce->default_static_members_table = erealloc(ce->default_static_members_table, sizeof(zval) * i);
+ if (ce->default_static_members_count) {
+ while (i-- > parent_ce->default_static_members_count) {
+ ce->default_static_members_table[i] = ce->default_static_members_table[i - parent_ce->default_static_members_count];
+ }
+ }
+ for (i = 0; i < parent_ce->default_static_members_count; i++) {
+ ZVAL_MAKE_REF(&CE_STATIC_MEMBERS(parent_ce)[i]);
+ ce->default_static_members_table[i] = CE_STATIC_MEMBERS(parent_ce)[i];
+ Z_ADDREF(ce->default_static_members_table[i]);
+ if (Z_CONSTANT_P(Z_REFVAL(ce->default_static_members_table[i]))) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ }
+ ce->default_static_members_count += parent_ce->default_static_members_count;
+ ce->static_members_table = ce->default_static_members_table;
+ }
+ } else {
+ if (parent_ce->default_static_members_count) {
+ int i = ce->default_static_members_count + parent_ce->default_static_members_count;
+
+ ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval) * i, ce->type == ZEND_INTERNAL_CLASS);
+ if (ce->default_static_members_count) {
+ while (i-- > parent_ce->default_static_members_count) {
+ ce->default_static_members_table[i] = ce->default_static_members_table[i - parent_ce->default_static_members_count];
+ }
+ }
+ for (i = 0; i < parent_ce->default_static_members_count; i++) {
+ ZVAL_MAKE_REF(&parent_ce->default_static_members_table[i]);
+ ce->default_static_members_table[i] = parent_ce->default_static_members_table[i];
+ Z_ADDREF(ce->default_static_members_table[i]);
+ if (Z_CONSTANT_P(Z_REFVAL(ce->default_static_members_table[i]))) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ }
+ ce->default_static_members_count += parent_ce->default_static_members_count;
+ if (ce->type == ZEND_USER_CLASS) {
+ ce->static_members_table = ce->default_static_members_table;
+ }
+ }
+ }
+
+ ZEND_HASH_FOREACH_PTR(&ce->properties_info, property_info) {
+ if (property_info->ce == ce) {
+ if (property_info->flags & ZEND_ACC_STATIC) {
+ property_info->offset += parent_ce->default_static_members_count;
+ } else {
+ property_info->offset += parent_ce->default_properties_count;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->properties_info, key, property_info) {
+ if (do_inherit_property_access_check(&ce->properties_info, property_info, key, ce TSRMLS_CC)) {
+ if (ce->type & ZEND_INTERNAL_CLASS) {
+ property_info = zend_duplicate_property_info_internal(property_info);
+ } else {
+ property_info = zend_duplicate_property_info(property_info TSRMLS_CC);
+ }
+ zend_hash_add_new_ptr(&ce->properties_info, key, property_info);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_STR_KEY_VAL(&parent_ce->constants_table, key, zv) {
+ do_inherit_class_constant(key, zv, ce, parent_ce TSRMLS_CC);
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
+ if (do_inherit_method_check(&ce->function_table, func, key, ce)) {
+ zend_function *new_func = do_inherit_method(func TSRMLS_CC);
+ zend_hash_add_new_ptr(&ce->function_table, key, new_func);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ do_inherit_parent_constructor(ce TSRMLS_CC);
+
+ if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) {
+ ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
+ } else if (!(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
+ /* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */
+ zend_verify_abstract_class(ce TSRMLS_CC);
+ }
+ ce->ce_flags |= parent_ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS;
+}
+/* }}} */
+
+static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zval *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
+{
+ zval *old_constant;
+
+ if ((old_constant = zend_hash_find(child_constants_table, name)) != NULL) {
+ if (!Z_ISREF_P(old_constant) ||
+ !Z_ISREF_P(parent_constant) ||
+ Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", name->val, iface->name->val);
+ }
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+static void do_inherit_iface_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */
+{
+ if (do_inherit_constant_check(&ce->constants_table, zv, name, iface)) {
+ ZVAL_MAKE_REF(zv);
+ Z_ADDREF_P(zv);
+ if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ zend_hash_update(&ce->constants_table, name, zv);
+ }
+}
+/* }}} */
+
+ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */
+{
+ uint32_t i, ignore = 0;
+ uint32_t current_iface_num = ce->num_interfaces;
+ uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0;
+ zend_function *func;
+ zend_string *key;
+ zval *zv;
+
+ for (i = 0; i < ce->num_interfaces; i++) {
+ if (ce->interfaces[i] == NULL) {
+ memmove(ce->interfaces + i, ce->interfaces + i + 1, sizeof(zend_class_entry*) * (--ce->num_interfaces - i));
+ i--;
+ } else if (ce->interfaces[i] == iface) {
+ if (i < parent_iface_num) {
+ ignore = 1;
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot implement previously implemented interface %s", ce->name->val, iface->name->val);
+ }
+ }
+ }
+ if (ignore) {
+ /* Check for attempt to redeclare interface constants */
+ ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, zv) {
+ do_inherit_constant_check(&iface->constants_table, zv, key, iface);
+ } ZEND_HASH_FOREACH_END();
+ } else {
+ if (ce->num_interfaces >= current_iface_num) {
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num));
+ } else {
+ ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num));
+ }
+ }
+ ce->interfaces[ce->num_interfaces++] = iface;
+
+ ZEND_HASH_FOREACH_STR_KEY_VAL(&iface->constants_table, key, zv) {
+ do_inherit_iface_constant(key, zv, ce, iface TSRMLS_CC);
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) {
+ if (do_inherit_method_check(&ce->function_table, func, key, ce)) {
+ zend_function *new_func = do_inherit_method(func TSRMLS_CC);
+ zend_hash_add_new_ptr(&ce->function_table, key, new_func);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ do_implement_interface(ce, iface TSRMLS_CC);
+ zend_do_inherit_interfaces(ce, iface TSRMLS_CC);
+ }
+}
+/* }}} */
+
+ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */
+{
+ uint32_t i, ignore = 0;
+ uint32_t current_trait_num = ce->num_traits;
+ uint32_t parent_trait_num = ce->parent ? ce->parent->num_traits : 0;
+
+ for (i = 0; i < ce->num_traits; i++) {
+ if (ce->traits[i] == NULL) {
+ memmove(ce->traits + i, ce->traits + i + 1, sizeof(zend_class_entry*) * (--ce->num_traits - i));
+ i--;
+ } else if (ce->traits[i] == trait) {
+ if (i < parent_trait_num) {
+ ignore = 1;
+ }
+ }
+ }
+ if (!ignore) {
+ if (ce->num_traits >= current_trait_num) {
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ce->traits = (zend_class_entry **) realloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num));
+ } else {
+ ce->traits = (zend_class_entry **) erealloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num));
+ }
+ }
+ ce->traits[ce->num_traits++] = trait;
+ }
+}
+/* }}} */
+
+static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_function *other_fn TSRMLS_DC) /* {{{ */
+{
+ uint32_t fn_flags = fn->common.scope->ce_flags;
+ uint32_t other_flags = other_fn->common.scope->ce_flags;
+
+ return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC)
+ && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC))
+ && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) ==
+ (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */
+}
+/* }}} */
+
+static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zend_function* fe TSRMLS_DC) /* {{{ */
+{
+ if (!strncmp(mname->val, ZEND_CLONE_FUNC_NAME, mname->len)) {
+ ce->clone = fe; fe->common.fn_flags |= ZEND_ACC_CLONE;
+ } else if (!strncmp(mname->val, ZEND_CONSTRUCTOR_FUNC_NAME, mname->len)) {
+ if (ce->constructor) {
+ zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name->val);
+ }
+ ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR;
+ } else if (!strncmp(mname->val, ZEND_DESTRUCTOR_FUNC_NAME, mname->len)) {
+ ce->destructor = fe; fe->common.fn_flags |= ZEND_ACC_DTOR;
+ } else if (!strncmp(mname->val, ZEND_GET_FUNC_NAME, mname->len)) {
+ ce->__get = fe;
+ } else if (!strncmp(mname->val, ZEND_SET_FUNC_NAME, mname->len)) {
+ ce->__set = fe;
+ } else if (!strncmp(mname->val, ZEND_CALL_FUNC_NAME, mname->len)) {
+ ce->__call = fe;
+ } else if (!strncmp(mname->val, ZEND_UNSET_FUNC_NAME, mname->len)) {
+ ce->__unset = fe;
+ } else if (!strncmp(mname->val, ZEND_ISSET_FUNC_NAME, mname->len)) {
+ ce->__isset = fe;
+ } else if (!strncmp(mname->val, ZEND_CALLSTATIC_FUNC_NAME, mname->len)) {
+ ce->__callstatic = fe;
+ } else if (!strncmp(mname->val, ZEND_TOSTRING_FUNC_NAME, mname->len)) {
+ ce->__tostring = fe;
+ } else if (!strncmp(mname->val, ZEND_DEBUGINFO_FUNC_NAME, mname->len)) {
+ ce->__debugInfo = fe;
+ } else if (ce->name->len == mname->len) {
+ zend_string *lowercase_name = zend_string_alloc(ce->name->len, 0);
+ zend_str_tolower_copy(lowercase_name->val, ce->name->val, ce->name->len);
+ lowercase_name = zend_new_interned_string(lowercase_name TSRMLS_CC);
+ if (!memcmp(mname->val, lowercase_name->val, mname->len)) {
+ if (ce->constructor) {
+ zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name->val);
+ }
+ ce->constructor = fe;
+ fe->common.fn_flags |= ZEND_ACC_CTOR;
+ }
+ zend_string_release(lowercase_name);
+ }
+}
+/* }}} */
+
+static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_string *key, zend_function *fn, HashTable **overriden TSRMLS_DC) /* {{{ */
+{
+ zend_function *existing_fn = NULL;
+ zend_function *new_fn;
+
+ if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) {
+ if (existing_fn->common.scope == ce) {
+ /* members from the current class override trait methods */
+ /* use temporary *overriden HashTable to detect hidden conflict */
+ if (*overriden) {
+ if ((existing_fn = zend_hash_find_ptr(*overriden, key)) != NULL) {
+ if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
+ /* Make sure the trait method is compatible with previosly declared abstract method */
+ if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
+ zend_get_function_declaration(fn TSRMLS_CC),
+ zend_get_function_declaration(existing_fn TSRMLS_CC));
+ }
+ } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
+ /* Make sure the abstract declaration is compatible with previous declaration */
+ if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
+ zend_get_function_declaration(fn TSRMLS_CC),
+ zend_get_function_declaration(existing_fn TSRMLS_CC));
+ }
+ return;
+ }
+ }
+ } else {
+ ALLOC_HASHTABLE(*overriden);
+ zend_hash_init_ex(*overriden, 8, NULL, ptr_dtor, 0, 0);
+ }
+ zend_hash_update_mem(*overriden, key, fn, sizeof(zend_function));
+ return;
+ } else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
+ /* Make sure the trait method is compatible with previosly declared abstract method */
+ if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
+ zend_get_function_declaration(fn TSRMLS_CC),
+ zend_get_function_declaration(existing_fn TSRMLS_CC));
+ }
+ } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
+ /* Make sure the abstract declaration is compatible with previous declaration */
+ if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
+ zend_get_function_declaration(fn TSRMLS_CC),
+ zend_get_function_declaration(existing_fn TSRMLS_CC));
+ }
+ return;
+ } else if ((existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
+ /* two traits can't define the same non-abstract method */
+#if 1
+ zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s",
+ name, ce->name->val);
+#else /* TODO: better error message */
+ zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s",
+ fn->common.scope->name->val, fn->common.function_name->val,
+ ce->name->val, name,
+ existing_fn->common.scope->name->val, existing_fn->common.function_name->val);
+#endif
+ } else {
+ /* inherited members are overridden by members inserted by traits */
+ /* check whether the trait method fulfills the inheritance requirements */
+ do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC);
+ }
+ }
+
+ function_add_ref(fn);
+ new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_fn, fn, sizeof(zend_op_array));
+ fn = zend_hash_update_ptr(&ce->function_table, key, new_fn);
+ zend_add_magic_methods(ce, key, fn TSRMLS_CC);
+}
+/* }}} */
+
+static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /* {{{ */
+{
+ if ((fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
+
+ fn->common.scope = ce;
+
+ if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
+ ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
+ }
+ if (fn->op_array.static_variables) {
+ ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS;
+ }
+ }
+}
+/* }}} */
+
+static int zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable **overriden, HashTable *exclude_table TSRMLS_DC) /* {{{ */
+{
+ zend_trait_alias *alias, **alias_ptr;
+ zend_string *lcname;
+ zend_function fn_copy;
+
+ /* apply aliases which are qualified with a class name, there should not be any ambiguity */
+ if (ce->trait_aliases) {
+ alias_ptr = ce->trait_aliases;
+ alias = *alias_ptr;
+ while (alias) {
+ /* Scope unset or equal to the function we compare to, and the alias applies to fn */
+ if (alias->alias != NULL
+ && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce)
+ && alias->trait_method->method_name->len == fnname->len
+ && (zend_binary_strcasecmp(alias->trait_method->method_name->val, alias->trait_method->method_name->len, fnname->val, fnname->len) == 0)) {
+ fn_copy = *fn;
+
+ /* if it is 0, no modifieres has been changed */
+ if (alias->modifiers) {
+ fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
+ }
+
+ lcname = zend_string_alloc(alias->alias->len, 0);
+ zend_str_tolower_copy(lcname->val, alias->alias->val, alias->alias->len);
+ zend_add_trait_method(ce, alias->alias->val, lcname, &fn_copy, overriden TSRMLS_CC);
+ zend_string_release(lcname);
+
+ /* Record the trait from which this alias was resolved. */
+ if (!alias->trait_method->ce) {
+ alias->trait_method->ce = fn->common.scope;
+ }
+ }
+ alias_ptr++;
+ alias = *alias_ptr;
+ }
+ }
+
+ if (exclude_table == NULL || zend_hash_find(exclude_table, fnname) == NULL) {
+ /* is not in hashtable, thus, function is not to be excluded */
+ fn_copy = *fn;
+
+ /* apply aliases which have not alias name, just setting visibility */
+ if (ce->trait_aliases) {
+ alias_ptr = ce->trait_aliases;
+ alias = *alias_ptr;
+ while (alias) {
+ /* Scope unset or equal to the function we compare to, and the alias applies to fn */
+ if (alias->alias == NULL && alias->modifiers != 0
+ && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce)
+ && (alias->trait_method->method_name->len == fnname->len)
+ && (zend_binary_strcasecmp(alias->trait_method->method_name->val, alias->trait_method->method_name->len, fnname->val, fnname->len) == 0)) {
+
+ fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
+
+ /** Record the trait from which this alias was resolved. */
+ if (!alias->trait_method->ce) {
+ alias->trait_method->ce = fn->common.scope;
+ }
+ }
+ alias_ptr++;
+ alias = *alias_ptr;
+ }
+ }
+
+ zend_add_trait_method(ce, fn->common.function_name->val, fnname, &fn_copy, overriden TSRMLS_CC);
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+static void zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */
+{
+ uint32_t i;
+
+ if ((trait->ce_flags & ZEND_ACC_TRAIT) != ZEND_ACC_TRAIT) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements", trait->name->val);
+ }
+
+ for (i = 0; i < ce->num_traits; i++) {
+ if (ce->traits[i] == trait) {
+ return;
+ }
+ }
+ zend_error_noreturn(E_COMPILE_ERROR, "Required Trait %s wasn't added to %s", trait->name->val, ce->name->val);
+}
+/* }}} */
+
+static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ size_t i, j = 0;
+ zend_trait_precedence *cur_precedence;
+ zend_trait_method_reference *cur_method_ref;
+ zend_string *lcname;
+ zend_bool method_exists;
+
+ /* resolve class references */
+ if (ce->trait_precedences) {
+ i = 0;
+ while ((cur_precedence = ce->trait_precedences[i])) {
+ /** Resolve classes for all precedence operations. */
+ if (cur_precedence->exclude_from_classes) {
+ cur_method_ref = cur_precedence->trait_method;
+ if (!(cur_precedence->trait_method->ce = zend_fetch_class(cur_method_ref->class_name,
+ ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name->val);
+ }
+ zend_check_trait_usage(ce, cur_precedence->trait_method->ce TSRMLS_CC);
+
+ /** Ensure that the prefered method is actually available. */
+ lcname = zend_string_alloc(cur_method_ref->method_name->len, 0);
+ zend_str_tolower_copy(lcname->val,
+ cur_method_ref->method_name->val,
+ cur_method_ref->method_name->len);
+ method_exists = zend_hash_exists(&cur_method_ref->ce->function_table,
+ lcname);
+ zend_string_free(lcname);
+ if (!method_exists) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A precedence rule was defined for %s::%s but this method does not exist",
+ cur_method_ref->ce->name->val,
+ cur_method_ref->method_name->val);
+ }
+
+ /** With the other traits, we are more permissive.
+ We do not give errors for those. This allows to be more
+ defensive in such definitions.
+ However, we want to make sure that the insteadof declaration
+ is consistent in itself.
+ */
+ j = 0;
+ while (cur_precedence->exclude_from_classes[j].class_name) {
+ zend_string* class_name = cur_precedence->exclude_from_classes[j].class_name;
+
+ if (!(cur_precedence->exclude_from_classes[j].ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", class_name->val);
+ }
+ zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j].ce TSRMLS_CC);
+
+ /* make sure that the trait method is not from a class mentioned in
+ exclude_from_classes, for consistency */
+ if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i].ce) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Inconsistent insteadof definition. "
+ "The method %s is to be used from %s, but %s is also on the exclude list",
+ cur_method_ref->method_name->val,
+ cur_precedence->trait_method->ce->name->val,
+ cur_precedence->trait_method->ce->name->val);
+ }
+
+ zend_string_release(class_name);
+ j++;
+ }
+ }
+ i++;
+ }
+ }
+
+ if (ce->trait_aliases) {
+ i = 0;
+ while (ce->trait_aliases[i]) {
+ /** For all aliases with an explicit class name, resolve the class now. */
+ if (ce->trait_aliases[i]->trait_method->class_name) {
+ cur_method_ref = ce->trait_aliases[i]->trait_method;
+ if (!(cur_method_ref->ce = zend_fetch_class(cur_method_ref->class_name, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name->val);
+ }
+ zend_check_trait_usage(ce, cur_method_ref->ce TSRMLS_CC);
+
+ /** And, ensure that the referenced method is resolvable, too. */
+ lcname = zend_string_alloc(cur_method_ref->method_name->len, 0);
+ zend_str_tolower_copy(lcname->val,
+ cur_method_ref->method_name->val,
+ cur_method_ref->method_name->len);
+ method_exists = zend_hash_exists(&cur_method_ref->ce->function_table,
+ lcname);
+ zend_string_free(lcname);
+
+ if (!method_exists) {
+ zend_error_noreturn(E_COMPILE_ERROR, "An alias was defined for %s::%s but this method does not exist", cur_method_ref->ce->name->val, cur_method_ref->method_name->val);
+ }
+ }
+ i++;
+ }
+ }
+}
+/* }}} */
+
+static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_trait_precedence **precedences, zend_class_entry *trait) /* {{{ */
+{
+ size_t i = 0, j;
+
+ if (!precedences) {
+ return;
+ }
+ while (precedences[i]) {
+ if (precedences[i]->exclude_from_classes) {
+ j = 0;
+ while (precedences[i]->exclude_from_classes[j].ce) {
+ if (precedences[i]->exclude_from_classes[j].ce == trait) {
+ zend_string *lcname = zend_string_alloc(precedences[i]->trait_method->method_name->len, 0);
+
+ zend_str_tolower_copy(lcname->val,
+ precedences[i]->trait_method->method_name->val,
+ precedences[i]->trait_method->method_name->len);
+ if (zend_hash_add_empty_element(exclude_table, lcname) == NULL) {
+ zend_string_release(lcname);
+ zend_error_noreturn(E_COMPILE_ERROR, "Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times", precedences[i]->trait_method->method_name->val, trait->name->val);
+ }
+ zend_string_release(lcname);
+ }
+ ++j;
+ }
+ }
+ ++i;
+ }
+}
+/* }}} */
+
+static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ uint32_t i;
+ HashTable *overriden = NULL;
+ zend_string *key;
+ zend_function *fn;
+
+ for (i = 0; i < ce->num_traits; i++) {
+ if (ce->trait_precedences) {
+ HashTable exclude_table;
+
+ /* TODO: revisit this start size, may be its not optimal */
+ zend_hash_init_ex(&exclude_table, 8, NULL, NULL, 0, 0);
+
+ zend_traits_compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]);
+
+ /* copies functions, applies defined aliasing, and excludes unused trait methods */
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->traits[i]->function_table, key, fn) {
+ zend_traits_copy_functions(key, fn, ce, &overriden, &exclude_table TSRMLS_CC);
+ } ZEND_HASH_FOREACH_END();
+
+ zend_hash_destroy(&exclude_table);
+ } else {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->traits[i]->function_table, key, fn) {
+ zend_traits_copy_functions(key, fn, ce, &overriden, NULL TSRMLS_CC);
+ } ZEND_HASH_FOREACH_END();
+ }
+ }
+
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, fn) {
+ zend_fixup_trait_method(fn, ce);
+ } ZEND_HASH_FOREACH_END();
+
+ if (overriden) {
+ zend_hash_destroy(overriden);
+ FREE_HASHTABLE(overriden);
+ }
+}
+/* }}} */
+
+static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t current_trait, zend_string *prop_name, zend_class_entry *coliding_ce) /* {{{ */
+{
+ size_t i;
+
+ if (coliding_ce == ce) {
+ for (i = 0; i < current_trait; i++) {
+ if (zend_hash_exists(&ce->traits[i]->properties_info, prop_name)) {
+ return ce->traits[i];
+ }
+ }
+ }
+
+ return coliding_ce;
+}
+/* }}} */
+
+static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ size_t i;
+ zend_property_info *property_info;
+ zend_property_info *coliding_prop;
+ zval compare_result;
+ zend_string* prop_name;
+ const char* class_name_unused;
+ zend_bool not_compatible;
+ zval* prop_value;
+ uint32_t flags;
+ zend_string *doc_comment;
+
+ /* In the following steps the properties are inserted into the property table
+ * for that, a very strict approach is applied:
+ * - check for compatibility, if not compatible with any property in class -> fatal
+ * - if compatible, then strict notice
+ */
+ for (i = 0; i < ce->num_traits; i++) {
+ ZEND_HASH_FOREACH_PTR(&ce->traits[i]->properties_info, property_info) {
+ /* first get the unmangeld name if necessary,
+ * then check whether the property is already there
+ */
+ flags = property_info->flags;
+ if ((flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
+ prop_name = zend_string_copy(property_info->name);
+ } else {
+ const char *pname;
+ size_t pname_len;
+
+ /* for private and protected we need to unmangle the names */
+ zend_unmangle_property_name_ex(property_info->name,
+ &class_name_unused, &pname, &pname_len);
+ prop_name = zend_string_init(pname, pname_len, 0);
+ }
+
+ /* next: check for conflicts with current class */
+ if ((coliding_prop = zend_hash_find_ptr(&ce->properties_info, prop_name)) != NULL) {
+ if (coliding_prop->flags & ZEND_ACC_SHADOW) {
+ zend_hash_del(&ce->properties_info, prop_name);
+ flags |= ZEND_ACC_CHANGED;
+ } else {
+ if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))
+ == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) {
+ /* flags are identical, now the value needs to be checked */
+ if (flags & ZEND_ACC_STATIC) {
+ not_compatible = (FAILURE == compare_function(&compare_result,
+ &ce->default_static_members_table[coliding_prop->offset],
+ &ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC))
+ || (Z_LVAL(compare_result) != 0);
+ } else {
+ not_compatible = (FAILURE == compare_function(&compare_result,
+ &ce->default_properties_table[coliding_prop->offset],
+ &ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC))
+ || (Z_LVAL(compare_result) != 0);
+ }
+ } else {
+ /* the flags are not identical, thus, we assume properties are not compatible */
+ not_compatible = 1;
+ }
+
+ if (not_compatible) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
+ find_first_definition(ce, i, prop_name, coliding_prop->ce)->name->val,
+ property_info->ce->name->val,
+ prop_name->val,
+ ce->name->val);
+ } else {
+ zend_error(E_STRICT,
+ "%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed",
+ find_first_definition(ce, i, prop_name, coliding_prop->ce)->name->val,
+ property_info->ce->name->val,
+ prop_name->val,
+ ce->name->val);
+ zend_string_release(prop_name);
+ continue;
+ }
+ }
+ }
+
+ /* property not found, so lets add it */
+ if (flags & ZEND_ACC_STATIC) {
+ prop_value = &ce->traits[i]->default_static_members_table[property_info->offset];
+ } else {
+ prop_value = &ce->traits[i]->default_properties_table[property_info->offset];
+ }
+ if (Z_REFCOUNTED_P(prop_value)) Z_ADDREF_P(prop_value);
+
+ doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL;
+ zend_declare_property_ex(ce, prop_name,
+ prop_value, flags,
+ doc_comment TSRMLS_CC);
+ zend_string_release(prop_name);
+ } ZEND_HASH_FOREACH_END();
+ }
+}
+/* }}} */
+
+static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ int i = 0;
+ zend_trait_alias* cur_alias;
+ zend_string* lc_method_name;
+
+ if (ce->trait_aliases) {
+ while (ce->trait_aliases[i]) {
+ cur_alias = ce->trait_aliases[i];
+ /** The trait for this alias has not been resolved, this means, this
+ alias was not applied. Abort with an error. */
+ if (!cur_alias->trait_method->ce) {
+ if (cur_alias->alias) {
+ /** Plain old inconsistency/typo/bug */
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "An alias (%s) was defined for method %s(), but this method does not exist",
+ cur_alias->alias->val,
+ cur_alias->trait_method->method_name->val);
+ } else {
+ /** Here are two possible cases:
+ 1) this is an attempt to modifiy the visibility
+ of a method introduce as part of another alias.
+ Since that seems to violate the DRY principle,
+ we check against it and abort.
+ 2) it is just a plain old inconsitency/typo/bug
+ as in the case where alias is set. */
+
+ lc_method_name = zend_string_alloc(cur_alias->trait_method->method_name->len, 0);
+ zend_str_tolower_copy(
+ lc_method_name->val,
+ cur_alias->trait_method->method_name->val,
+ cur_alias->trait_method->method_name->len);
+ if (zend_hash_exists(&ce->function_table,
+ lc_method_name)) {
+ zend_string_free(lc_method_name);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "The modifiers for the trait alias %s() need to be changed in the same statment in which the alias is defined. Error",
+ cur_alias->trait_method->method_name->val);
+ } else {
+ zend_string_free(lc_method_name);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "The modifiers of the trait method %s() are changed, but this method does not exist. Error",
+ cur_alias->trait_method->method_name->val);
+
+ }
+ }
+ }
+ i++;
+ }
+ }
+}
+/* }}} */
+
+ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+
+ if (ce->num_traits <= 0) {
+ return;
+ }
+
+ /* complete initialization of trait strutures in ce */
+ zend_traits_init_trait_structures(ce TSRMLS_CC);
+
+ /* first care about all methods to be flattened into the class */
+ zend_do_traits_method_binding(ce TSRMLS_CC);
+
+ /* Aliases which have not been applied indicate typos/bugs. */
+ zend_do_check_for_inconsistent_traits_aliasing(ce TSRMLS_CC);
+
+ /* then flatten the properties into it, to, mostly to notfiy developer about problems */
+ zend_do_traits_property_binding(ce TSRMLS_CC);
+
+ /* verify that all abstract methods from traits have been implemented */
+ zend_verify_abstract_class(ce TSRMLS_CC);
+
+ /* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */
+ if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
+ ce->ce_flags -= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
+ }
+}
+/* }}} */
+
diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h
new file mode 100644
index 0000000000..9cc432080c
--- /dev/null
+++ b/Zend/zend_inheritance.h
@@ -0,0 +1,39 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2014 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_INHERITANCE_H
+#define ZEND_INHERITANCE_H
+
+#include "zend.h"
+
+BEGIN_EXTERN_C()
+
+ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_entry *iface TSRMLS_DC);
+ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC);
+
+ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC);
+ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC);
+
+ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC);
+void zend_do_early_binding(TSRMLS_D);
+
+END_EXTERN_C()
+
+#endif
+
diff --git a/configure.in b/configure.in
index 428c574bf8..616fc502a4 100644
--- a/configure.in
+++ b/configure.in
@@ -1490,11 +1490,8 @@ PHP_ADD_SOURCES(Zend, \
zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \
zend_closures.c zend_float.c zend_string.c zend_signal.c zend_generators.c \
- zend_virtual_cwd.c zend_ast.c)
-
-if test -r "$abs_srcdir/Zend/zend_objects.c"; then
- PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_default_classes.c)
-fi
+ zend_virtual_cwd.c zend_ast.c zend_objects.c zend_object_handlers.c zend_objects_API.c \
+ zend_default_classes.c zend_inheritance.c)
dnl Selectively disable optimization due to high RAM usage during
dnl compiling the executor.