# 2.2 變量的值
PHP內核提供了三個基礎宏來方便我們對變量的值進行操作,這幾個宏同樣以Z_開頭,并且P結尾和PP結尾的同上一節中的宏一樣,
分別代表這參數是指針還是指針的指針。
此外,為了進一步方便我們的工作,內核中針對具體的數據類型分別定義了相應的宏。
如針對IS_BOOL型的BVAL組合(Z_BVAL、Z_BVAL_P、Z_BVAL_PP)和針對IS_DOUBLE的DVAL組合(Z_DVAL、ZDVAL_P、ZDVAL_PP)等等。
我們通過下面這個例子來應用一下這幾個宏:
````c
void display_value(zval zv,zval *zv_p,zval **zv_pp)
{
if( Z_TYPE(zv) == IS_NULL )
{
php_printf("類型是 IS_NULL!\n");
}
if( Z_TYPE_P(zv_p) == IS_LONG )
{
php_printf("類型是 IS_LONG,值是:%ld" , Z_LVAL_P(zv_p));
}
if(Z_TYPE_PP(zv_pp) == IS_DOUBLE )
{
php_printf("類型是 IS_DOUBLE,值是:%f" , Z_DVAL_PP(zv_pp) );
}
}
````
string型變量比較特殊,因為內核在保存String型變量時,不僅保存了字符串的值,還保存了它的長度,
所以它有對應的兩種宏組合STRVAL和STRLEN,即:Z_STRVAL、Z_STRVAL_P、Z_STRVAL_PP與Z_STRLEN、Z_STRLEN_P、Z_STRLEN_PP。
前一種宏返回的是char *型,即字符串的地址;后一種返回的是int型,即字符串的長度。
````c
void display_string(zval *zstr)
{
if (Z_TYPE_P(zstr) != IS_STRING) {
php_printf("這個變量不是字符串!\n");
return;
}
PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));
//這里用了PHPWRITE宏,只要知道它是從Z_STRVAL_P(zstr)地址開始,輸出Z_STRLEN_P(zstr)長度的字符就可以了。
}
````
Array型變量的值其實是存儲在C語言實現的HashTable中的,
我們可以用ARRVAL組合宏(Z_ARRVAL, Z_ARRVAL_P, Z_ARRVAL_PP)這三個宏來訪問數組的值。
如果你看舊版本php的源碼或者部分pecl擴展的源碼,可能會發現一個HASH_OF()宏,這個宏等價于Z_ARRVAL_P()。
但不推薦在新代碼中再使用了。
對象是一個復雜的結構體(zend_object_value結構體),不僅存儲屬性的定義、屬性的值,還存儲著訪問權限、方法等信息。
內核中定義了以下組合宏讓我們方便的操作對象:
OBJ_HANDLE:返回handle標識符,
OBJ_HT:handle表,
OBJCE:類定義,
OBJPROP:HashTable的屬性,
OBJ_HANDLER:在OBJ_HT中操作一個特殊的handler方法。
現在不用擔心這些宏對象的意思,后續有專門的章節介紹object。
資源型變量的值其實就是一個整數,可以用RESVAL組合宏來訪問它,我們把它的值傳給zend_fetch_resource函數,便可以得到這個資源的操作句柄,如mysql的鏈接句柄等。有關資源的內容我們將在第9章展開敘述。
有關值操作的宏都定義在./Zend/zend_operators.h文件里:
````c
//操作整數的
#define Z_LVAL(zval) (zval).value.lval
#define Z_LVAL_P(zval_p) Z_LVAL(*zval_p)
#define Z_LVAL_PP(zval_pp) Z_LVAL(**zval_pp)
//操作IS_BOOL布爾型的
#define Z_BVAL(zval) ((zend_bool)(zval).value.lval)
#define Z_BVAL_P(zval_p) Z_BVAL(*zval_p)
#define Z_BVAL_PP(zval_pp) Z_BVAL(**zval_pp)
//操作浮點數的
#define Z_DVAL(zval) (zval).value.dval
#define Z_DVAL_P(zval_p) Z_DVAL(*zval_p)
#define Z_DVAL_PP(zval_pp) Z_DVAL(**zval_pp)
//操作字符串的值和長度的
#define Z_STRVAL(zval) (zval).value.str.val
#define Z_STRVAL_P(zval_p) Z_STRVAL(*zval_p)
#define Z_STRVAL_PP(zval_pp) Z_STRVAL(**zval_pp)
#define Z_STRLEN(zval) (zval).value.str.len
#define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
#define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)
#define Z_ARRVAL(zval) (zval).value.ht
#define Z_ARRVAL_P(zval_p) Z_ARRVAL(*zval_p)
#define Z_ARRVAL_PP(zval_pp) Z_ARRVAL(**zval_pp)
//操作對象的
#define Z_OBJVAL(zval) (zval).value.obj
#define Z_OBJVAL_P(zval_p) Z_OBJVAL(*zval_p)
#define Z_OBJVAL_PP(zval_pp) Z_OBJVAL(**zval_pp)
#define Z_OBJ_HANDLE(zval) Z_OBJVAL(zval).handle
#define Z_OBJ_HANDLE_P(zval_p) Z_OBJ_HANDLE(*zval_p)
#define Z_OBJ_HANDLE_PP(zval_p) Z_OBJ_HANDLE(**zval_p)
#define Z_OBJ_HT(zval) Z_OBJVAL(zval).handlers
#define Z_OBJ_HT_P(zval_p) Z_OBJ_HT(*zval_p)
#define Z_OBJ_HT_PP(zval_p) Z_OBJ_HT(**zval_p)
#define Z_OBJCE(zval) zend_get_class_entry(&(zval) TSRMLS_CC)
#define Z_OBJCE_P(zval_p) Z_OBJCE(*zval_p)
#define Z_OBJCE_PP(zval_pp) Z_OBJCE(**zval_pp)
#define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(&(zval) TSRMLS_CC)
#define Z_OBJPROP_P(zval_p) Z_OBJPROP(*zval_p)
#define Z_OBJPROP_PP(zval_pp) Z_OBJPROP(**zval_pp)
#define Z_OBJ_HANDLER(zval, hf) Z_OBJ_HT((zval))->hf
#define Z_OBJ_HANDLER_P(zval_p, h) Z_OBJ_HANDLER(*zval_p, h)
#define Z_OBJ_HANDLER_PP(zval_p, h) Z_OBJ_HANDLER(**zval_p, h)
#define Z_OBJDEBUG(zval,is_tmp) (Z_OBJ_HANDLER((zval),get_debug_info)? \
Z_OBJ_HANDLER((zval),get_debug_info)(&(zval),&is_tmp TSRMLS_CC): \
(is_tmp=0,Z_OBJ_HANDLER((zval),get_properties)?Z_OBJPROP(zval):NULL))
#define Z_OBJDEBUG_P(zval_p,is_tmp) Z_OBJDEBUG(*zval_p,is_tmp)
#define Z_OBJDEBUG_PP(zval_pp,is_tmp) Z_OBJDEBUG(**zval_pp,is_tmp)
//操作資源的
#define Z_RESVAL(zval) (zval).value.lval
#define Z_RESVAL_P(zval_p) Z_RESVAL(*zval_p)
#define Z_RESVAL_PP(zval_pp) Z_RESVAL(**zval_pp)
````
## links
* [目錄](<preface.md>)
* 2.1 [變量的類型](<2.1.md>)
* 2.3 [創建PHP變量](<2.3.md>)
- about
- 開始閱讀
- 目錄
- 1 PHP的生命周期
- 1.讓我們從SAPI開始
- 2.PHP的啟動與終止
- 3.PHP的生命周期
- 4.線程安全
- 5.小結
- 2 PHP變量在內核中的實現
- 1. 變量的類型
- 2. 變量的值
- 3. 創建PHP變量
- 4. 變量的存儲方式
- 5. 變量的檢索
- 6. 類型轉換
- 7. 小結
- 3 內存管理
- 1. 內存管理
- 2. 引用計數
- 3. 總結
- 4 動手編譯PHP
- 1. 編譯前的準備
- 2. PHP編譯前的config配置
- 3. Unix/Linux平臺下的編譯
- 4. 在Win32平臺上編譯PHP
- 5. 小結
- 5 Your First Extension
- 1. 一個擴展的基本結構
- 2. 編譯我們的擴展
- 3. 靜態編譯
- 4. 編寫函數
- 5. 小結
- 6 函數返回值
- 1. 一個特殊的參數:return_value
- 2. 引用與函數的執行結果
- 3. 小結
- 7 函數的參數
- 1. zend_parse_parameters
- 2. Arg Info 與類型綁定
- 3. 小結
- 8 使用HashTable與{數組}
- 1. 數組(C中的)與鏈表
- 2. 操作HashTable的API
- 3. 在內核中操作PHP語言中數組
- 4. 小結
- 9 PHP中的資源類型
- 1. 復合類型的數據——{資源}
- 2. Persistent Resources
- 3. {資源}自有的引用計數
- 4. 小結
- 10 PHP中的面向對象(一)
- 1. zend_class_entry
- 2. 定義一個類
- 3. 定義一個接口
- 4. 類的繼承與接口的實現
- 5. 小結
- 11 PHP中的面向對象(二)
- 1. 生成對象的實例與調用方法
- 2. 讀寫對象的屬性
- 3. 小結
- 12 啟動與終止的那點事
- 2. 小結
- 1. 關于生命周期
- 2. MINFO與phpinfo
- 3. 常量
- 4. PHP擴展中的全局變量
- 5. PHP語言中的超級全局變量
- 6. 小結
- 13 INI設置
- 1. 聲明和訪問ini設置
- 2. 小結
- 2. 小結
- 14 流式訪問
- 1. 概覽
- 2. 打開流
- 3. 訪問流
- 4. 靜態資源操作
- 5. 小結
- 15 流的實現
- 1. php流的表象之下
- 2. 包裝器操作
- 3. 實現一個包裝器
- 4. 操縱
- 5. 檢查
- 6. 小結
- 16 有趣的流
- 1. 上下文
- 2. 過濾器
- 3. 小結
- 17 配置和鏈接
- 1. autoconf
- 2. 庫的查找
- 3. 強制模塊依賴
- 4. Windows方言
- 5. 小結
- 18 擴展生成
- 1. ext_skel
- 2. PECL_Gen
- 3. 小結
- 19 設置宿主環境
- 1. 嵌入式SAPI
- 2. 構建并編譯一個宿主應用
- 3. 通過嵌入包裝重新創建cli
- 4. 老技術新用
- 5. 小結
- 20 高級嵌入式
- 1. 回調到php中
- 2. 錯誤處理
- 3. 初始化php
- 4. 覆寫INI_SYSTEM和INI_PERDIR選項
- 5. 捕獲輸出
- 6. 同時擴展和嵌入
- 7. 小結
- 約定