PHP扩展开发指南
1.1?????使用數(shù)組
曾講到,PHP數(shù)組本質(zhì)上就是個HashTable,因此訪問數(shù)組就是對HashTable進(jìn)行操作,Zend為我們提供的一組數(shù)組函數(shù)也只是對HashTable操作進(jìn)行了簡單包裝而已。
來看創(chuàng)建數(shù)組,由于數(shù)組也是存在于zval里的,因此要先用MAKE_STD_ZVAL()宏創(chuàng)建一個zval,之后調(diào)用如下宏將其轉(zhuǎn)化為一個空數(shù)組:
array_init(zval*)接下來是朝數(shù)組中添加元素,這對關(guān)聯(lián)數(shù)組元素和非關(guān)聯(lián)數(shù)組元素要采用不同操作。
?
1.1.1?關(guān)聯(lián)數(shù)組元素
關(guān)聯(lián)數(shù)組采用char*作為key,zval*作為value,可以使用如下宏將已有的zval加入數(shù)組或者更新已有元素:
int add_assoc_zval(zval *arr, char *key, zval *value)需要特別注意的是,Zend不會復(fù)制zval,只會簡單的儲存其指針,并且不關(guān)心任何引用計數(shù),因此不能將其他變量的zval或者是棧上的zval傳給它,只能用MAKE_STD_ZVAL()宏構(gòu)建。
Zend為常用的類型定義了相應(yīng)的API,以簡化我們的操作:
| add_assoc_long(zval *array, char *key, long n); |
| add_assoc_bool(zval *array, char *key, int b); |
| add_assoc_resource(zval *array, char *key, int r); |
| add_assoc_double(zval *array, char *key, double d); |
| add_assoc_string(zval *array, char *key, char *str, int duplicate); |
| add_assoc_stringl(zval *array, char *key, char *str, uint length, int duplicate); |
| add_assoc_null(zval *array, char *key); |
當(dāng)函數(shù)發(fā)現(xiàn)目標(biāo)元素已經(jīng)存在時,會首先遞減其原zval的refcount,然后才插入新zval,這就保證了原zval引用信息的正確性。這種行為是通過HashTable.pDestructor(參見1.2.1)實現(xiàn)的,每次刪除一個元素時,HashTable都將對被刪元素調(diào)用這個函數(shù)指針,而數(shù)組為其HashTable設(shè)置的函數(shù)指針就是用來處理被刪除zval的引用信息。
另外,查看這些函數(shù)的源代碼可以發(fā)現(xiàn)一個有意思的現(xiàn)象,它們沒有直接使用HashTable操作,而是使用變量符號表操作,可見關(guān)聯(lián)數(shù)組和變量符號表就是一種東西。
Zend沒有提供刪除和獲取數(shù)組元素的函數(shù),此類操作只能使用HashTable函數(shù)或者是2.6節(jié)的變量符號表操作。
1.1.2非關(guān)聯(lián)數(shù)組元素
非關(guān)聯(lián)數(shù)組沒有key,使用index作為hash,相應(yīng)函數(shù)和上面關(guān)聯(lián)數(shù)組的十分類似:
| add_index_zval(zval *array, uint idx, zval *value); |
| add_index_long(zval *array, uint idx, long n); |
| add_index_bool(zval *array, uint idx, int b); |
| add_index_resource(zval *array, uint idx, int r); |
| add_index_double(zval *array, uint idx, double d); |
| add_index_string(zval *array, uint idx, char *str, int duplicate); |
| add_index_stringl(zval *array, uint idx, char *str, uint length, int duplicate); |
| add_index_null(zval *array, uint idx); |
如果只是想插入值,而不指定index的話,可以使用如下函數(shù):
| add_next_index_zval(zval *array, zval *value); |
| add_next_index_long(zval *array, long n); |
| add_next_index_bool(zval *array, int b); |
| add_next_index_resource(zval *array, int r); |
| add_next_index_double(zval *array, double d); |
| add_next_index_string(zval *array, char *str, int duplicate); |
| add_next_index_stringl(zval *array, char *str, uint length, int duplicate); |
| add_next_index_null(zval *array); |
1.2??????使用資源
1.2.1? 注冊資源類型
1.1.1節(jié)曾經(jīng)提到,所謂資源就是內(nèi)部數(shù)據(jù)的handle(但是這句話并不全對),使用資源是比較簡單的,首先是注冊一個資源類型:
int zend_register_list_destructors_ex( rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number);第一個參數(shù)是函數(shù)指針,當(dāng)資源不再被使用或者模塊將被卸載時,Zend使用它來銷毀資源,稍候再作介紹;第二個參數(shù)和第一個類似,只是它被用來銷毀持久性資源(*);type_name是資源名稱,用戶可以使用var_dump函數(shù)來讀取;module_number是模塊號,在啟動函數(shù)中可以獲取該值。
注冊過程其實就是將我們傳入的參數(shù)放到一個內(nèi)部數(shù)據(jù)結(jié)構(gòu),然后把這個數(shù)據(jù)結(jié)構(gòu)放入一個沒有使用key的HashTable里,該函數(shù)返回的值,也就是所謂“資源類型id”,其實就是HashTable的index。
1.2.1? 注冊資源
注冊完資源類型后,就可以注冊一個該類型的資源了:
| 1 | ZEND_REGISTER_RESOURCE( |
| 2 | rsrc_result, |
| 3 | rsrc_pointer, |
| 4 | rsrc_type) |
src_pointer是個指針類型,就是你的資源的handle, 通常是指向內(nèi)部數(shù)據(jù)的指針,當(dāng)然也可以是index或者其它標(biāo)志符;rsrc_type是上面獲取的資源類型id;rsrc_result是個已有的zval,注冊完成后,資源的id就被放入該zval,同時其type也被設(shè)為IS_RESOURCE,通常是傳入return_value,以將資源返回給用戶。
在內(nèi)部,Zend使用如下數(shù)據(jù)結(jié)構(gòu)表示一個資源:
| 1 | typedef struct _zend_rsrc_list_entry { |
| 2 | ????void *ptr; |
| 3 | ????int type; |
| 4 | ????int refcount; |
| 5 | } zend_rsrc_list_entry; |
ptr和type就是我們在上面?zhèn)魅氲膮?shù);refcount是引用計數(shù),由Zend維護(hù),當(dāng)引用減到0時,Zend會銷毀該資源。不出所料的是,這個數(shù)據(jù)結(jié)構(gòu)也被組織在一個HashTable里,并且沒有使用key,僅僅使用index——這就是zval里存放的東西?,F(xiàn)在資源的整個脈絡(luò)已經(jīng)清晰:通過zval可以獲得資源id,通過資源id可以獲得資源handle和資源類型id,通過資源類型id可以獲得資源的銷毀函數(shù)。 現(xiàn)在講一下銷毀函數(shù):
| 1 | typedef void (*rsrc_dtor_func_t)( |
| 2 | zend_rsrc_list_entry *rsrc |
| 3 | TSRMLS_DC); |
rsrc是需要被銷毀的資源,我們在函數(shù)的實現(xiàn)中可以通過它獲得資源的handle,并且加以處理,比如釋放內(nèi)存塊、關(guān)閉數(shù)據(jù)庫連接或是關(guān)閉文件描述符等。
1.2.3 ?獲取資源
當(dāng)創(chuàng)建了資源后,用戶通常都要調(diào)用創(chuàng)建者提供的函數(shù)來操作資源,此時我們需要從用戶傳入的zval中取出資源:
| 1 | ZEND_FETCH_RESOURCE( |
| 2 | rsrc,? rsrc_type, |
| 3 | passed_id, default_id, |
| 4 | resource_type_name, resource_type) |
首個參數(shù)用于接收handle值,第二個參數(shù)是handle值的類型,這個函數(shù)會擴(kuò)展成“rsrc = (rsrc_type) zend_fetch_resource(…)”,因此應(yīng)該保證rsrc是rsrc_type類型的;passed_id是用戶傳入的zval,這里使用zval**類型,函數(shù)從中取得資源id;default_id用來直接指定資源id,如果該值不是-1,則使用它,并且忽略passed_id,所以通常應(yīng)該使用-1;resource_type_name是資源名稱,當(dāng)獲取資源失敗時,函數(shù)使用它來輸出錯誤信息;resource_type是資源類型,如果取得的資源不是該類型的,則函數(shù)返回NULL,這用于防止用戶傳入一個其他類型資源的zval。
不過,這個宏確實比較難用,用其底層的宏反倒更加容易些:
| 1 | zend_list_find(id, type) |
id是要查找的資源id;type是int*類型,用于接收取出的資源的類型,可以用它來判斷這是不是我們想要的資源;函數(shù)最后返回資源的handle,失敗返回NULL。
1.2.4? 維護(hù)引用計數(shù)
通常,當(dāng)用戶對資源類型的PHP變量執(zhí)行賦值或是unset之類操作時,Zend會自動維護(hù)資源的引用計數(shù)。但有時,我們也需要手動進(jìn)行,比如我們要復(fù)用一個數(shù)據(jù)庫連接或者用戶調(diào)用我們提供的close操作關(guān)閉一個文件,此時可以使用如下宏:
| 1 | zend_list_addref(id) |
| 2 | zend_list_delete(id) |
id是資源id,這兩個宏分別增加和減少目標(biāo)資源的引用計數(shù),第二個宏還會在引用計數(shù)減到0時,調(diào)用先前注冊的函數(shù)銷毀資源。
轉(zhuǎn)載于:https://www.cnblogs.com/lsl8966/archive/2012/12/10/2811944.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
- 上一篇: Cisco DHCP and NAT c
- 下一篇: linux 下开源常见监控软件