|
其中refcount__gc和is_ref__gc表示變數是否為一個引用。 type欄位標識變數的類型,type的值可以是:IS_NULL,IS_BOOL,IS_LONG,IS_FLOAT,IS_STRING,IS_ARRAY,IS_OBJECT,IS_RESOURCE。 PHP根據type的類型,來選擇如何儲存到zvalue_value。 zvalue_value能夠實現變數弱型態的核心,定義如下:
[php] view
plaincopy
- typedef union _zvalue_value { long lval; double dval; /* doub le value char *val; int len; will always be set for strings */ } str; /* string (always has length) */ HashTable *ht; /* an array */ zend_object_value obj; /* stores an object store handle, and handlers */ } zvalue_value; 布林型,zval.type
布爾型,zval.type如果是字串,zval.type=IS_STRING,會讀取zval.value.str,這是一個結構體,儲存了字串指標和長度。 C語言中,用"
plaincopy
typedefstruct_zend_rsrc_list_entry { void *ptr; int
其中,ptr是一個指向資源的最終實現的指針,例如一個文件句柄,或者一個資料庫連接結構。 type是一個類型標記,用來區分不同的資源類型。 refcount用於資源的參考計數。 核心中,資源類型是透過函數ZEND_FETCH_RESOURCE取得的。
[php] view
plaincopy
-
ZEND_FETCH_RESOURCE(con, type, zval *, default, resource_name, resource_type); 按照現在我們對PHP語言的了解,變數的類型依賴zval.type欄位指示,變數的內容依照zval.type儲存到zval.value。當PHP中需要變數的時候,只需要兩個步驟:把zval.value的值或指標改變,再改變zval.type的類型。不過對於PHP的一些高階變數Array/Object/Resource,變數轉換要進行更多操作。
變數轉換原理分為3種:5.1 標準型別相互轉換
比較簡單,依照上述的步驟轉換即可。
5.2 標準型別與資源型別轉換
資源型別可以理解為是int,較方便轉換標準型別。轉換後資源會被close或回收。
[php] view
plaincopy
php $var
- = fopen()caaa/ // 資源 # 1 $var = (int) $var; var_dump($var); // 輸出1 ?> 5.3 305.3 類型標準與複合型態轉換型/電腦型態轉換型/電腦型標準差35.3 305.300002 型式轉換類型/電腦類型。傳回元素個數;轉換bool回傳Array中是否有元素;轉換成string回傳'Array',並拋出warning。 詳情取決於經驗,請閱讀PHP手冊: http://php.net/manual/en/language.types.type-juggling.php 5.4 複雜型別相互轉換array和object可以互轉。如果其它任何類型的值被轉換成對象,將會建立一個內建類別stdClass的實例。
在我們寫PHP擴充的時候,PHP核心提供了一組函數用於型別轉換:
void convert_to_long(zval* p void convert_to_long_base(zval* pzval, int base)
void convert_to_null(zval* pzval) void convert_to_leanf(Fvalzpvalp(Fpvalzp
void convert_to_object( zval* pzval)
| void convert_object_to_type(zval* pzval, convert_func_t converter)
|
PHP核提供的一組蛋白質來更容易的存取valval, |
| 內核存取zval容器的API
| 宏
存取變數 |
| Z_DVAL(zval) (zval).value.dval |
Z_STRVAL(zval)
(zval).value.str.val). len
| Z_ARRVAL(zval)
(zval). value.ht |
|
Z_TYPE(zval) | LVAL_P(zval) |
( *zval).value.lval
| Z_DVAL_P(zval) |
(*zval).value.dval
. |
|
Z_STRLEN_P(zval_p)
(*zval).value.str.len |
| 毫
Z_OBJ_HT_P(zval_p)
| (*zval).value.obj.handlers |
Z_LVAL_PP(zval_pp)
| (**zval).value.lval_pp) |
(**zval).value.lval_pp)
(**zval ).value.dval |
|
Z_STRVAL_PP(zval_pp)
(**zval).value.str.val |
len |
Z_ARRVAL_PP(zval_pp) |
(**zval). value.ht |
6. 變數的符號表與作用域
PHP的變數符號表與zval值的映射,是透過HashTable(哈希表,又叫做散列表,下面簡稱HT),HashTable在ZE中廣泛使用,包括常數、變數、函數等語言特性都是HT來組織,在PHP的陣列類型也是透過HashTable來實現。 舉例:
[php] view
plaincopy
-
php $var = 'Hello World';符號表中,代表$var的類型和值的zval結構儲存在哈希表中。核心透過變數符號表與zval位址的哈希映射,來實現PHP變數的存取。 為什麼要提作用域呢?因為函數內部變數保護。依照作用域PHP的變數分為全域變數和局部變量,每個作用域PHP都會維護一個符號表的HashTable。當在PHP中建立一個函數或類別的時候,ZE會建立一個新的符號表,表示函數或類別中的變數是局部變量,這樣就實現了局部變數的保護--外部無法存取函數內部的變數。當建立一個PHP變數的時候,ZE會分配一個zval,並設定對應type和初始值,把這個變數加入目前作用域的符號表,這樣使用者才能使用這個變數。
核心中使用ZEND_SET_SYMBOL來設定變數:
[php] view
plaincopy
ZEND_SET_SYMBOL( EG(active_symbol_table), "foo"
- , foo); [php] view
plaincopy
Zend/zend_globals.h
struct _zend_ HashTable symbol_table;//全域變項的符號表 HashTable *active_symbol_table;//局部變項的符號表 ///略為@); //略為略);
- 在寫PHP擴充時候,可以透過EG宏來存取PHP的變數符號表。 EG(symbol_table)存取全域作用域的變數符號表,EG(active_symbol_table)存取目前作用域的變數符號表,局部變數儲存的是指針,在對HashTable進行操作的時候傳遞給對應函數。 為了更好的理解變數的雜湊表與作用域,舉個簡單的例子:
- [php] view
plaincopy
php $temp = 'global';
$temp = - 'active'; } test (); var_dump($temp); ?> 建立函數外的變數$temp,就會將這個它加入全域符號表,同時在全域符號表的HashTable中建立函數外的變數$temp,會把這個它加入全域符號表,同時在全域符號表的HashTable中類型的zval,值為'global'。建立函數test內部變數$temp,會把它加入屬於函數test的符號表,分配字元型zval,值為’active' 。 7. PHP擴充中變數操作建立PHP變數我們可以在擴充中呼叫函數MAKE_STD_ZVAL(pzv)來建立一個PHP可呼叫的變量,MAKE_STD_ZVAL應用到的宏有:] view
plaincopy
- #define MAKE_STD_ZVAL(zv) ZEND_FAST_ALLOC(z, zval, ZVAL_CACHE_LIST) ZEND) (type * ) emalloc(sizeof(type)) #define INIT_PZVAL(z) 0;
MAKE_STD_ZVAL(foo)展開後得到:
[php] view
plaincopy
- (foo) = (zval *) emalloc(sizeof(zval)); (foo)->refcount__gc =__1;
可以看出, MAKE_STD_ZVAL做了三件事:分配記憶體、初始化zval結構中的refcount、is_ref。 核心中提供一些巨集來簡化我們的操作,可以只用一步便設定好zval的類型和值。
API Macros for Accessing zval | 宏
實作方法 |
|
ZVAL_BOOL(pvz) |
| Z_TYPE_P(pzv) = IS_BOOL; Z_BVAL_P(pzv) = b ? 1 : 0;
| ZVAL_TRUE(pvz) FALSE(pvz)
|
ZVAL_BOOL(pzv, 0);
| ZVAL_LONG(pvz, l)(l 是值) |
Z_TYPE_P(pzv) = IS_LONGZ_LLet z, d )
Z_TYPE_P(pzv) = IS_DOUBLE;Z_LVAL_P(pzv) = d; |
|
ZVAL_STRINGL(pvz, stret, len,d,nid; pzv) = len; if (dup) { {Z_STRVAL_P(pzv) =estrndup(str, len + 1);} }else { pvz, str, len) |
| ZVAL_STRINGL(pzv, str,strlen(str), dup);
ZVAL_RESOURCE(pvz,res) |
| ZVAL_RESOURCE(pvz, res)
ZVAL_STRINGL(pzv,str,len,dup)中的dup參數
先闡述一下ZVAL_STRINGL(pzv,str,len,dup); str和len兩個參數很好理解,因為我們知道內核中保存了字串的位址和它的長度,後面的dup的意思其實很簡單,它指明了該字串是否需要被複製。值為 1 將先申請一塊新記憶體並賦值該字串,然後把新記憶體的位址複製給pzv,為 0 時則是直接把str的位址賦值給zval。
ZVAL_STRINGL與ZVAL_STRING的區別
如果你想在某一位置截取該字串或已經知道了這個字串的長度,那麼可以使用宏ZVAL_STRINGL(zval, string, length, duplicate) ,它顯式的指定字串長度,而不是使用strlen()。這個宏該字串長度作為參數。但它是二進位安全的,而且速度也比ZVAL_STRING快,因為少了個strlen。 ZVAL_RESOURCE約等於ZVAL_LONG
在章節4中我們說過,PHP中的資源類型的值是一個整數,所以ZVAL_RESOURCE和ZVAL_LONG的工作差不多,只不過它會把zval的類型設為 IS_RESOURCE。
8. 總結
PHP的弱類型是透過ZE的zval容器轉換完成,透過哈希表來儲存變數名稱和zval數據,在運作效率方面有一定犧牲。另外因為變數類型的隱性轉換,在開發過程中對變數類型檢測力度不夠,可能會導致問題出現。
不過PHP的弱型別、陣列、記憶體託管、擴充等語言特性,非常適合Web開發場景,開發效率很高,能夠加快產品迭代週期。在海量服務中,通常瓶頸存在於資料存取層,而不是語言本身。在實際使用PHP不僅擔任邏輯層和展現層的任務,我們甚至用PHP開發的UDPServer/TCPServer作為資料和cache的中間層。
以上就介紹了王帥:深入PHP核心(一)-弱型變項原理探究,包含了面向的內容,希望對PHP教學有興趣的朋友有幫助。
|
|
|