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