PHP資源類型

來源:互聯網
上載者:User

在PHP中,我們經常使用到資源類型變數。例如:mysql串連、檔案控制代碼等。

這些變數無法使用標量來表示,那麼在Zend核心中是如何將PHP中的資源變數與C語言中的資源銜接的呢?

一、資源變數在PHP中的使用

$fp = fopen("test.txt", "rw");var_dump($fp);fclose($fp);

列印結果:resource(5) of type (stream)

數字5:表示資源ID為5,具體含義後面介紹。

stream:資源類型名稱。

二、資源ID

核心中將註冊的資源變數儲存在一個HashTable中,並把資源所在HashTable中的key作為資源ID。

所以,實際上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,看出確實是對整型變數的賦值。

三、資源類型名稱

為了區分資源類型,需要為我們定義的資源定義類型名稱。

#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

這裡為了方便理解,我們就把它認為是擴充在初始化時,會向核心中註冊新的資源類型。

四、建立資源變數

資源類型已經註冊成功,也為資源定義了區分的類型名稱。現在可以使用這種資源的變數了。

實現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);//向全域變數&EG(regular_list)中註冊資源變數,並將對應HashTable的ID賦值給resRETURN_RESOURCE(res);//向PHP返回資源變數}

這裡,定義了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));//EG就類似於PHP中的$_GLOBALS。在全域資源變數regular_list中刪除對應ID的資源}else{php_error_docref(NULL TSRMLS_CC, E_WARNING, "參數必須是資源類型變數");RETURN_FALSE;}RETURN_TRUE;}

定義了PHP中名稱為my_fclose的函數。my_fclose($resource)

五、編譯、安裝擴充,重啟php-fpm或mod_php5等

六、PHP中使用自訂擴充中的方法

my_fwrite($fp, "aaTest");var_dump($fp);my_fclose($fp);var_dump($fp);

可以正常,開啟和關閉資源。

七、我們在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);//fclose($fp); 此處不使用fclose釋放資源unset($fp); //而是使用unset釋放//unset沒有問題,會正常釋放$fp變數。但$fp對應真正的開啟檔案資源控制代碼資源將永遠釋放不了,直至mod_php5或php-fpm重啟//可以看出,在註冊資源類型時定義解構函式的必要性了

定義解構函式:

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串連未釋放問題,也是因為擴充中定義了析構方法,協助自動釋放。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.