|
|
|
@ -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++) = ' '; |
|
|
|
|