初始化阶段

3.3.3.1 初始化阶段

此阶段的主要工作有两个:查找函数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关系:

zend_exe_init

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

  • __call_info: 调用信息,通过 This.u1.reserved__ 记录,因为我们的主脚本、用户自定义函数调用、内核函数调用、include/require/eval等都会生成一个zend_execute_data,这个值就是用来区分这些不同类型的,对应的具体值为:ZEND_CALL_TOP_CODE、ZEND_CALL_NESTED_FUNCTION、ZEND_CALL_TOP_FUNCTION、ZEND_CALL_NESTED_CODE,这个信息是在分配zend_execute_data时显式声明的
  • num_args: 函数调用实际传入的参数数量,通过 This.u2.num_args 记录,比如示例中我们定义的函数有3个参数,其中1个是必传的,而我们调用时传入了2个,所以这个例子中的num_args就是2,这个值在编译时知道的,保存在 zend_op->extended_value
联系我们

邮箱 626512443@qq.com
电话 18611320371(微信)
QQ群 235681453

Copyright © 2015-2024

备案号:京ICP备15003423号-3