這篇文章主要介紹的內容是關於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。
总结
zval 是一种 C 语言实现的数据结构,功能是作为 PHP 变量的容器;
它保存了变量的各种信息(如类型和值),并为其他功能(如垃圾回收)提供支持;
在不同的 PHP 版本中,它的结构不同。PHP7 的 zval 占 16 个字节,PHP5 的要占 24 个字节。
参考
PHP内核探索之变量(1)变量的容器-Zval
PHP垃圾回收深入理解
深入理解PHP7之zval
相关推荐:
以上是PHP內核之zval的詳細內容。更多資訊請關注PHP中文網其他相關文章!

PHP在現代Web開發中仍然重要,尤其在內容管理和電子商務平台。 1)PHP擁有豐富的生態系統和強大框架支持,如Laravel和Symfony。 2)性能優化可通過OPcache和Nginx實現。 3)PHP8.0引入JIT編譯器,提升性能。 4)雲原生應用通過Docker和Kubernetes部署,提高靈活性和可擴展性。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

PHP在現代編程中仍然是一個強大且廣泛使用的工具,尤其在web開發領域。 1)PHP易用且與數據庫集成無縫,是許多開發者的首選。 2)它支持動態內容生成和麵向對象編程,適合快速創建和維護網站。 3)PHP的性能可以通過緩存和優化數據庫查詢來提升,其廣泛的社區和豐富生態系統使其在當今技術棧中仍具重要地位。

在PHP中,弱引用是通過WeakReference類實現的,不會阻止垃圾回收器回收對象。弱引用適用於緩存系統和事件監聽器等場景,需注意其不能保證對象存活,且垃圾回收可能延遲。

\_\_invoke方法允許對象像函數一樣被調用。 1.定義\_\_invoke方法使對象可被調用。 2.使用$obj(...)語法時,PHP會執行\_\_invoke方法。 3.適用於日誌記錄和計算器等場景,提高代碼靈活性和可讀性。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SublimeText3漢化版
中文版,非常好用

WebStorm Mac版
好用的JavaScript開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器