搜尋
PHP內核之zvalApr 19, 2018 am 09:26 AM
phpzval核心

這篇文章主要介紹的內容是關於PHP核心之zval,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

原文地址
作者:Twei 主頁

前言


之前面試的時候面試官問過php中變數是如何實現的,遺憾的是只答道了大概是用結構體實現的。這篇文章是谷歌之後覺得總結 的比較到位的,故轉載進而學習之。

正文


PHP中的資料型別


#相對於C、 C 、 Java等其他程式語言,PHP 是一個弱型別的語言,表示當我們要使用一個變數時,不需要去宣告它的型別。這個特性為我們帶來了許多便利,同時有時也會帶來一些陷阱。那麼,PHP 是真的沒有資料型這個說法嗎?

當然不是。在 PHP 官方文件中將 PHP 中的變數劃分為三類:標量類型、複雜型別和特殊型別。標量類型包括布林型(bool)、整數型(int)、浮點型(float)和字串(string);複雜型別包括陣列(array)和物件(object);特殊型別包括NULL 和資源(resource) 。所以說 PHP 的變數細分的話,有 8 種資料型別。

總所周知,PHP 的底層是用 C 語言實現的。我們的 PHP 腳本會經過 Zend 引擎解析為 C 程式碼再執行。那麼,一個 PHP 的變量,在 C 語言上是怎麼表示的呢?它最終會被解析成什麼樣子呢?

答案就是 zval。不管什麼類型的 PHP 變量,在 PHP 原始碼中統一用一個叫做 zval 的結構表示。 zval 可以看做是 PHP 變數在 C 程式碼中的容器,它儲存了這個變數的值、型別等相關資訊。

那我們就來看看 zval 的基本結構(需要一點 C 語言的基本知識)。

zval的基本結構


在PHP 原始碼中zval 這個結構是一個名叫_zval_struct 的結構體(struct) ,具體定義在原始碼的Zend/zend.h 檔案中,下面是相關程式碼的摘錄:

struct _zval_struct {
    zvalue_value value;       /* value */ 
    zend_uint refcount__gc;   /* value of ref count */
    zend_uchar type;          /* active type */ 
    zend_uchar is_ref__gc;    /* if it is a ref variable */ }; 
typedef struct _zval_struct zval;

也就是說,在PHP 的原始碼中,就用這一個結構體表示PHP 中各種類型的變量,並且還可以實現其他的一些功能,例如垃圾回收(GC:Grabage Collection)。

可以看到它由 4 個欄位構成,分別表示這個變數的某個資訊。

ZVALUE_VALUE VALUE


value 用來表示變數的實際值,具體來說它是一個zvalue_value 的聯合體(union):

typedef union _zvalue_value {    long lval;                  /* long value */
    double dval;                /* double value */
    struct {                    /* string */
        char *val;        int len;
    } str;
    HashTable *ht;              /* hash table value,used for array */
    zend_object_value obj;      /* object */} zvalue_value;

可以看到_zvalue_value 中只有5 個字段,但是PHP 中有8 種資料類型,那麼如何用5 個字段表示8 種類型呢?

這算是 PHP 設計比較巧妙的地方,它透過重複使用欄位達到了減少欄位的目的。例如,在PHP 內部布林型、整數及資源(只要儲存資源的識別碼即可)都是透過lval 欄位儲存的;dval 用於儲存浮點型;str 儲存字串;ht 儲存陣列(注意PHP 中的陣列其實是雜湊表);而obj 儲存物件類型;如果所有欄位全部置為0 或NULL則表示PHP 中的NULL,這樣就達到了用5 個欄位儲存8 種類型的值。

ZEND_UINT REFCOUNT__GC


從它的字尾 gc 可以看到,這個欄位是和垃圾回收相關的。

它實際上是一個計數器,用來保存有多少變數指向該zval。在變數生成時,置為1,也就是 refcount = 1。

對變數進行不同的操作會改變它的值。典型的賦值運算如

b 會使 refcount 加 1,而 unset() 運算會對應的減 1。

透過判斷它的值可以進行垃圾回收。在 PHP5.3 之前,使用引用計數的機制來實作 GC:如果一個 zval 的 refcount 減為 0,那麼 Zend 引擎會認為沒有任何變數指向該 zval,就會釋放該 zval 所佔的記憶體空間。但僅僅使用引用計數機制無法釋放掉循環引用的 zval,這是就會導致記憶體洩漏(Memory Leak)。

在5.3 以前,這個欄位的名字還叫做refcount,5.3 以後,在引入新的垃圾回收演算法來對付循環引用,作者加入了大量的巨集來操作refcount,為了能讓錯誤更快的顯現,所以改名為refcount__gc, 迫使大家都使用巨集來操作refcount。

類似的, 還有第四個欄位 is_ref, 這個值表示了 PHP 中的一個類型是否是引用。
想了解 PHP 的垃圾回收機制,可以參考這篇部落格:PHP的垃圾回收機制
註:變量,也可以稱為符號,symbol。所有的符號都存在符號表(symbol table)中, 不同的作用域使用不同的符號表,關於這一點,這篇博客進行了講解。

ZEND_UCHAR TYPE


这个字段用于表明变量属于 PHP 8 种类型的哪种。在 zend 内部,这些类型对应于下面的宏(代码位置 phpsrc/Zend/zend.h):

#define IS_NULL     0#define IS_LONG     1#define IS_DOUBLE   2#define IS_BOOL     3#define IS_ARRAY    4#define IS_OBJECT   5#define IS_STRING   6#define IS_RESOURCE 7#define IS_CONSTANT 8#define IS_CONSTANT_ARRAY   9#define IS_CALLABLE 10

ZEND_UCHAR IS_REF__GC


这个字段用于标记变量是否是引用变量。对于普通的变量,该值为 0,而对于引用型的变量,该值为 1。这个变量会影响 zval 的共享、分离等。它也和 PHP 的垃圾回收有关。

PHP7中的zval


上述的 zval 结构,随着时间的发展,暴露出许多问题,例如占用空间大(24 字节)、不支持拓展、 对象和引用效率差等,所以在 PHP7 的时候,对 zval 进行了较大的改变,现在它的结构是这样的:

struct _zval_struct {    union {
        zend_long         lval;             /* long value */
        double            dval;             /* double value */
        zend_refcounted  *counted;
        zend_string      *str;
        zend_array       *arr;
        zend_object      *obj;
        zend_resource    *res;
        zend_reference   *ref;
        zend_ast_ref     *ast;
        zval             *zv;        void             *ptr;
        zend_class_entry *ce;
        zend_function    *func;        struct {
            uint32_t w1;
            uint32_t w2;
        } ww;
    } value;    union {        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;    union {
        uint32_t     var_flags;
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
    } u2;
};

虽然看起来变得好大,但其实仔细看,它的字段都是联合体,这个新的 zval 在 64 位环境下,只需要 16 个字节(2 个指针 size)。PHP7 中的 zval,已经变成了一个值指针,它要么保存着原始值,要么保存着指向一个保存原始值的指针。

这部分内容来自鸟哥的GitHub。

总结


  1. zval 是一种 C 语言实现的数据结构,功能是作为 PHP 变量的容器;

  2. 它保存了变量的各种信息(如类型和值),并为其他功能(如垃圾回收)提供支持;

  3. 在不同的 PHP 版本中,它的结构不同。PHP7 的 zval 占 16 个字节,PHP5 的要占 24 个字节。

参考


PHP内核探索之变量(1)变量的容器-Zval
PHP垃圾回收深入理解
深入理解PHP7之zval

相关推荐:

PHP内核之探究内存管理与缓存机制

PHP内核分析-Zend虚拟机详解

以上是PHP內核之zval的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace(" ","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么查找字符串是第几位php怎么查找字符串是第几位Apr 22, 2022 pm 06:48 PM

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
4 週前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版