跟廠長學PHP核心7(六) 變數之zval

2021-08-27 20:42:45 字數 3347 閱讀 2329

記得網上流傳甚廣的段子「php是世界上最好的語言」,暫且不去討論是否言過其實,但至少php確實有獨特優勢的,比如它的弱型別,即只需要$符號即可宣告變數,使得php入手門檻極低,成為大家所青睞的web服務端語言。那麼它的變數是如何實現的呢?我們今天就來學習一下php的基本變數。

php的變數儲存在zval結構體中,在執行階段中編譯為op_array時就能看到zval的身影。結構體定義在zend/zend_types.h中,定義內容如下所示:

struct _zval_struct  v;

uint32_t type_info;

} u1;

union u2;

};

結構體的第乙個變數是zend_value,顧名思義,它其實也是乙個結構體,用於存放變數的值,比如整型、浮點型、引用計數、字串、陣列、物件、資源等。zend_value定義了眾多態別的指標,但這些型別並不都是變數的型別,有些是給核心自己使用的,比如指標ast、zv、ptr。

typedef union _zend_value  ww;

} zend_value;

u1是乙個聯合體,它聯合了結構體v和整型type_info。下面我們先來看一下結構體v的構成。

union  v;

uint32_t type_info;

} u1;

type是指變數的型別,剛在2.1中講到了zend_value是用來儲存變數的值,所以也應該有地方儲存變數的型別,而這就是type的職責。以下是php定義的所有變數型別,有我們熟知的布林、null、浮點、陣列、字串等型別。也有陌生的undef、indirect、ptr型別,變數型別在下一章中詳解,這裡不再贅述。

/* regular data types */

#define is_undef 0

#define is_null 1

#define is_false 2

#define is_true 3

#define is_long 4

#define is_double 5

#define is_string 6

#define is_array 7

#define is_object 8

#define is_resource 9

#define is_reference 10

/* constant expressions */

#define is_constant 11

#define is_constant_ast 12

/* fake types */

#define _is_bool 13

#define is_callable 14

/* internal types */

#define is_indirect 15

#define is_ptr 17

可以把它理解為子型別,上面提到了變數的型別,這個是針對不同型別的子型別或標記,type_flags一共有以下6種。

/* zval.u1.v.type_flags */

#define is_type_constant (1<<0) /* 常量 */

#define is_type_immutable (1<<1) /* 不可變的型別 */

#define is_type_refcounted (1<<2) /* 需要引用計數的型別 */

#define is_type_collectable (1<<3) /* 可能包含迴圈引用的型別 */

#define is_type_copyable (1<<4) /* 可被複製的型別 */

#define is_type_symboltable (1<<5) /* 符號表型別 */

常量型別的標記,對應的屬性為:

/* zval.u1.v.const_flags */

#define is_constant_unqualified 0x010

#define is_lexical_var 0x020

#define is_lexical_ref 0x040

#define is_constant_class 0x080 /* __class__ in trait */

#define is_constant_in_namespace 0x100 /* used only in opline->extended_value */

type_info與結構體v共用記憶體,修改type_info等同於修改結構體v的值,所以type_info是v中四個char的組合。

本來使用u1和zend_value就可以表示變數的,沒有必要定義u2,但是我們來看一下,如果沒有u2,在記憶體對齊的情況下zval記憶體大小為16個位元組,當聯合了u2後依然是占用16個位元組。既然有或沒有占用記憶體大小相同,不如用它來記錄一些附屬資訊。下面我們來看下u2都儲存了哪些內容。

用來解決雜湊衝突問題,記錄衝突的下乙個元素位置。

執行時快取,在執行函式時回去快取中查詢,若快取中沒有則到全域性function表中查詢。

檔案執行的行號,應用在ast節點上。zend引擎在詞法和語法解析時會把當前執行的檔案行號記錄下來,記錄在zend_ast中的lineno中。

函式呼叫時傳入函式的引數個數。

用於遍歷陣列時記錄當前遍歷的位置,比如每次執行foreach時fe_pos都會加一,當再次呼叫foreach進行遍歷時,fe_post會進行重置。

這個與fe_pos類似,只不過它是針對物件的。物件的屬性也是hashtable,傳入的引數是物件時,會獲取物件的屬性,所以遍歷物件就是在變數物件的屬性。

跟廠長學PHP核心7(六) 變數之zval

記得網上流傳甚廣的段子 php是世界上最好的語言 暫且不去討論是否言過其實,但至少php確實有獨特優勢的,比如它的弱型別,即只需要 符號即可宣告變數,使得php入手門檻極低,成為大家所青睞的web服務端語言。那麼它的變數是如何實現的呢?我們今天就來學習一下php的基本變數。php的變數儲存在zval...

跟廠長學PHP核心7(六) 變數之zval

記得網上流傳甚廣的段子 php是世界上最好的語言 暫且不去討論是否言過其實,但至少php確實有獨特優勢的,比如它的弱型別,即只需要 符號即可宣告變數,使得php入手門檻極低,成為大家所青睞的web服務端語言。那麼它的變數是如何實現的呢?我們今天就來學習一下php的基本變數。php的變數儲存在zval...

跟廠長學PHP7核心(一) 發展史

1994年,一位名叫rasmus lerdorf的兄台為了在網上展示自己的履歷和網頁流量的統計,用perl開發了一套指令碼,後來因與日俱增的需求無法得到滿足,lerdorf便使用c語言進行了重寫,重寫後的程式支援資料庫的訪問,以及web應用程式的簡單開發,備受好評,隨後便以personal home...