在PHP中,咱們常常使用到資源類型變量。例如:mysql鏈接、文件句柄等。php
這些變量沒法使用標量來表示,那麼在Zend內核中是如何將PHP中的資源變量與C語言中的資源銜接的呢?mysql
1、資源變量在PHP中的使用sql
- $fp = fopen("test.txt", "rw");
-
- var_dump($fp);
-
- fclose($fp);
打印結果:resource(5) of type (stream)數據庫
數字5:表示資源ID爲5,具體含義後面介紹。函數
stream:資源類型名稱。php-fpm
2、資源IDspa
內核中將註冊的資源變量存儲在一個HashTable中,並把資源所在HashTable中的key做爲資源ID。.net
因此,實際上PHP中的資源變量實際存儲的是一個整型,經過這個ID找到HashTable中對應的資源。指針
- #define Z_RESVAL(zval) (zval).value.lval
- #define Z_RESVAL_P(zval) Z_RESVAL(*zval)
- #define Z_RESVAL_PP(zval) Z_RESVAL(**zval)
上面的宏,是內核中ZE爲資源變量賦值的API,看出確實是對整型變量的賦值。blog
3、資源類型名稱
爲了區分資源類型,須要爲咱們定義的資源定義類型名稱。
- #define MY_RES_NAME "my_resource" //資源類型名稱,PHP經過var_dump打印資源變量時會看到這個名稱
- static int my_resource_descriptor;
-
- ZEND_MINIT_FUNCTION(jinyong)
- {
- my_resource_descriptor = zend_register_list_destructors_ex(NULL, NULL, MY_RES_NAME, module_number);
- }
ZEND_MINIT_FUNCTION(jinyong)會在PHP做爲SAPI(例如,Apache的mod_php5擴展)被加載到內存時,會執行全部擴展的ZEND_MINIT_FUNCTION。
其中jinyong,是當前擴展的名字。例如此時擴展的名字就是jinyong
這裏爲了方便理解,咱們就把它認爲是擴展在初始化時,會向內核中註冊新的資源類型。
4、建立資源變量
資源類型已經註冊成功,也爲資源定義了區分的類型名稱。如今能夠使用這種資源的變量了。
實現PHP中的fopen函數:
- PHP_FUNCTION(my_fopen)
- {
- zval *res;
-
- char *filename, *mode;
-
- int filename_strlen, mode_strlen;
-
- FILE *fp;
-
- if(zend_parse_parameters(ZEND_NUM_ARGS TSRMLS_CC, "s|s", &filename, &filename_strlen, &mode, &mode_strlen) == FAILURE){
- RETURN_FALSE;
- }
-
-
- fp = fopen(filename, mode);
-
- ZEND_REGISTER_RESOURCE(res, fp, my_resource_descriptor);
-
- RETURN_RESOURCE(res);
- }
這裏,定義了PHP中名稱爲my_fopen的函數。my_fopen(string $file_name, string $mode)
實現PHP中的fclose函數:
- PHP_FUNCTION(my_fclose)
- {
- zval *res;
-
- FILE *fp;
-
- if(zend_parse_parameters(ZEND_NUM_ARGS TSRMS_CC, "r", &res) == FAILURE){
- RETURN_FALSE;
- }
-
- if(Z_TYPE_P(res) == IS_RESOURCE){
- zend_hash_index_del(&EG(regular_list), Z_RESVAL_P(res));
- }else{
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "參數必須是資源類型變量");
- RETURN_FALSE;
- }
-
- RETURN_TRUE;
- }
定義了PHP中名稱爲my_fclose的函數。my_fclose($resource)
5、編譯、安裝擴展,重啓php-fpm或mod_php5等
6、PHP中使用自定義擴展中的方法
- my_fwrite($fp, "aaTest");
-
- var_dump($fp);
-
- my_fclose($fp);
-
- var_dump($fp);
能夠正常,打開和關閉資源。
7、咱們在PHP中常常使用數據庫鏈接資源、文件句柄資源,但他們一般無需咱們手工釋放,也不會出現內存泄漏問題,這是如何實現的呢?
- my_resource_descriptor = zend_register_list_destructors_ex(NULL, NULL, MY_RES_NAME, module_number);
回到最開始的註冊資源類型,看到zend_register_list_destructors_ex的第一個參數,這個參數就是析構函數的指針。
那麼,若是須要實現自動釋放功能,只須要定義析構函數並傳遞函數指針便可。
再看一個問題:
- $fp = fopen("test.txt", "rw");
-
- var_dump($fp);
-
-
- unset($fp);
定義析構函數:
- static void php_myres_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC){
- FILE *fp = (FILE*)rsrc->ptr;
- fclose(fp);
- }
-
- ZEND_MINIT_FUNCTION(jinyong)
- {
- my_resource_descriptor = zend_register_list_destructors_ex(php_myres_dtor, NULL, MY_RES_NAME, module_number);
- }
- 在PHP中,所謂資源變量,實際都是經過存儲整型值,在到內核全局資源變量列表EG(regular_list)中找到對應的指針,並進行相應操做。
- 而PHP資源變量,之因此不用擔憂相似MYSQL鏈接未釋放問題,也是由於擴展中定義了析構方法,幫助自動釋放。