diff options
author | Nikita Popov <nikic@php.net> | 2014-09-19 00:01:05 +0200 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2014-09-19 19:54:37 +0200 |
commit | c343ca4efba7e600f7d20c73fde34dbda8192e48 (patch) | |
tree | 55d4aa3f431db584f6d8ae2c34f6ff2406994a65 | |
parent | 2be8fdcfd6b719cf21e4344c75b46fd5704727e4 (diff) | |
download | php-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.dsp | 4 | ||||
-rw-r--r-- | Zend/ZendTS.dsp | 4 | ||||
-rw-r--r-- | Zend/zend_API.c | 1 | ||||
-rw-r--r-- | Zend/zend_compile.c | 1545 | ||||
-rw-r--r-- | Zend/zend_compile.h | 8 | ||||
-rw-r--r-- | Zend/zend_execute.c | 1 | ||||
-rw-r--r-- | Zend/zend_inheritance.c | 1574 | ||||
-rw-r--r-- | Zend/zend_inheritance.h | 39 | ||||
-rw-r--r-- | configure.in | 7 |
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. |