diff options
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 96 |
1 files changed, 65 insertions, 31 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 672f62b7ee..90ff26297a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1822,7 +1822,7 @@ void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC) /* } /* }}} */ -void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, const znode *initialization, znode *class_type, zend_uchar pass_by_reference TSRMLS_DC) /* {{{ */ +void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, const znode *initialization, znode *class_type, zend_uchar pass_by_reference, zend_bool is_variadic TSRMLS_DC) /* {{{ */ { zend_op *opline; zend_arg_info *cur_arg_info; @@ -1846,6 +1846,19 @@ void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, con } } + if (CG(active_op_array)->fn_flags & ZEND_ACC_VARIADIC) { + zend_error(E_COMPILE_ERROR, "Only the last parameter can be variadic"); + } + + if (is_variadic) { + if (op == ZEND_RECV_INIT) { + zend_error(E_COMPILE_ERROR, "Variadic parameter cannot have a default value"); + } + + op = ZEND_RECV_VARIADIC; + CG(active_op_array)->fn_flags |= ZEND_ACC_VARIADIC; + } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); CG(active_op_array)->num_args++; opline->opcode = op; @@ -1854,16 +1867,19 @@ void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, con if (op == ZEND_RECV_INIT) { SET_NODE(opline->op2, initialization); } else { - CG(active_op_array)->required_num_args = CG(active_op_array)->num_args; SET_UNUSED(opline->op2); + if (!is_variadic) { + CG(active_op_array)->required_num_args = CG(active_op_array)->num_args; + } } CG(active_op_array)->arg_info = erealloc(CG(active_op_array)->arg_info, sizeof(zend_arg_info)*(CG(active_op_array)->num_args)); cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1]; cur_arg_info->name = zend_new_interned_string(estrndup(Z_STRVAL(varname->u.constant), Z_STRLEN(varname->u.constant)), Z_STRLEN(varname->u.constant) + 1, 1 TSRMLS_CC); cur_arg_info->name_len = Z_STRLEN(varname->u.constant); cur_arg_info->type_hint = 0; - cur_arg_info->allow_null = 1; cur_arg_info->pass_by_reference = pass_by_reference; + cur_arg_info->allow_null = 1; + cur_arg_info->is_variadic = is_variadic; cur_arg_info->class_name = NULL; cur_arg_info->class_name_len = 0; @@ -3083,7 +3099,7 @@ static void do_inherit_method(zend_function *function) /* {{{ */ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */ { - zend_uint i; + zend_uint 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 @@ -3113,48 +3129,66 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c return 0; } - if (fe->common.type != ZEND_USER_FUNCTION - && (proto->common.fn_flags & ZEND_ACC_PASS_REST_BY_REFERENCE) != 0 - && (fe->common.fn_flags & ZEND_ACC_PASS_REST_BY_REFERENCE) == 0) { - 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; } - for (i=0; i < proto->common.num_args; i++) { - if (ZEND_LOG_XOR(fe->common.arg_info[i].class_name, proto->common.arg_info[i].class_name)) { + 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) + && 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->common.arg_info[i].class_name) { + if (fe_arg_info->class_name) { const char *fe_class_name, *proto_class_name; zend_uint fe_class_name_len, proto_class_name_len; - if (!strcasecmp(fe->common.arg_info[i].class_name, "parent") && proto->common.scope) { + if (!strcasecmp(fe_arg_info->class_name, "parent") && proto->common.scope) { fe_class_name = proto->common.scope->name; fe_class_name_len = proto->common.scope->name_length; - } else if (!strcasecmp(fe->common.arg_info[i].class_name, "self") && fe->common.scope) { + } else if (!strcasecmp(fe_arg_info->class_name, "self") && fe->common.scope) { fe_class_name = fe->common.scope->name; fe_class_name_len = fe->common.scope->name_length; } else { - fe_class_name = fe->common.arg_info[i].class_name; - fe_class_name_len = fe->common.arg_info[i].class_name_len; + fe_class_name = fe_arg_info->class_name; + fe_class_name_len = fe_arg_info->class_name_len; } - if (!strcasecmp(proto->common.arg_info[i].class_name, "parent") && proto->common.scope && proto->common.scope->parent) { + if (!strcasecmp(proto_arg_info->class_name, "parent") && proto->common.scope && proto->common.scope->parent) { proto_class_name = proto->common.scope->parent->name; proto_class_name_len = proto->common.scope->parent->name_length; - } else if (!strcasecmp(proto->common.arg_info[i].class_name, "self") && proto->common.scope) { + } else if (!strcasecmp(proto_arg_info->class_name, "self") && proto->common.scope) { proto_class_name = proto->common.scope->name; proto_class_name_len = proto->common.scope->name_length; } else { - proto_class_name = proto->common.arg_info[i].class_name; - proto_class_name_len = proto->common.arg_info[i].class_name_len; + proto_class_name = proto_arg_info->class_name; + proto_class_name_len = proto_arg_info->class_name_len; } if (strcasecmp(fe_class_name, proto_class_name)!=0) { @@ -3181,24 +3215,17 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c } } } - if (fe->common.arg_info[i].type_hint != proto->common.arg_info[i].type_hint) { + 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->common.arg_info[i].pass_by_reference != proto->common.arg_info[i].pass_by_reference) { + if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) { return 0; } } - if (proto->common.fn_flags & ZEND_ACC_PASS_REST_BY_REFERENCE) { - for (i=proto->common.num_args; i < fe->common.num_args; i++) { - if (!fe->common.arg_info[i].pass_by_reference) { - return 0; - } - } - } return 1; } /* }}} */ @@ -3271,6 +3298,13 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ if (arg_info->pass_by_reference) { *(offset++) = '&'; } + + if (arg_info->is_variadic) { + *(offset++) = '.'; + *(offset++) = '.'; + *(offset++) = '.'; + } + *(offset++) = '$'; if (arg_info->name) { @@ -3286,7 +3320,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ idx /= 10; } while (idx > 0); } - if (i >= required) { + if (i >= required && !arg_info->is_variadic) { *(offset++) = ' '; *(offset++) = '='; *(offset++) = ' '; |