PHP主脚本会生成一个zend_op_array,每个function也会编译为独立的zend_op_array,所以从二进制程序的角度看zend_op_array包含着当前作用域下的所有堆栈信息,函数调用实际就是不同zend_op_array间的切换。
struct _zend_op_array {
//common是普通函数或类成员方法对应的opcodes快速访问时使用的字段,后面分析PHP函数实现的时候会详细讲
...
uint32_t *refcount;
uint32_t this_var;
uint32_t last;
//opcode指令数组
zend_op *opcodes;
//PHP代码里定义的变量数:op_type为IS_CV的变量,不含IS_TMP_VAR、IS_VAR的
//编译前此值为0,然后发现一个新变量这个值就加1
int last_var;
//临时变量数:op_type为IS_TMP_VAR、IS_VAR的变量
uint32_t T;
//PHP变量名数组
zend_string **vars; //这个数组在ast编译期间配合last_var用来确定各个变量的编号,非常重要的一步操作
...
//静态变量符号表:通过static声明的
HashTable *static_variables;
...
//字面量数量
int last_literal;
//字面量(常量)数组,这些都是在PHP代码定义的一些值
zval *literals;
//运行时缓存数组大小
int cache_size;
//运行时缓存,主要用于缓存一些znode_op以便于快速获取数据,后面单独介绍这个机制
void **run_time_cache;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
zend_op_array.opcodes指向指令列表,具体每条指令的结构如下:
struct _zend_op {
const void *handler; //指令执行handler
znode_op op1; //操作数1
znode_op op2; //操作数2
znode_op result; //返回值
uint32_t extended_value;
uint32_t lineno;
zend_uchar opcode; //opcode指令
zend_uchar op1_type; //操作数1类型
zend_uchar op2_type; //操作数2类型
zend_uchar result_type; //返回值类型
};
//操作数结构
typedef union _znode_op {
uint32_t constant;
uint32_t var;
uint32_t num;
uint32_t opline_num; /* Needs to be signed */
uint32_t jmp_offset;
} znode_op;
opcode各字段含义下面展开说明。