zend_op_array.opcodes指向指令列表,具體每條指令的結構如下:
struct _zend_op { // *opline *opcodes
const void *handler; //指令執行handler
znode_op op1; //運算元1
znode_op op2; //運算元型別實際就是個32位整形,它主要用於儲存一些變數的索引位置、數值記錄等等
znode_op result; //返回值
uint32_t extended_value;
uint32_t lineno;
zend_uchar opcode; //opcode指令
zend_uchar op1_type; //運算元1型別
zend_uchar op2_type;
zend_uchar result_type; //返回值型別
};
//運算元結構
//比如賦值語句:"$a = 45;",兩個運算元分別記錄"$a"、"45"的儲存位置,執行時根據op2取到值"45",
//然後賦值給"$a",而"$a"的位置透過op1獲取到
//當然運算元並不是全部這麼用的,上面只是賦值時候的情況,其它操作會有不同的用法
//如函式呼叫時的傳參,op1記錄的就是傳遞的引數是第幾個,op2記錄的是引數的儲存位置,
//result記錄的是函式接收引數的儲存位置。
typedef union _znode_op { //運算元型別實際就是個32位整形,它主要用於儲存一些變數的索引位置、數值記錄等等
uint32_t constant; //運算元記錄著當前指令的關鍵資訊, 可以用於變數的儲存、訪問
uint32_t var;
uint32_t num;
uint32_t opline_num; /* Needs to be signed */
#if ZEND_USE_ABS_JMP_ADDR
zend_op *jmp_addr;
#else
uint32_t jmp_offset;
#endif
#if ZEND_USE_ABS_CONST_ADDR
zval *zv;
#endif
} znode_op;
//運算元有5種不同的型別;
#define IS_CONST (1<<0) //字面量,編譯時就可確定且不會改變的值,比如:$a = "hello~",其中字串"hello~"就是常量
#define IS_TMP_VAR (1<<1) //臨時變數 $a = "hello~" . time(),其中"hello~" . time()的值型別就是IS_TMP_VAR
//"123" + $b的結果型別也是IS_TMP_VAR,從這兩個例子可以猜測,臨時變數多是執行期間其它
//型別組合現生成的一箇中間值,由於它是現生成的,所以把IS_TMP_VAR賦值給IS_CV變數時不會增加其引用計數
#define IS_VAR (1<<2)//PHP變數
//這個很容易認為是PHP指令碼里的變數,其實不是,這裡PHP變數的含義可以這樣理解:PHP變數是沒有顯
//式的在PHP指令碼中定義的,不是直接在程式碼透過$var_name定義的
//。這個型別最常見的例子是PHP函式的返回值,再如$a[0]陣列這種,它取出的值也是IS_VAR,再比如$$a這種
#define IS_UNUSED (1<<3) /* Unused variable */ //表示運算元沒有用
#define IS_CV (1<<4) /* Compiled variable */
//PHP指令碼變數,即指令碼里透過$var_name定義的變數,這些變數是編譯階段確定的,所以是compile variable
//result_type除了上面幾種型別外還有一種型別EXT_TYPE_UNUSED (1<<5),返回值沒有使用時會用到,
//這個跟IS_UNUSED的區別是:IS_UNUSED表示本操作返回值沒有意義(也可簡單的認為沒有返回值),
//而EXT_TYPE_UNUSED的含義是有返回值,但是沒有用到,比如函式返回值沒有接收
本作品採用《CC 協議》,轉載必須註明作者和本文連結