### 3.3.1 數據結構
執行流程中有幾個重要的數據結構,先看下這幾個結構。
#### 3.3.1.1 opcode
opcode是將PHP代碼編譯產生的Zend虛擬機可識別的指令,php7共有173個opcode,定義在`zend_vm_opcodes.h`中,PHP中的所有語法實現都是由這些opcode組成的。
```c
struct _zend_op {
const void *handler; //對應執行的C語言function,即每條opcode都有一個C function處理
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; //返回值類型
};
```
#### 3.3.1.2 zend_op_array
`zend_op_array`是Zend引擎執行階段的輸入,整個執行階段的操作都是圍繞著這個結構,關于其具體結構前面我們已經講過了。

這里再重復說下zend_op_array幾個核心組成部分:
* __opcode指令__:即PHP代碼具體對應的處理動作,與二進制程序中的代碼段對應
* __字面量存儲__:PHP代碼中定義的一些變量初始值、調用的函數名稱、類名稱、常量名稱等等稱之為字面量,這些值用于執行時初始化變量、函數調用等等
* __變量分配情況__:與字面量類似,這里指的是當前opcodes定義了多少變量、臨時變量,每個變量都有一個對應的編號,執行初始化按照總的數目一次性分配zval,使用時也完全按照編號索引,而不是根據變量名索引
#### 3.3.1.3 zend_executor_globals
`zend_executor_globals executor_globals`是PHP整個生命周期中最主要的一個結構,是一個全局變量,在main執行前分配(非ZTS下),直到PHP退出,它記錄著當前請求全部的信息,經常見到的一個宏`EG`操作的就是這個結構。
```c
//zend_compile.c
#ifndef ZTS
ZEND_API zend_compiler_globals compiler_globals;
ZEND_API zend_executor_globals executor_globals;
#endif
//zend_globals_macros.h
# define EG(v) (executor_globals.v)
```
`zend_executor_globals`結構非常大,定義在`zend_globals.h`中,比較重要的幾個字段含義如下圖所示:

#### 3.3.1.4 zend_execute_data
`zend_execute_data`是執行過程中最核心的一個結構,每次函數的調用、include/require、eval等都會生成一個新的結構,它表示當前的作用域、代碼的執行位置以及局部變量的分配等等,等同于機器碼執行過程中stack的角色,后面分析具體執行流程的時候會詳細分析其作用。
```c
#define EX(element) ((execute_data)->element)
//zend_compile.h
struct _zend_execute_data {
const zend_op *opline; //指向當前執行的opcode,初始時指向zend_op_array起始位置
zend_execute_data *call; /* current call */
zval *return_value; //返回值指針
zend_function *func; //當前執行的函數(非函數調用時為空)
zval This; //這個值并不僅僅是面向對象的this,還有另外兩個值也通過這個記錄:call_info + num_args,分別存在zval.u1.reserved、zval.u2.num_args
zend_class_entry *called_scope; //當前call的類
zend_execute_data *prev_execute_data; //函數調用時指向調用位置作用空間
zend_array *symbol_table; //全局變量符號表
#if ZEND_EX_USE_RUN_TIME_CACHE
void **run_time_cache; /* cache op_array->run_time_cache */
#endif
#if ZEND_EX_USE_LITERALS
zval *literals; //字面量數組,與func.op_array->literals相同
#endif
};
```
zend_execute_data與zend_op_array的關聯關系:

- 前言
- 第1章 PHP基本架構
- 1.1 PHP簡介
- 1.2 PHP7的改進
- 1.3 FPM
- 1.3.1 概述
- 1.3.2 基本實現
- 1.3.3 FPM的初始化
- 1.3.4 請求處理
- 1.3.5 進程管理
- 1.4 PHP執行的幾個階段
- 第2章 變量
- 2.1 變量的內部實現
- 2.2 數組
- 2.3 靜態變量
- 2.4 全局變量
- 2.5 常量
- 第3章 Zend虛擬機
- 3.1 PHP代碼的編譯
- 3.1.1 詞法解析、語法解析
- 3.1.2 抽象語法樹編譯流程
- 3.2 函數實現
- 3.2.1 內部函數
- 3.2.2 用戶函數的實現
- 3.3 Zend引擎執行流程
- 3.3.1 基本結構
- 3.3.2 執行流程
- 3.3.3 函數的執行流程
- 3.3.4 全局execute_data和opline
- 3.4 面向對象實現
- 3.4.1 類
- 3.4.2 對象
- 3.4.3 繼承
- 3.4.4 動態屬性
- 3.4.5 魔術方法
- 3.4.6 類的自動加載
- 3.5 運行時緩存
- 3.6 Opcache
- 3.6.1 opcode緩存
- 3.6.2 opcode優化
- 3.6.3 JIT
- 第4章 PHP基礎語法實現
- 4.1 類型轉換
- 4.2 選擇結構
- 4.3 循環結構
- 4.4 中斷及跳轉
- 4.5 include/require
- 4.6 異常處理
- 第5章 內存管理
- 5.1 Zend內存池
- 5.2 垃圾回收
- 第6章 線程安全
- 6.1 什么是線程安全
- 6.2 線程安全資源管理器
- 第7章 擴展開發
- 7.1 概述
- 7.2 擴展的實現原理
- 7.3 擴展的構成及編譯
- 7.3.1 擴展的構成
- 7.3.2 編譯工具
- 7.3.3 編寫擴展的基本步驟
- 7.3.4 config.m4
- 7.4 鉤子函數
- 7.5 運行時配置
- 7.5.1 全局變量
- 7.5.2 ini配置
- 7.6 函數
- 7.6.1 內部函數注冊
- 7.6.2 函數參數解析
- 7.6.3 引用傳參
- 7.6.4 函數返回值
- 7.6.5 函數調用
- 7.7 zval的操作
- 7.7.1 新生成各類型zval
- 7.7.2 獲取zval的值及類型
- 7.7.3 類型轉換
- 7.7.4 引用計數
- 7.7.5 字符串操作
- 7.7.6 數組操作
- 7.8 常量
- 7.9 面向對象
- 7.9.1 內部類注冊
- 7.9.2 定義成員屬性
- 7.9.3 定義成員方法
- 7.9.4 定義常量
- 7.9.5 類的實例化
- 7.10 資源類型
- 7.11 經典擴展解析
- 7.8.1 Yaf
- 7.8.2 Redis
- 第8章 命名空間
- 8.1 概述
- 8.2 命名空間的定義
- 8.2.1 定義語法
- 8.2.2 內部實現
- 8.3 命名空間的使用
- 8.3.1 基本用法
- 8.3.2 use導入
- 8.3.3 動態用法
- 附錄
- break/continue按標簽中斷語法實現
- defer推遲函數調用語法的實現
- 一起線上事故引發的對PHP超時控制的思考