Browse Source

Implement call_user_func() and call_user_func_array() using special opcodes.

In some rare cases it leads to insignificant changes in error messages.
pull/732/merge
Dmitry Stogov 12 years ago
parent
commit
de306e7088
  1. 2
      Zend/tests/001.phpt
  2. 2
      Zend/tests/002.phpt
  3. 2
      Zend/tests/003.phpt
  4. 2
      Zend/tests/call_user_func_004.phpt
  5. 2
      Zend/tests/call_user_func_005.phpt
  6. 2
      Zend/tests/fr47160.phpt
  7. 2
      Zend/tests/objects_027.phpt
  8. 184
      Zend/zend_compile.c
  9. 17
      Zend/zend_execute.c
  10. 1
      Zend/zend_execute_API.c
  11. 237
      Zend/zend_vm_def.h
  12. 521
      Zend/zend_vm_execute.h
  13. 6
      Zend/zend_vm_opcodes.c
  14. 3
      Zend/zend_vm_opcodes.h
  15. 1
      ext/opcache/Optimizer/optimize_func_calls.c

2
Zend/tests/001.phpt

@ -44,7 +44,7 @@ int(0)
int(2)
int(0)
Warning: Missing argument 2 for test3() in %s on line %d
Warning: Missing argument 2 for test3()%s
int(1)
int(2)
int(1)

2
Zend/tests/002.phpt

@ -85,7 +85,7 @@ bool(false)
Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)
Warning: Missing argument 2 for test3() in %s on line %d
Warning: Missing argument 2 for test3()%s
int(1)
Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d

2
Zend/tests/003.phpt

@ -60,7 +60,7 @@ array(2) {
array(0) {
}
Warning: Missing argument 2 for test3() in %s on line %d
Warning: Missing argument 2 for test3()%s
array(1) {
[0]=>
int(1)

2
Zend/tests/call_user_func_004.phpt

@ -13,6 +13,6 @@ call_user_func(array('foo', 'teste'));
?>
--EXPECTF--
Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method foo::teste() should not be called statically in %s on line %d
Strict Standards: %son-static method foo::teste() should not be called statically in %s on line %d
Fatal error: Using $this when not in object context in %s on line %d

2
Zend/tests/call_user_func_005.phpt

@ -18,7 +18,7 @@ var_dump(call_user_func(array('foo', 'teste')));
?>
--EXPECTF--
Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method foo::teste() should not be called statically in %s on line %d
Strict Standards: %son-static method foo::teste() should not be called statically in %s on line %d
%string|unicode%(1) "x"
array(1) {
[0]=>

2
Zend/tests/fr47160.phpt

@ -105,7 +105,7 @@ Hello, you
Notice: Undefined variable: this in %s on line %d
NULL
Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method Hello::world() should not be called statically in %s on line %d
Strict Standards: %son-static method Hello::world() should not be called statically in %s on line %d
Hello, you
Notice: Undefined variable: this in %s on line %d

2
Zend/tests/objects_027.phpt

@ -35,7 +35,7 @@ object(foo)#%d (0) {
object(foo)#%d (0) {
}
Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method foo::test() should not be called statically in %s on line %d
Strict Standards: %son-static method foo::test() should not be called statically in %s on line %d
Strict Standards: Non-static method bar::show() should not be called statically in %s on line %d
object(foo)#%d (0) {

184
Zend/zend_compile.c

@ -2539,6 +2539,170 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na
}
/* }}} */
static int zend_do_convert_call(zend_op *init_opline, zend_op *opline, long num_args, zend_function **func_ptr TSRMLS_CC) /* {{{ */
{
zval *function_name;
zend_string *lcname;
zend_function *function;
*func_ptr = NULL;
if (opline->op1_type == IS_CONST && Z_TYPE(CONSTANT(opline->op1.constant)) == IS_STRING) {
function_name = &CONSTANT(opline->op1.constant);
lcname = STR_ALLOC(Z_STRLEN_P(function_name), 0);
zend_str_tolower_copy(lcname->val, Z_STRVAL_P(function_name), Z_STRLEN_P(function_name));
if (((function = zend_hash_find_ptr(CG(function_table), lcname)) == NULL) ||
((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) &&
(function->type == ZEND_INTERNAL_FUNCTION))) {
function = NULL;
STR_RELEASE(lcname);
opline->opcode = ZEND_INIT_USER_CALL;
opline->extended_value = num_args;
opline->op2_type = opline->op1_type;
opline->op2 = opline->op1;
opline->op1_type = IS_CONST;
opline->op1 = init_opline->op2;
SET_UNUSED(opline->result);
MAKE_NOP(init_opline);
return 1;
} else {
STR_RELEASE(Z_STR_P(function_name));
Z_STR_P(function_name) = zend_new_interned_string(lcname TSRMLS_CC);
opline->opcode = ZEND_INIT_FCALL;
opline->extended_value = num_args;
opline->op2_type = IS_CONST;
opline->op2.constant = opline->op1.constant;
GET_CACHE_SLOT(opline->op2.constant);
SET_UNUSED(opline->op1);
SET_UNUSED(opline->result);
MAKE_NOP(init_opline);
*func_ptr = function;
return 1;
}
} else {
opline->opcode = ZEND_INIT_USER_CALL;
opline->extended_value = num_args;
opline->op2_type = opline->op1_type;
opline->op2 = opline->op1;
opline->op1_type = IS_CONST;
opline->op1 = init_opline->op2;
SET_UNUSED(opline->result);
MAKE_NOP(init_opline);
return 1;
}
return 0;
}
/* }}} */
static int zend_do_convert_call_user_func(zend_op *init_opline, zend_uint num_args TSRMLS_DC) /* {{{ */
{
zend_op *opline = init_opline + 1;
int level = 0;
int converted = 0;
zend_uint arg_num = 0;
zend_function *func = NULL;
while (1) {
switch (opline->opcode) {
case ZEND_SEND_VAL:
case ZEND_SEND_VAL_EX:
case ZEND_SEND_VAR:
case ZEND_SEND_VAR_EX:
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_REF:
case ZEND_SEND_UNPACK:
if (level == 0) {
if (!converted) {
if (opline->opcode == ZEND_SEND_UNPACK) {
return 0;
}
if (!zend_do_convert_call(init_opline, opline, init_opline->extended_value - 1, &func TSRMLS_CC)) {
return 0;
}
converted = 1;
} else {
/* Use ZEND_SEND_USER instruction for parameters that may pass by reference */
if (opline->opcode != ZEND_SEND_VAL &&
(func == NULL ||
ARG_SHOULD_BE_SENT_BY_REF(func, opline->op2.num-1))) {
opline->opcode = ZEND_SEND_USER;
}
opline->op2.num--;
}
if (++arg_num == num_args) {
return 1;
}
}
break;
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_NEW:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_FCALL:
level++;
break;
case ZEND_DO_FCALL:
level--;
break;
}
opline++;
}
}
/* }}} */
static int zend_do_convert_call_user_func_array(zend_op *init_opline TSRMLS_DC) /* {{{ */
{
zend_op *opline = init_opline + 1;
zend_op *send1 = NULL, *send2 = NULL;
int level = 0;
zend_function *func;
do {
switch (opline->opcode) {
case ZEND_SEND_VAL:
case ZEND_SEND_VAL_EX:
case ZEND_SEND_VAR:
case ZEND_SEND_VAR_EX:
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_REF:
case ZEND_SEND_UNPACK:
if (level == 0) {
if (opline->opcode == ZEND_SEND_UNPACK) {
return 0;
}
if (send1 == NULL) {
send1 = opline;
} else {
send2 = opline;
}
}
break;
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_NEW:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_FCALL:
level++;
break;
case ZEND_DO_FCALL:
level--;
break;
}
opline++;
} while (send2 == NULL);
if (!zend_do_convert_call(init_opline, send1, 0, &func TSRMLS_CC)) {
return 0;
}
send2->opcode = ZEND_SEND_ARRAY;
send2->extended_value = 0;
send2->op2.num--;
return 1;
}
/* }}} */
void zend_do_end_function_call(znode *function_name, znode *result, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */
{
zend_op *opline;
@ -2558,6 +2722,26 @@ void zend_do_end_function_call(znode *function_name, znode *result, int is_metho
if (opline->opcode == ZEND_NEW) {
call_flags = ZEND_CALL_CTOR;
} else {
zend_function *func = fcall->fbc;
if (func && func->type == ZEND_INTERNAL_FUNCTION) {
/* Convert calls to some internal functions into built-ins */
if (func->common.function_name->len == sizeof("call_user_func")-1 &&
memcmp(func->common.function_name->val, "call_user_func", sizeof("call_user_func")-1) == 0) {
if (fcall->arg_num > 0) {
if (zend_do_convert_call_user_func(opline, fcall->arg_num TSRMLS_CC)) {
fcall->arg_num--;
}
}
} else if (func->common.function_name->len == sizeof("call_user_func_array")-1 &&
memcmp(func->common.function_name->val, "call_user_func_array", sizeof("call_user_func_array")-1) == 0) {
if (fcall->arg_num == 2) {
if (zend_do_convert_call_user_func_array(opline TSRMLS_CC)) {
fcall->arg_num = 0;
}
}
}
}
}
opline = get_next_op(CG(active_op_array) TSRMLS_CC);

17
Zend/zend_execute.c

@ -76,6 +76,23 @@ static zend_always_inline void zend_pzval_unlock_func(zval *z, zend_free_op *sho
}
}
static ZEND_FUNCTION(pass)
{
}
static const zend_internal_function zend_pass_function = {
ZEND_INTERNAL_FUNCTION, /* type */
NULL, /* name */
NULL, /* scope */
0, /* fn_flags */
NULL, /* prototype */
0, /* num_args */
0, /* required_num_args */
NULL, /* arg_info */
ZEND_FN(pass), /* handler */
NULL /* module */
};
#undef zval_ptr_dtor
#define zval_ptr_dtor(zv) i_zval_ptr_dtor(zv ZEND_FILE_LINE_CC TSRMLS_CC)
#define zval_ptr_dtor_nogc(zv) i_zval_ptr_dtor_nogc(zv ZEND_FILE_LINE_CC TSRMLS_CC)

1
Zend/zend_execute_API.c

@ -825,7 +825,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
} else if (Z_ISREF(fci->params[i]) &&
/* don't separate references for __call */
(func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) {
param = &tmp;
param = ZEND_CALL_ARG(call, i+1);
ZVAL_DUP(param, Z_REFVAL(fci->params[i]));
} else {

237
Zend/zend_vm_def.h

@ -2507,6 +2507,47 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
}
}
ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMP|VAR|CV)
{
USE_OPLINE
zend_free_op free_op2;
zval *function_name = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
zend_fcall_info_cache fcc;
char *error = NULL;
zend_function *func;
zend_class_entry *called_scope;
zend_object *object;
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
if (error) {
efree(error);
}
func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
Z_ADDREF_P(function_name);
}
called_scope = fcc.called_scope;
object = fcc.object;
if (object) {
GC_REFCOUNT(object)++; /* For $this pointer */
}
} else {
zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
efree(error);
func = (zend_function*)&zend_pass_function;
called_scope = NULL;
object = NULL;
}
EX(call) = zend_vm_stack_push_call_frame(
func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
FREE_OP2();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
{
@ -3287,6 +3328,202 @@ ZEND_VM_C_LABEL(unpack_iter_dtor):
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
{
USE_OPLINE
zend_free_op free_op1;
zval *args;
SAVE_OPLINE();
args = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
if (Z_TYPE_P(args) != IS_ARRAY) {
zend_error(E_WARNING, "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args)));
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
} else {
zend_uint arg_num = 1;
HashTable *ht = Z_ARRVAL_P(args);
zval *arg, *param, tmp;
zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht) TSRMLS_CC);
if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_TMP_VAR && Z_IMMUTABLE_P(args)) {
zend_uint i;
int separate = 0;
/* check if any of arguments are going to be passed by reference */
for (i = 0; i < zend_hash_num_elements(ht); i++) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
separate = 1;
break;
}
}
if (separate) {
zval_copy_ctor(args);
ht = Z_ARRVAL_P(args);
}
}
param = ZEND_CALL_ARG(EX(call), arg_num);
ZEND_HASH_FOREACH_VAL(ht, arg) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
// TODO: Scalar values don't have reference counters anymore.
// They are assumed to be 1, and they may be easily passed by
// reference now. However, previously scalars with refcount==1
// might be passed and with refcount>1 might not. We can support
// only single behavior ???
#if 0
if (Z_REFCOUNTED_P(arg) &&
// This solution breaks the following test (omit warning message) ???
// Zend/tests/bug61273.phpt
// ext/reflection/tests/bug42976.phpt
// ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
#else
if (!Z_REFCOUNTED_P(arg) ||
// This solution breaks the following test (emit warning message) ???
// ext/pdo_sqlite/tests/pdo_005.phpt
#endif
(!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
arg_num,
EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
EX(call)->func->common.scope ? "::" : "",
EX(call)->func->common.function_name->val);
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
break;
}
if (Z_REFCOUNTED_P(arg)) {
Z_DELREF_P(arg);
}
ZVAL_DUP(&tmp, arg);
ZVAL_NEW_REF(arg, &tmp);
Z_ADDREF_P(arg);
} else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
Z_ADDREF_P(arg);
} else if (Z_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
ZVAL_COPY_VALUE(param, arg);
} else if (Z_ISREF_P(arg) &&
/* don't separate references for __call */
(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
ZVAL_DUP(param, Z_REFVAL_P(arg));
} else {
ZVAL_COPY(param, arg);
}
EX(call)->num_args++;
arg_num++;
param++;
} ZEND_HASH_FOREACH_END();
}
FREE_OP1();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
{
USE_OPLINE
zval *arg, *param, tmp;
zend_free_op free_op1;
arg = GET_OP1_ZVAL_PTR(BP_VAR_R);
param = ZEND_CALL_ARG(EX(call), opline->op2.num);
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
// TODO: Scalar values don't have reference counters anymore.
// They are assumed to be 1, and they may be easily passed by
// reference now. However, previously scalars with refcount==1
// might be passed and with refcount>1 might not. We can support
// only single behavior ???
#if 0
if (Z_REFCOUNTED_P(arg) &&
// This solution breaks the following test (omit warning message) ???
// Zend/tests/bug61273.phpt
// ext/reflection/tests/bug42976.phpt
// ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
#else
if (!Z_REFCOUNTED_P(arg) ||
// This solution breaks the following test (emit warning message) ???
// ext/pdo_sqlite/tests/pdo_005.phpt
#endif
(!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
EX(call)->func->common.scope ? "::" : "",
EX(call)->func->common.function_name->val);
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
FREE_OP1();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
if (Z_REFCOUNTED_P(arg)) {
Z_DELREF_P(arg);
}
ZVAL_DUP(&tmp, arg);
ZVAL_NEW_REF(arg, &tmp);
Z_ADDREF_P(arg);
} else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
Z_ADDREF_P(arg);
} else if (Z_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
ZVAL_COPY_VALUE(param, arg);
} else if (Z_ISREF_P(arg) &&
/* don't separate references for __call */
(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
ZVAL_DUP(param, Z_REFVAL_P(arg));
} else {
ZVAL_COPY(param, arg);
}
EX(call)->num_args = opline->op2.num;
FREE_OP1();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY)
{
USE_OPLINE

521
Zend/zend_vm_execute.h

@ -888,6 +888,123 @@ unpack_iter_dtor:
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *args;
SAVE_OPLINE();
args = get_zval_ptr_deref(opline->op1_type, &opline->op1, execute_data, &free_op1, BP_VAR_R);
if (Z_TYPE_P(args) != IS_ARRAY) {
zend_error(E_WARNING, "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args)));
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
} else {
zend_uint arg_num = 1;
HashTable *ht = Z_ARRVAL_P(args);
zval *arg, *param, tmp;
zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht) TSRMLS_CC);
if (opline->op1_type != IS_CONST && opline->op1_type != IS_TMP_VAR && Z_IMMUTABLE_P(args)) {
zend_uint i;
int separate = 0;
/* check if any of arguments are going to be passed by reference */
for (i = 0; i < zend_hash_num_elements(ht); i++) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
separate = 1;
break;
}
}
if (separate) {
zval_copy_ctor(args);
ht = Z_ARRVAL_P(args);
}
}
param = ZEND_CALL_ARG(EX(call), arg_num);
ZEND_HASH_FOREACH_VAL(ht, arg) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
// TODO: Scalar values don't have reference counters anymore.
// They are assumed to be 1, and they may be easily passed by
// reference now. However, previously scalars with refcount==1
// might be passed and with refcount>1 might not. We can support
// only single behavior ???
#if 0
if (Z_REFCOUNTED_P(arg) &&
// This solution breaks the following test (omit warning message) ???
// Zend/tests/bug61273.phpt
// ext/reflection/tests/bug42976.phpt
// ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
#else
if (!Z_REFCOUNTED_P(arg) ||
// This solution breaks the following test (emit warning message) ???
// ext/pdo_sqlite/tests/pdo_005.phpt
#endif
(!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
arg_num,
EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
EX(call)->func->common.scope ? "::" : "",
EX(call)->func->common.function_name->val);
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
break;
}
if (Z_REFCOUNTED_P(arg)) {
Z_DELREF_P(arg);
}
ZVAL_DUP(&tmp, arg);
ZVAL_NEW_REF(arg, &tmp);
Z_ADDREF_P(arg);
} else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
Z_ADDREF_P(arg);
} else if (Z_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
ZVAL_COPY_VALUE(param, arg);
} else if (Z_ISREF_P(arg) &&
/* don't separate references for __call */
(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
ZVAL_DUP(param, Z_REFVAL_P(arg));
} else {
ZVAL_COPY(param, arg);
}
EX(call)->num_args++;
arg_num++;
param++;
} ZEND_HASH_FOREACH_END();
}
FREE_OP(free_op1);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -1566,7 +1683,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
}
}
static int ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -1896,7 +2012,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
}
}
static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -2071,7 +2186,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
}
}
static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -2284,7 +2398,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
}
}
static int ZEND_FASTCALL ZEND_BW_NOT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -3828,6 +3941,47 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER(
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *function_name = opline->op2.zv;
zend_fcall_info_cache fcc;
char *error = NULL;
zend_function *func;
zend_class_entry *called_scope;
zend_object *object;
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
if (error) {
efree(error);
}
func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
Z_ADDREF_P(function_name);
}
called_scope = fcc.called_scope;
object = fcc.object;
if (object) {
GC_REFCOUNT(object)++; /* For $this pointer */
}
} else {
zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
efree(error);
func = (zend_function*)&zend_pass_function;
called_scope = NULL;
object = NULL;
}
EX(call) = zend_vm_stack_push_call_frame(
func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -4796,6 +4950,48 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMP_HANDLER(ZE
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
zval *function_name = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2 TSRMLS_CC);
zend_fcall_info_cache fcc;
char *error = NULL;
zend_function *func;
zend_class_entry *called_scope;
zend_object *object;
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
if (error) {
efree(error);
}
func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
Z_ADDREF_P(function_name);
}
called_scope = fcc.called_scope;
object = fcc.object;
if (object) {
GC_REFCOUNT(object)++; /* For $this pointer */
}
} else {
zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
efree(error);
func = (zend_function*)&zend_pass_function;
called_scope = NULL;
object = NULL;
}
EX(call) = zend_vm_stack_push_call_frame(
func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
zval_dtor(free_op2.var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_CASE_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -5634,6 +5830,48 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_VAR_HANDLER(ZE
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
zval *function_name = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2 TSRMLS_CC);
zend_fcall_info_cache fcc;
char *error = NULL;
zend_function *func;
zend_class_entry *called_scope;
zend_object *object;
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
if (error) {
efree(error);
}
func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
Z_ADDREF_P(function_name);
}
called_scope = fcc.called_scope;
object = fcc.object;
if (object) {
GC_REFCOUNT(object)++; /* For $this pointer */
}
} else {
zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
efree(error);
func = (zend_function*)&zend_pass_function;
called_scope = NULL;
object = NULL;
}
EX(call) = zend_vm_stack_push_call_frame(
func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
zval_ptr_dtor_nogc(free_op2.var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_CASE_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -7164,6 +7402,47 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CV_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *function_name = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var TSRMLS_CC);
zend_fcall_info_cache fcc;
char *error = NULL;
zend_function *func;
zend_class_entry *called_scope;
zend_object *object;
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
if (error) {
efree(error);
}
func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
Z_ADDREF_P(function_name);
}
called_scope = fcc.called_scope;
object = fcc.object;
if (object) {
GC_REFCOUNT(object)++; /* For $this pointer */
}
} else {
zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
efree(error);
func = (zend_function*)&zend_pass_function;
called_scope = NULL;
object = NULL;
}
EX(call) = zend_vm_stack_push_call_frame(
func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -13127,6 +13406,85 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *arg, *param, tmp;
zend_free_op free_op1;
arg = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
param = ZEND_CALL_ARG(EX(call), opline->op2.num);
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
// TODO: Scalar values don't have reference counters anymore.
// They are assumed to be 1, and they may be easily passed by
// reference now. However, previously scalars with refcount==1
// might be passed and with refcount>1 might not. We can support
// only single behavior ???
#if 0
if (Z_REFCOUNTED_P(arg) &&
// This solution breaks the following test (omit warning message) ???
// Zend/tests/bug61273.phpt
// ext/reflection/tests/bug42976.phpt
// ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
#else
if (!Z_REFCOUNTED_P(arg) ||
// This solution breaks the following test (emit warning message) ???
// ext/pdo_sqlite/tests/pdo_005.phpt
#endif
(!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
EX(call)->func->common.scope ? "::" : "",
EX(call)->func->common.function_name->val);
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
zval_ptr_dtor_nogc(free_op1.var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
if (Z_REFCOUNTED_P(arg)) {
Z_DELREF_P(arg);
}
ZVAL_DUP(&tmp, arg);
ZVAL_NEW_REF(arg, &tmp);
Z_ADDREF_P(arg);
} else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
Z_ADDREF_P(arg);
} else if (Z_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
ZVAL_COPY_VALUE(param, arg);
} else if (Z_ISREF_P(arg) &&
/* don't separate references for __call */
(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
ZVAL_DUP(param, Z_REFVAL_P(arg));
} else {
ZVAL_COPY(param, arg);
}
EX(call)->num_args = opline->op2.num;
zval_ptr_dtor_nogc(free_op1.var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_BOOL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -30286,6 +30644,83 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *arg, *param, tmp;
arg = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC);
param = ZEND_CALL_ARG(EX(call), opline->op2.num);
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
// TODO: Scalar values don't have reference counters anymore.
// They are assumed to be 1, and they may be easily passed by
// reference now. However, previously scalars with refcount==1
// might be passed and with refcount>1 might not. We can support
// only single behavior ???
#if 0
if (Z_REFCOUNTED_P(arg) &&
// This solution breaks the following test (omit warning message) ???
// Zend/tests/bug61273.phpt
// ext/reflection/tests/bug42976.phpt
// ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
#else
if (!Z_REFCOUNTED_P(arg) ||
// This solution breaks the following test (emit warning message) ???
// ext/pdo_sqlite/tests/pdo_005.phpt
#endif
(!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
EX(call)->func->common.scope ? "::" : "",
EX(call)->func->common.function_name->val);
if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (EX(call)->object) {
OBJ_RELEASE(EX(call)->object);
}
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
EX(call)->object = NULL;
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
if (Z_REFCOUNTED_P(arg)) {
Z_DELREF_P(arg);
}
ZVAL_DUP(&tmp, arg);
ZVAL_NEW_REF(arg, &tmp);
Z_ADDREF_P(arg);
} else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
Z_ADDREF_P(arg);
} else if (Z_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
ZVAL_COPY_VALUE(param, arg);
} else if (Z_ISREF_P(arg) &&
/* don't separate references for __call */
(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
ZVAL_DUP(param, Z_REFVAL_P(arg));
} else {
ZVAL_COPY(param, arg);
}
EX(call)->num_args = opline->op2.num;
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_BOOL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -43109,7 +43544,11 @@ void zend_init_opcodes_handlers(void)
ZEND_SEND_VAR_SPEC_CV_HANDLER,
ZEND_SEND_VAR_SPEC_CV_HANDLER,
ZEND_SEND_VAR_SPEC_CV_HANDLER,
ZEND_INIT_USER_CALL_SPEC_CONST_CONST_HANDLER,
ZEND_INIT_USER_CALL_SPEC_CONST_TMP_HANDLER,
ZEND_INIT_USER_CALL_SPEC_CONST_VAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_INIT_USER_CALL_SPEC_CONST_CV_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
@ -43130,6 +43569,31 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
@ -43140,50 +43604,21 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SEND_USER_SPEC_VAR_HANDLER,
ZEND_SEND_USER_SPEC_VAR_HANDLER,
ZEND_SEND_USER_SPEC_VAR_HANDLER,
ZEND_SEND_USER_SPEC_VAR_HANDLER,
ZEND_SEND_USER_SPEC_VAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SEND_USER_SPEC_CV_HANDLER,
ZEND_SEND_USER_SPEC_CV_HANDLER,
ZEND_SEND_USER_SPEC_CV_HANDLER,
ZEND_SEND_USER_SPEC_CV_HANDLER,
ZEND_SEND_USER_SPEC_CV_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,

6
Zend/zend_vm_opcodes.c

@ -140,9 +140,9 @@ const char *zend_vm_opcodes_map[169] = {
"ZEND_ISSET_ISEMPTY_DIM_OBJ",
"ZEND_SEND_VAL_EX",
"ZEND_SEND_VAR",
NULL,
NULL,
NULL,
"ZEND_INIT_USER_CALL",
"ZEND_SEND_ARRAY",
"ZEND_SEND_USER",
NULL,
NULL,
NULL,

3
Zend/zend_vm_opcodes.h

@ -141,6 +141,9 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);
#define ZEND_ISSET_ISEMPTY_DIM_OBJ 115
#define ZEND_SEND_VAL_EX 116
#define ZEND_SEND_VAR 117
#define ZEND_INIT_USER_CALL 118
#define ZEND_SEND_ARRAY 119
#define ZEND_SEND_USER 120
#define ZEND_PRE_INC_OBJ 132
#define ZEND_PRE_DEC_OBJ 133
#define ZEND_POST_INC_OBJ 134

1
ext/opcache/Optimizer/optimize_func_calls.c

@ -38,6 +38,7 @@ static void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_FCALL:
case ZEND_INIT_USER_CALL:
call_stack[call].opline = opline;
call++;
break;

Loading…
Cancel
Save