PHP是解析型高级语言,事实上从Zend内核的角度来看PHP就是一个普通的C程序,它有main函数,咱们写的PHP代码是这个程序的输入,而后通过内核的处理输出结果,内核将PHP代码"翻译"为C程序可识别的过程就是PHP的编译。node
C程序在编译时将一行行代码编译为机器码,每个操做都认为是一条机器指令,这些指令写入到编译后的二进制程序中,执行的时候将二进制程序load进相应的内存区域(常量区、数据区、代码区)、分配运行栈,而后从代码区起始位置开始执行,这是C程序编译、执行的简单过程。数组
一样,PHP的编译与普通的C程序相似,只是PHP代码没有编译成机器码,而是解析成了若干条opcode数组,每条opcode就是C里面普通的struct,含义对应C程序的机器指令,执行的过程就是引擎依次执行opcode,好比咱们在PHP里定义一个变量:$a = 123;
,最终到内核里执行就是malloc一块内存,而后把值写进去。缓存
在zend_compile.h文件中,opcode结构:函数
1 struct _zend_op { 2 const void *handler; //对应执行的C语言function,即每条opcode都有一个C function处理 3 znode_op op1; //操做数1 4 znode_op op2; //操做数2 5 znode_op result; //返回值 6 uint32_t extended_value; 7 uint32_t lineno; 8 zend_uchar opcode; //opcode指令 9 zend_uchar op1_type; //操做数1类型 10 zend_uchar op2_type; //操做数2类型 11 zend_uchar result_type; //返回值类型 12 };
因此PHP的解析过程任务就是将PHP代码(经过词法分析re2c,语法分析bison)转化为opcode数组,代码里的全部信息都保存在opcode中,而后将opcode数组交给zend引擎执行,opcode就是内核具体执行的命令,好比赋值、加减操做、函数调用等,每一条opcode都对应一个处理handle,这些handler是提早定义好的C函数。ui
1 struct _zend_op_array { 2 //common是普通函数或类成员方法对应的opcodes快速访问时使用的字段 3 /* Common elements */ 4 zend_uchar type; 5 zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */ 6 uint32_t fn_flags; 7 zend_string *function_name; 8 zend_class_entry *scope; 9 zend_function *prototype; 10 uint32_t num_args; 11 uint32_t required_num_args; 12 zend_arg_info *arg_info; 13 /* END of common elements */ 14 15 uint32_t *refcount; 16 17 uint32_t last; 18 //opcode指令数组 19 zend_op *opcodes; 20 21 //PHP代码里定义的变量数:op_type为IS_CV的变量,不含IS_TMP_VAR、IS_VAR的 22 //编译前此值为0,而后发现一个新变量这个值就加1 23 int last_var; 24 //临时变量数:op_type为IS_TMP_VAR、IS_VAR的变量 25 uint32_t T; 26 //PHP变量名数组 27 zend_string **vars;//这个数组在ast编译期间配合last_var用来肯定各个变量的编号,很是重要的一步操做 28 29 int last_live_range; 30 int last_try_catch; 31 zend_live_range *live_range; 32 zend_try_catch_element *try_catch_array; 33 34 //静态变量符号表:经过static声明的 35 /* static variables support */ 36 HashTable *static_variables; 37 38 zend_string *filename; 39 uint32_t line_start; 40 uint32_t line_end; 41 zend_string *doc_comment; 42 uint32_t early_binding; /* the linked list of delayed declarations */ 43 44 //字面量数量 45 int last_literal; 46 //字面量(常量)数组,这些都是在PHP代码定义的一些值 47 zval *literals; 48 49 //运行时缓存数组大小 50 int cache_size; 51 //运行时缓存,主要用于缓存一些znode_op以便于快速获取数据,后面单独介绍这个机制 52 void **run_time_cache; 53 54 void *reserved[ZEND_MAX_RESERVED_RESOURCES]; 55 };
opcode指令:即PHP代码具体对应的处理动做,与二进制程序中的代码段对应
字面量存储:PHP代码中定义的一些变量初始值、调用的函数名称、类名称、常量名称等等称之为字面量,这些值用于执行时初始化变量、函数调用等等
变量分配状况:与字面量相似,这里指的是当前opcodes定义了多少变量、临时变量,每一个变量都有一个对应的编号,执行初始化按照总的数目一次性分配zval,使用时也彻底按照编号索引,而不是根据变量名索引spa
从PHP代码到opcode是怎么实现的?最容易想到的方式就是正则匹配,固然过程没有这么简单。PHP编译过程包括词法分析、语法分析,使用re2c、bison完成,旧的PHP版本直接生成了opcode,PHP7新增了抽象语法树(AST),在语法分析阶段生成AST,而后再生成opcode数组prototype