# 介紹
PHP是弱類型語言,在PHP代碼中聲明或使用變量的時候,并不需要強制指明其數據類型.在PHP內核中定義的變量類型:
```c
/* Zend/zend_type.h : line 304 */
#define IS_UNDEF 0
#define IS_NULL 1
...
#define IS_PTR 17
#define _IS_ERROR 20
```
## zval
PHP底層設計了一個zval(“Zend value”的縮寫)的數據結構,可以用來表示任意類型的PHP值,因此,它可能是所有PHP中最重要的結構.本節介紹zvals及其使用背后的基本概念.
```c
/* Zend/zend_type.h */
typedef union _zend_value {
...
} zend_value;
struct _zval_struct {
...
};
```
雖然看起來變得好大, 但其實你仔細看, 全部都是聯合體, 這個新的zval在64位環境下,現在只需要16個字節(2個指針size), 它主要分為倆個部分, value和擴充字段, 而擴充字段又分為u1和u2倆個部分, 其中u1是type info, u2是各種輔助字.
其中value部分, 是一個size_t大小(一個指針大小), 可以保存一個指針, 或者一個long, 或者一個double.
而type info部分則保存了這個zval的類型. 擴充輔助字段則會在多個其他地方使用, 比如next, 就用在取代Hashtable中原來的拉鏈指針, 這部分會在以后介紹HashTable的時候再來詳解.

從PHP7開始, 對于在zval的value字段中能保存下的值, 就不再對他們進行引用計數了, 而是在拷貝的時候直接賦值, 這樣就省掉了大量的引用計數相關的操作, 這部分類型有:
IS_LONG
IS_DOUBLE
當然對于那種根本沒有值, 只有類型的類型, 也不需要引用計數了:
IS_NULL
IS_FALSE
IS_TRUE
而對于復雜類型, 一個size_t保存不下的, 那么我們就用value來保存一個指針, 這個指針指向這個具體的值, 引用計數也隨之作用于這個值上, 而不在是作用于zval上了.
所以, 在PHP7開始, 我們移除了MAKE_STD_ZVAL/ALLOC_ZVAL宏, 不再支持存堆內存上申請zval. 函數內部使用的zval要么來自外面輸入, 要么使用在棧上分配的臨時zval.
### 操作zval
對zval的操作定義了很多宏,這維持了一個抽象層次,使意圖更清晰,將來的PHP版本zval的內部改變了還可以兼容.
```c
//不正確
zval zv = ...
if (zv_ptr->type == IS_LONG) {
php_printf("此變量為長整形,值為 %ld\n", zv->value.lval);
} else ...
//推薦使用宏操作,
zval zv = ...
if (Z_TYPE zv ) == IS_LONG ) {
php_printf (“此變量為長整形,值為 %ld \ n ” , Z_LVAL_P zv ));
} else ...
//宏有_P后綴表示操作的是zval指針
Z_TYPE(zv); // 獲取zval類型 = zv.type
Z_TYPE_P(zv_ptr); // 獲取指針指向的zval類型= zv_ptr->type
```
#### zval.value 相關宏
```code
No. 名稱 變量名 含義 訪問宏
1 zend_long lval 整數(integer) Z_LVAL(zval)
2 double dval 浮動小數點數(float/double) Z_DVAL(zval)
3 zend_refcounted *counted 引用計數 Z_COUNTED(zval)
4 zend_string *str 字符串 Z_STR(zval)
5 zend_array *arr 數組 Z_ARR(zval)
6 zend_object *obj 對象 Z_OBJ(zval)
7 zend_resource *res 資源 Z_RES(zval)
8 zend_reference *ref 引用 Z_REF(zval)
9 zend_ast_ref *ast 抽象構文木 Z_AST(zval)
10 zval *zv zval指針 Z_INDIRECT(zval)
11 void *ptr 任意類型指針 Z_PTR(zval)
12 zend_class_entry *ce 類 Z_CE(zval)
13 zend_function *func 函數 Z_FUNC(zval)
```
#### zval 相關宏
```code
ZVAL_UNDEF(z): 表示zval被銷毀
ZVAL_NULL(z): 設置為NULL
ZVAL_FALSE(z): 設置為false
ZVAL_TRUE(z): 設置為true
ZVAL_BOOL(z, b): 設置為布爾型,b為IS_TRUE、IS_FALSE,與上面兩個等價
ZVAL_LONG(z, l): 設置為整形,l類型為zend_long,如:zval z; ZVAL_LONG(&z, 88);
ZVAL_DOUBLE(z, d): 設置為浮點型,d類型為double
ZVAL_STR(z, s): 設置字符串,將z的value設置為s,s類型為zend_string*,不會增加s的refcount,支持interned strings
ZVAL_NEW_STR(z, s): 同ZVAL_STR(z, s),s為普通字符串,不支持interned strings
ZVAL_STR_COPY(z, s): 將s拷貝到z的value,s類型為zend_string*,同ZVAL_STR(z, s),這里會增加s的refcount
ZVAL_ARR(z, a): 設置為數組,a類型為zend_array*
ZVAL_NEW_ARR(z): 新分配一個數組,主動分配一個zend_array
ZVAL_NEW_PERSISTENT_ARR(z): 創建持久化數組,通過malloc分配,需要手動釋放
ZVAL_OBJ(z, o): 設置為對象,o類型為zend_object*
ZVAL_RES(z, r): 設置為資源,r類型為zend_resource*
ZVAL_NEW_RES(z, h, p, t): 新創建一個資源,h為資源handle,t為type,p為資源ptr指向結構
ZVAL_REF(z, r): 設置為引用,r類型為zend_reference*
ZVAL_NEW_EMPTY_REF(z): 新創建一個空引用,沒有設置具體引用的value
ZVAL_NEW_REF(z, r): 新創建一個引用,r為引用的值,類型為zval*
```
### 在代碼中查看zval
## 參考資料:
https://github.com/pangudashu/php7-internal/blob/master/7/var.md
https://net-newbie.com/phpext/7-zval.html
https://github.com/laruence/php7-internal/blob/master/zval.md