PHP 變數在 C 層面以 zval(Zend Value)結構體儲存,支援弱型別、動態型別轉換。本文剖析 zval 與 zvalue_value 結構,詳細說明 8 種內部資料型別的儲存細節。
typedef struct _zval_struct {
zvalue_value value; // 實際資料值 (union)
zend_uint refcount; // 參考計數 (Copy-on-Write 機制核心)
zend_uchar type; // 資料型別標識 (IS_LONG, IS_STRING 等)
zend_uchar is_ref; // 是否為參考 (& 符號標記)
} zval;
typedef union _zvalue_value {
long lval; // 整數
double dval; // 浮點數
struct { // 字串
char *val;
int len;
} str;
HashTable *ht; // 陣列
zend_object_value obj; // 物件
} zvalue_value;
Union 特性:同一記憶體區塊,依 type 欄位決定解讀方式,節省空間。
| 型別常數 | 用途 | 儲存細節 |
|---|---|---|
| IS_NULL | 未初始化變數、NULL 常數 | 特殊「無資料」狀態,區別於 FALSE/0 |
| IS_BOOL | 布林值 TRUE/FALSE | 儲存於 lval (0/1) |
| IS_LONG | 整數 | 儲存於 lval (平台相關 32/64 位) |
| IS_DOUBLE | 浮點數 | 儲存於 dval (IEEE 754 雙精度),非精確儲存 |
| IS_STRING | 字串 | str.val 指向字元陣列,str.len 明確長度(二進制安全,可含 NULL 字元) |
| IS_ARRAY | 陣列 | 指向 HashTable,每個桶含 key(zval*) 與 value(zval*) |
| IS_OBJECT | 物件 | zend_object_value 含 handle(物件 ID) 與 handlers(方法表指標) |
| IS_RESOURCE | 資源 | 整數 ID,索引全域資源表查找實際指標 |
len + 1 位元組,尾端附加 \0,相容 C 字串函式。len,字串可包含任意位元組(含 \0),不會被截斷。PHP 陣列非 C 語言連續記憶體向量,而是 有序雜湊表:
zval*,可巢狀任意型別。zend_object_value,採用 物件控制代碼,傳參為傳遞 handle(類似參考),支援可見性、介面、魔術方法、靜態成員等。$a = "hello"; // refcount=1, is_ref=0
$b = $a; // refcount=2, 共享同一 zval
$b = "world"; // 觸發 COW:複製 zval,修改新副本,refcount 各自=1
is_ref=1 表示經由 & 明確建立參考($b = &$a),修改任一變數直接影響另一個。