摘要:釋放用于在不同請(qǐng)求中始終存在的永久資源的函數(shù)。這是新增的數(shù)據(jù)結(jié)構(gòu),在則是。這使得引擎做一些例如資源類型,注冊(cè)變量等的一次初始化。也就是說(shuō)該函數(shù)的返回值為指針。會(huì)將返回值的設(shè)為,設(shè)為。資源的刪除資源刪除傳入需要被刪除的資源即可。
PHP 擴(kuò)展開發(fā)的文章,我均已更新至《TIPI》
在閱讀下面的內(nèi)容之前,我們假定你已經(jīng)對(duì) PHP 7 基本的數(shù)據(jù)結(jié)構(gòu) 都有大致的了解了,這是下面內(nèi)容閱讀的前提。
我們分為兩大塊:
首先實(shí)現(xiàn)一個(gè)自定義的文件打開、讀取、寫入、關(guān)閉的文件操作擴(kuò)展;
然后分析各個(gè)操作背后的實(shí)現(xiàn)原理,其中某些部分的實(shí)現(xiàn)會(huì)和PHP 5.3 使用資源對(duì)比分析。
首先進(jìn)入到源碼目錄的ext目錄中,添加一個(gè)文件操作的原型文件
[shell] [root@localhost php-src-php-7.0.3]# cd ext/ [root@localhost ext]# vim tipi_file.proto
編輯為
[shell] resource file_open(string filename, string mode) string file_read(resource filehandle, int size) bool file_write(resource filehandle, string buffer) bool file_close(resource filehandle)
然后生成骨架,這些前面都說(shuō)過(guò),我們不再詳細(xì)說(shuō)
[shell] [root@localhost ext]# ./ext_skel --extname=tipi_file --proto=./tipi_file.proto
完整的代碼 tipi_file.c 可以先有一個(gè)大致的了解,這樣后面閱讀時(shí),思路可能會(huì)清晰很多。
注冊(cè)資源類型 認(rèn)識(shí)注冊(cè)資源類型API[c] ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, const char *type_name, int module_number)
參數(shù) | 描述 |
---|---|
ld | 釋放該資源時(shí)調(diào)用的函數(shù)。 |
pld | 釋放用于在不同請(qǐng)求中始終存在的永久資源的函數(shù)。 |
type_name | 描述性類型名稱的字符串別名。 |
module_number | 為引擎內(nèi)部使用,已經(jīng)定義好了,比如在PHP_FUNCTION宏中已定義 |
該 API 返回一個(gè)資源類型 id,該 id 應(yīng)當(dāng)被作為全局變量保存在擴(kuò)展里,以便在必要的時(shí)候傳遞給其他資源 API。
添加資源釋放回調(diào)函數(shù)該方法表示在釋放該類型資源時(shí)都需要關(guān)閉打開的文件描述符。
[c] static void tipi_file_dtor(zend_resource *rsrc TSRMLS_DC){ FILE *fp = (FILE *) rsrc->ptr; fclose(fp); }
我們發(fā)現(xiàn)該函數(shù)的參數(shù)類型是 zend_resource 。這是 PHP7 新增的數(shù)據(jù)結(jié)構(gòu),在 PHP 5 則是 zend_rsrc_list_entry 。細(xì)節(jié)的內(nèi)容,我們留在后面分析。
在 PHP_MINIT_FUNCTION 中注冊(cè)資源類型我們知道在 PHP 生命周期中,當(dāng) PHP 被裝載時(shí),PHP_MINIT_FUNCTION(模塊啟動(dòng)函數(shù))即被引擎調(diào)用。
這使得引擎做一些例如資源類型,注冊(cè)INI變量等的一次初始化。
那么我們需要在這里通過(guò) zend_register_list_destructors_ex在 PHP_MINIT_FUNCTION來(lái)注冊(cè)資源類型。
[c] PHP_MINIT_FUNCTION(tipi_file) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ le_tipi_file = zend_register_list_destructors_ex(tipi_file_dtor, NULL, TIPI_FILE_TYPE, module_number); return SUCCESS; }
注冊(cè)資源NOTICE 其中 TIPI_FILE_TYPE 在前面已經(jīng)定義了,是該擴(kuò)展的別名。具體請(qǐng)參考最該節(jié)最開始給予的完整代碼樣例。
前面是注冊(cè)了新的資源類型,然后需要注冊(cè)一個(gè)該類型的資源。
注冊(cè)資源 API在 PHP 7 中刪除了原來(lái)的 ZEND_REGISTER_RESOURCE 宏,直接使用 zend_register_resource 函數(shù)
[c] ZEND_API zend_resource* zend_register_resource(void *rsrc_pointer, int rsrc_type)
參數(shù) | 描述 |
---|---|
rsrc_pointer | 資源數(shù)據(jù)指針 |
rsrc_type | 注冊(cè)資源類型時(shí)獲得的資源類型 id |
[c] PHP_FUNCTION(file_open) { char *filename = NULL; char *mode = NULL; int argc = ZEND_NUM_ARGS(); size_t filename_len; size_t mode_len; #ifndef FAST_ZPP if (zend_parse_parameters(argc TSRMLS_CC, "ss", &filename, &filename_len, &mode, &mode_len) == FAILURE) return; #else ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STRING(filename, filename_len) Z_PARAM_STRING(mode, mode_len) ZEND_PARSE_PARAMETERS_END(); #endif // 使用 VCWD 宏取代標(biāo)準(zhǔn) C 文件操作函數(shù) FILE *fp = VCWD_FOPEN(filename, mode); if (fp == NULL) { RETURN_FALSE; } RETURN_RES(zend_register_resource(fp, le_tipi_file)); }
其中 RETURN_RES 宏的作用是將返回的 zend_resource添加到 zval 中,然后將最后的 zval 作為返回值。也就是說(shuō)該函數(shù)的返回值為zval 指針。RETURN_RES(zend_register_resource(fp, le_tipi_file)) 會(huì)將返回值的 value.res 設(shè)為 fp,u1.type_info 設(shè)為 IS_RESOURCE_EX 。大家可以根據(jù)源碼非常直觀的了解到,這里不粘貼代碼詳細(xì)說(shuō)明了。
使用資源 使用資源API在 PHP 7 中刪除了原有的 ZEND_FETCH_RESOURCE 宏,直接使用函數(shù) zend_fetch_resource ,而且解析方式也變得簡(jiǎn)單了很多,相比 PHP 5 要高效很多,后面我們?cè)偻ㄟ^(guò)繪圖的形式分析對(duì)比。
[c] ZEND_API void *zend_fetch_resource(zend_resource *res, const char *resource_type_name, int resource_type)
參數(shù) | 描述 |
---|---|
res | 資源指針 |
resource_type_name | 該類資源的字符串別名 |
resource_type | 該類資源的類型 id |
當(dāng)我們要實(shí)現(xiàn)文件的讀取時(shí),最終還是需要使用原生的 fread 函數(shù),所以這里需要通過(guò) zend_fetch_resource 將 zend_resource 解析成為該資源包裹的原始的 FILE * 的指針。
[c] PHP_FUNCTION(file_read) { int argc = ZEND_NUM_ARGS(); int filehandle_id = -1; zend_long size; zval *filehandle = NULL; FILE *fp = NULL; char *result; size_t bytes_read; #ifndef FAST_ZPP if (zend_parse_parameters(argc TSRMLS_CC, "rl", &filehandle, &size) == FAILURE) return; #else ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_RESOURCE(filehandle) Z_PARAM_LONG(size) ZEND_PARSE_PARAMETERS_END(); #endif if ((fp = (FILE *)zend_fetch_resource(Z_RES_P(filehandle), TIPI_FILE_TYPE, le_tipi_file)) == NULL) { RETURN_FALSE; } result = (char *) emalloc(size+1); bytes_read = fread(result, 1, size, fp); result[bytes_read] = "