}
/* }}} */
-void zend_check_writable_variable(const znode *variable) /* {{{ */
+static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
{
- zend_uint type = variable->EA;
+ uint32_t i, num_args;
- if (type & ZEND_PARSED_METHOD_CALL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Can't use method return value in write context");
- }
- if (type == ZEND_PARSED_FUNCTION_CALL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Can't use function return value in write context");
+ /* 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 ((fe->common.fn_flags & ZEND_ACC_VARIADIC)
++ 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;
}
}
/* }}} */