PHP 是用 C 實作的解釋器,所有變數在底層都是 zval 結構體(PHP 7 稱為 zval,PHP 5 為 zval* 指標)。
struct _zval_struct {
zend_value value; // 聯合體:實際資料
zend_refcount_h refcount; // 參考計數
zend_uchar type; // 型別標記
zend_uchar is_ref; // 是否為參考
};
typedef union _zend_value {
zend_long lval; // 整數、布林
double dval; // 浮點數
zend_string *str; // 字串(指標)
zend_array *arr; // 陣列
zend_object *obj; // 物件
zend_resource *res; // 資源
zend_reference *ref; // 參考包裝
} zend_value;
弱型別本質:type 標記當前活躍的聯合體欄位,同一塊記憶體依 type 解讀為不同型別。變數賦值新值時,只需修改 type 與對應的聯合體欄位。
| 常數 | 對應聯合體欄位 | PHP 型別 |
|---|---|---|
| IS_LONG | lval | int / bool |
| IS_DOUBLE | dval | float |
| IS_STRING | str | string |
| IS_ARRAY | arr | array |
| IS_OBJECT | obj | object |
| IS_RESOURCE | res | resource |
| IS_REFERENCE | ref | 參考包裝 |
| IS_NULL | — | null |
struct _zend_string {
zend_refcount_h gc_refcount; // GC 參考計數
zend_ulong h; // 預計算雜湊值
size_t len; // 長度(不含結尾 \0)
char val[1]; // 柔性陣列成員,實際字元資料
};
strlen() 直接讀取 len,O(1) 極快。zend_string*,整數鍵直接儲存。<?php
$a = "hello"; // 建立 zval(type=IS_STRING, refcount=1)
$b = $a; // 指向同一 zval,refcount=2
$a = "world"; // 寫入前發現 refcount>1 → 複製新 zval,舊 zval refcount--,新 zval refcount=1
// 現在 $a、$b 指向不同 zval,互不影響
?>
is_ref=1,寫入不複製,直接修改共用 zval。lval 儲存資源 ID(整數索引)。EG(regular_list)。var_export 回傳 NULL。<?php
$a = 1;
debug_zval_dump($a); // long(1) refcount(2) ← 參數傳遞增加 1
$b = $a;
debug_zval_dump($a); // long(1) refcount(3)
$a = 2;
debug_zval_dump($a); // long(2) refcount(2) ← 分離後新 zval
debug_zval_dump($b); // long(1) refcount(2)
?>
PHP 變數底層以 zval 統一表示,type 實現弱型別,refcount + is_ref 實現 Copy-on-Write 與參考語意。理解這機制能寫出高效能、低副作用的代碼,並看懂擴充套件開發文件。