]> granicus.if.org Git - php/commitdiff
Merge branch 'PHP-5.6'
authorNikita Popov <nikic@php.net>
Sat, 30 Aug 2014 18:45:10 +0000 (20:45 +0200)
committerNikita Popov <nikic@php.net>
Sat, 30 Aug 2014 18:45:10 +0000 (20:45 +0200)
Conflicts:
Zend/zend_compile.c

1  2 
Zend/zend_compile.c

index 5d1fe7fbd8523d1446991a95d28bbb3e4232b58b,54b01a845b5598e5861270ea61aedae75df8cdb0..4fbdff3059df7085ba6f7b742cd5e9a74d5c2261
@@@ -1073,782 -1281,15 +1073,782 @@@ static zend_function *do_inherit_method
  }
  /* }}} */
  
 -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;
        }
  }
  /* }}} */