当前位置:首页 > PHP教程 > php应用 > 列表

如何编译php文件

发布:smiling 来源: PHP粉丝网  添加日期:2020-05-08 13:52:01 浏览: 评论:0 

PHP是解析型高级语言,事实上从Zend内核的角度来看PHP就是一个普通的C程序,它有main函数,我们写的PHP代码是这个程序的输入,然后经过内核的处理输出结果,内核将PHP代码"翻译"为C程序可识别的过程就是PHP的编译。

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.  
  3.     const void *handler; //对应执行的C语言function,即每条opcode都有一个C function处理 
  4.  
  5.     znode_op op1; //操作数1 
  6.  
  7.     znode_op op2; //操作数2 
  8.  
  9.     znode_op result; //返回值 
  10.  
  11.     uint32_t extended_value;  
  12.  
  13.     uint32_t lineno; 
  14.  
  15.     zend_uchar opcode;  //opcode指令 
  16.  
  17.     zend_uchar op1_type; //操作数1类型 
  18.  
  19.     zend_uchar op2_type; //操作数2类型 
  20.  
  21.     zend_uchar result_type; //返回值类型 
  22.  
  23. }; 

所以PHP的解析过程任务就是将PHP代码(通过词法分析re2c,语法分析bison)转化为opcode数组,代码里的所有信息都保存在opcode中,然后将opcode数组交给zend引擎执行,opcode就是内核具体执行的命令,比如赋值、加减操作、函数调用等,每一条opcode都对应一个处理handle,这些handler是提前定义好的C函数。

  1. struct _zend_op_array { 
  2.  
  3.     //common是普通函数或类成员方法对应的opcodes快速访问时使用的字段 
  4.  
  5.     /* Common elements */ 
  6.  
  7.     zend_uchar type; 
  8.  
  9.     zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */ 
  10.  
  11.     uint32_t fn_flags; 
  12.  
  13.     zend_string *function_name; 
  14.  
  15.     zend_class_entry *scope; 
  16.  
  17.     zend_function *prototype; 
  18.  
  19.     uint32_t num_args; 
  20.  
  21.     uint32_t required_num_args; 
  22.  
  23.     zend_arg_info *arg_info; 
  24.  
  25.     /* END of common elements */ 
  26.  
  27.  
  28.  
  29.     uint32_t *refcount; 
  30.  
  31.  
  32.  
  33.     uint32_t last; 
  34.  
  35.      //opcode指令数组 
  36.  
  37.     zend_op *opcodes; 
  38.  
  39.  
  40.  
  41.     //PHP代码里定义的变量数:op_type为IS_CV的变量,不含IS_TMP_VAR、IS_VAR的 
  42.  
  43.     //编译前此值为0,然后发现一个新变量这个值就加1 
  44.  
  45.     int last_var; 
  46.  
  47.     //临时变量数:op_type为IS_TMP_VAR、IS_VAR的变量 
  48.  
  49.     uint32_t T; 
  50.  
  51.     //PHP变量名数组 
  52.  
  53.     zend_string **vars;//这个数组在ast编译期间配合last_var用来确定各个变量的编号,非常重要的一步操作 
  54.  
  55.  
  56.  
  57.     int last_live_range; 
  58.  
  59.     int last_try_catch; 
  60.  
  61.     zend_live_range *live_range; 
  62.  
  63.     zend_try_catch_element *try_catch_array; 
  64.  
  65.  
  66.  
  67.     //静态变量符号表:通过static声明的 
  68.  
  69.     /* static variables support */ 
  70.  
  71.     HashTable *static_variables; 
  72.  
  73.  
  74.  
  75.     zend_string *filename; 
  76.  
  77.     uint32_t line_start; 
  78.  
  79.     uint32_t line_end; 
  80.  
  81.     zend_string *doc_comment; 
  82.  
  83.     uint32_t early_binding; /* the linked list of delayed declarations */ 
  84.  
  85.  
  86.  
  87.     //字面量数量 
  88.  
  89.     int last_literal; 
  90.  
  91.     //字面量(常量)数组,这些都是在PHP代码定义的一些值 
  92.  
  93.     zval *literals; 
  94.  
  95.  
  96.  
  97.     //运行时缓存数组大小 
  98.  
  99.     int  cache_size; 
  100.  
  101.     //运行时缓存,主要用于缓存一些znode_op以便于快速获取数据,后面单独介绍这个机制 
  102.  
  103.     void **run_time_cache; 
  104.  
  105.  
  106.  
  107.     void *reserved[ZEND_MAX_RESERVED_RESOURCES]; 
  108.  
  109. }; 

opcode指令:即PHP代码具体对应的处理动作,与二进制程序中的代码段对应

字面量存储:PHP代码中定义的一些变量初始值、调用的函数名称、类名称、常量名称等等称之为字面量,这些值用于执行时初始化变量、函数调用等等

变量分配情况:与字面量类似,这里指的是当前opcodes定义了多少变量、临时变量,每个变量都有一个对应的编号,执行初始化按照总的数目一次性分配zval,使用时也完全按照编号索引,而不是根据变量名索引

从PHP代码到opcode是怎么实现的?

最容易想到的方式就是正则匹配,当然过程没有这么简单。PHP编译过程包括词法分析、语法分析,使用re2c、bison完成,旧的PHP版本直接生成了opcode,PHP7新增了抽象语法树(AST),在语法分析阶段生成AST,然后再生成opcode数组

Tags: 如何编译php文件

分享到: