PHP是怎么存儲變量的

蝸牛 互聯網技術資訊 2022-05-26 118 0

本篇內容主要講解“PHP是怎么存儲變量的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“PHP是怎么存儲變量的”吧!

PHP 源碼中的 zval

在 PHP 中定義一個變量是不需要聲明類型的,一開始給變量 $a 賦予一個整型值,后面又可以輕而易舉地將其改變為其他類型。那在 PHP 的源碼中是如何來存儲這個變量 $a 的呢?帶著這個疑問我們一起去看一看 PHP 的源碼。

PHP 的源碼是由 C 編寫的,在 PHP 的源碼中使用了一個 zval 的結構體來存儲在 PHP 代碼中創建的變量。我們把 zval 結構體的定義拿出來簡單分析一下。

這是 PHP 在 Github 上的官方倉庫:github.com/php/php-src,本文使用的分支是 PHP-7.4.29。

zval 結構體

在 PHP 的源碼中找到這個文件:php-src/Zend/zend_types.h,可以看到其中 zval 結構體的定義如下,左側是源碼。源碼中使用了 PHP 自己定義的類型 zend_uchar 、uint16_t 、uint32_t 等,這些類型會針對不同平臺和編譯器會轉為該平臺下的 char short int 等。為了便于理解,我將其翻譯為普通類型并展示在了源碼的右側。同時還把其中的宏函數 ZEND_ENDIAN_LOHI_3() 也展開了。

typedef?struct?_zval_struct?zval;
...
???????《源代碼》???????????????????????????????????????????????《翻譯后》
-------------------------------------------------------------------------------------------
struct?_zval_struct?{???????????????????????????????|?struct?_zval_struct?{
????zend_value?value;???????????????????????????????|?????zend_value?value;
????union?{?????????????????????????????????????????|?????union?{
????????struct?{????????????????????????????????????|?????????struct?{
????????????ZEND_ENDIAN_LOHI_3(?????????????????????|?????????????unsigned?char?type;
????????????????zend_uchar?type,????????????????????|?????????????unsigned?char?type_flags;
????????????????zend_uchar?type_flags,??????????????|?????????????union?{
????????????????union?{?????????????????????????????|?????????????????unsigned?short?extra;
????????????????????uint16_t?extra;?????????????????|?????????????}?u;
????????????????}?u?????????????????????????????????|?????????}?v;
????????????)???????????????????????????????????????|?????????unsigned?int?type_info;
????????}?v;????????????????????????????????????????|?????}?u1;
????????uint32_t?type_info;?????????????????????????|?????union?{
????}?u1;???????????????????????????????????????????|?????????unsigned?int?next;
????union?{?????????????????????????????????????????|?????????unsigned?int?cache_slot;
????????uint32_t?next;??????????????????????????????|?????????unsigned?int?opline_num;
????????uint32_t?cache_slot;????????????????????????|?????????unsigned?int?lineno;
????????uint32_t?opline_num;????????????????????????|?????????unsigned?int?num_args;
????????uint32_t?lineno;????????????????????????????|?????????unsigned?int?fe_pos;
????????uint32_t?num_args;??????????????????????????|?????????unsigned?int?fe_iter_idx;
????????uint32_t?fe_pos;????????????????????????????|?????????unsigned?int?access_flags;
????????uint32_t?fe_iter_idx;???????????????????????|?????????unsigned?int?property_guard;
????????uint32_t?access_flags;??????????????????????|?????????unsigned?int?constant_flags;
????????uint32_t?property_guard;????????????????????|?????????unsigned?int?extra;
????????uint32_t?constant_flags;????????????????????|?????}?u2;
????????uint32_t?extra;?????????????????????????????|?};
????}?u2;???????????????????????????????????????????|
};??????????????????????????????????????????????????|

在 zval 結構體中,變量的值就存儲在 zend_value 類型的 value 屬性中。并通過 u1.v.type 來記錄這個值是什么類型的,比如 IS_LONG 對應整型,IS_STRING 對應字符串類型。

zend_value 聯合體

zend_value 類型也是在 php-src/Zend/zend_types.h 中定義的,是一個聯合體,下面是 zend_value 聯合體的定義,左側是源碼。同樣在右側我也做了簡單的翻譯,把 zend_long uint32_t 翻譯為普通類型便于查看。

????????????《源代碼》??????????????????????????????????????????????《翻譯后》
------------------------------------------------------------------------------------
typedef?union?_zend_value?{?????????????????????????|?typedef?union?_zend_value?{
????zend_long?????????lval;?/*?long?value?*/????????|?????long??????????????lval;
????double????????????dval;?/*?double?value?*/??????|?????double????????????dval;
????zend_refcounted??*counted;??????????????????????|?????zend_refcounted??*counted;
????zend_string??????*str;??????????????????????????|?????zend_string??????*str;
????zend_array???????*arr;??????????????????????????|?????zend_array???????*arr;
????zend_object??????*obj;??????????????????????????|?????zend_object??????*obj;
????zend_resource????*res;??????????????????????????|?????zend_resource????*res;
????zend_reference???*ref;??????????????????????????|?????zend_reference???*ref;
????zend_ast_ref?????*ast;??????????????????????????|?????zend_ast_ref?????*ast;
????zval?????????????*zv;???????????????????????????|?????zval?????????????*zv;
????void?????????????*ptr;??????????????????????????|?????void?????????????*ptr;
????zend_class_entry?*ce;???????????????????????????|?????zend_class_entry?*ce;
????zend_function????*func;?????????????????????????|?????zend_function????*func;
????struct?{????????????????????????????????????????|?????struct?{
????????uint32_t?w1;????????????????????????????????|?????????unsigned?int?w1;
????????uint32_t?w2;????????????????????????????????|?????????unsigned?int?w2;
????}?ww;???????????????????????????????????????????|?????}?ww;
}?zend_value;???????????????????????????????????????|?}?zend_value;

聯合體的一個特點是其占用的內存是其屬性中最大類型對應的長度。其中的 zend_long 就是 long 類型,可以看到 long 類型的 lval 和 double 類型的 dval 占用的長度都是 8 個字節。里面其他指針類型,也均為 8 個字節。最后面的結構體屬性 ww 是由兩個 int 型構成,長度相加也是 8 個字節。因此此聯合體的長度為 8 個字節。

在我們寫的 PHP 代碼中,整型和浮點型數據的值會直接存放到 lval 和 dval 中。如果是字符串、數組以及其他類型時會開辟一段空間存儲數據,并將其地址存放在 zend_value 中,也就是 zval.value 屬性,如:zval.value.zend_long = 9527、zval.value.zend_string = 字符串地址 、zval.value.zend_array = 數組地址。然后在 zval.u1.v.type 上標記這個 zval.value 是整型、或浮點型、或字符串、或其他類型。

zval.u1.v.type 類型定義也是在 php-src/Zend/zend_types.h 文件中,全部的定義如下:

/*?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_AST?11
/*?internal?types?*/
#define?IS_INDIRECT?????13
#define?IS_PTR??????????14
#define?IS_ALIAS_PTR????15
#define?_IS_ERROR???????15
/*?fake?types?used?only?for?type?hinting?(Z_TYPE(zv)?can?not?use?them)?*/
#define?_IS_BOOL????????16
#define?IS_CALLABLE?????17
#define?IS_ITERABLE?????18
#define?IS_VOID?????????19
#define?_IS_NUMBER??????20

zval 結構體內存占用

接下來我們分析一下 zval 所需占用的內存。

  • value:zend_value 類型 8 個字節。

  • u1:

  • u1.v.type:unsigned char 1 個字節,u1.v.type_flags:unsigned char 1 個字節,u1.v.u:聯合體中只有一個 unsigned short 的 extra 屬性 2 個字節,因此 u1.v 的結構體總共是 4 個字節。

  • u1.type_info:unsigned int 4 個字節。

  • 因此 u1 這個聯合體的長度取最長的屬性的長度:4 個字節。

  • u2:也是一個聯合體,里面都是 int 型的屬性,因此長度是 4 個字節。

  • 所以 zval 總共占用的內存是 8 + 4 + 4 = 16 個字節。

也就是說當我們在寫 PHP 代碼時,如果創建了一個整型的變量,那么實際上它在運行中會占用 16 個字節的內存,內存開銷至少是 C 語言的兩倍。當然這兩倍的開銷也帶來了 PHP 處理變量的靈活性。

到此,相信大家對“PHP是怎么存儲變量的”有了更深的了解,不妨來實際操作一番吧!這里是蝸牛博客網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:niceseo99@gmail.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

評論

日本韩欧美一级A片在线观看