PHP内核在pass_two这个函数里面,会对op_array进行一个编译时到运行时的转化。
主要体现在以下几个地方:
重新分配literals
让literals和opcodes由原来分散存储的内存合并为连续的一块内存。这么做除了内存连续带来的性能提升之外,另一个好处是,在执行opline的时候,直接通过偏移量就可以拿到对应的字面量了,不需要传递op_array,相当于少传递了一个参数(之前需要通过op_array->literals的方式来获取)。
重新设置常量的constant值
znode_op::constant最终是要存储这个常量相对这条
opline的偏移量
在编译完AST生成完opcode之后,znode_op::constant存储的是这个常量在literals数组的索引。
znode_op::constant在从编译期转运行期之后,变成了相对这条opline的偏移量。
重新设置临时变量的var值
znode_op::var最终是要存储这个变量相对
execute_data的偏移量
我们知道,IS_CV变量它相对execute_data的偏移量在编译这个变量的时候就已经通过EX_NUM_TO_VAR确定了。但是,IS_TMP类型的变量,它的znode_op::var里面只存了这个临时变量是第几个,还没有确定这个临时变量相对execute_data的偏移量。所以,在编译时转化为运行时的阶段,需要确定好。
那么为什么只有IS_TMP需要做转化呢?而IS_CV不需要呢?这是和PHP栈帧的设计有关的,PHP的栈帧结构如下:
1 | /* |
可以发现,前面是IS_CV类型的变量,IS_TMP类型的变量在IS_CV变量的后面。所以,我们在编译出IS_TMP的时候,还无法确定IS_CV变量的个数,所以,也就无法确定IS_TMP相对于execute_data的偏移量。所以,得把IS_TMP的转化放在后面进行。