此阶段的主要工作有两个:查找函数zend_function、分配zend_execute_data。
上面的例子此过程执行的opcode为ZEND_INIT_FCALL,根据op_type计算可得handler为ZEND_INIT_FCALL_SPEC_CONST_HANDLER:
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *fname = EX_CONSTANT(opline->op2); //调用的函数名称通过操作数2记录
zval *func;
zend_function *fbc;
zend_execute_data *call;
//这里牵扯到zend的一种缓存机制:运行时缓存,后面我们会单独分析,这里忽略即可
...
//首先根据函数名去EG(function_table)索引zend_function
func = zend_hash_find(EG(function_table), Z_STR_P(fname));
if (UNEXPECTED(func == NULL)) {
SAVE_OPLINE();
zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(fname));
HANDLE_EXCEPTION();
}
fbc = Z_FUNC_P(func); //(*func).value.func
...
//分配zend_execute_data
call = zend_vm_stack_push_call_frame_ex(
opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
fbc, opline->extended_value, NULL, NULL);
call->prev_execute_data = EX(call);
EX(call) = call; //将当前正在运行的zend_execute_data.call指向新分配的zend_execute_data
ZEND_VM_NEXT_OPCODE();
}
当前zend_execute_data及新生成的zend_execute_data关系:

注意 This 这个值,它并不仅仅指的是面向对象中那个this,此外它还记录着其它两个信息: