国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP 進階之路 - PHP7 使用資源包裹第三方擴展的實現

caozhijian / 3523人閱讀

摘要:釋放用于在不同請求中始終存在的永久資源的函數。這是新增的數據結構,在則是。這使得引擎做一些例如資源類型,注冊變量等的一次初始化。也就是說該函數的返回值為指針。會將返回值的設為,設為。資源的刪除資源刪除傳入需要被刪除的資源即可。

PHP 擴展開發的文章,我均已更新至《TIPI》

在閱讀下面的內容之前,我們假定你已經對 PHP 7 基本的數據結構 都有大致的了解了,這是下面內容閱讀的前提。
我們分為兩大塊:
首先實現一個自定義的文件打開、讀取、寫入、關閉的文件操作擴展;
然后分析各個操作背后的實現原理,其中某些部分的實現會和PHP 5.3 使用資源對比分析。

通過原型生成擴展骨架

首先進入到源碼目錄的ext目錄中,添加一個文件操作的原型文件

[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)

然后生成骨架,這些前面都說過,我們不再詳細說

[shell]
[root@localhost ext]# ./ext_skel --extname=tipi_file --proto=./tipi_file.proto

完整的代碼 tipi_file.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)
參數 描述
ld 釋放該資源時調用的函數。
pld 釋放用于在不同請求中始終存在的永久資源的函數。
type_name 描述性類型名稱的字符串別名。
module_number 為引擎內部使用,已經定義好了,比如在PHP_FUNCTION宏中已定義

該 API 返回一個資源類型 id,該 id 應當被作為全局變量保存在擴展里,以便在必要的時候傳遞給其他資源 API。

添加資源釋放回調函數

該方法表示在釋放該類型資源時都需要關閉打開的文件描述符。

[c]
static void tipi_file_dtor(zend_resource *rsrc TSRMLS_DC){
     FILE *fp = (FILE *) rsrc->ptr;
     fclose(fp);
}

我們發現該函數的參數類型是 zend_resource 。這是 PHP7 新增的數據結構,在 PHP 5 則是 zend_rsrc_list_entry 。細節的內容,我們留在后面分析。

在 PHP_MINIT_FUNCTION 中注冊資源類型

我們知道在 PHP 生命周期中,當 PHP 被裝載時,PHP_MINIT_FUNCTION(模塊啟動函數)即被引擎調用。
這使得引擎做一些例如資源類型,注冊INI變量等的一次初始化。
那么我們需要在這里通過 zend_register_list_destructors_exPHP_MINIT_FUNCTION來注冊資源類型。

[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;
}

NOTICE 其中 TIPI_FILE_TYPE 在前面已經定義了,是該擴展的別名。具體請參考最該節最開始給予的完整代碼樣例。

注冊資源

前面是注冊了新的資源類型,然后需要注冊一個該類型的資源。

注冊資源 API

在 PHP 7 中刪除了原來的 ZEND_REGISTER_RESOURCE 宏,直接使用 zend_register_resource 函數

[c]
ZEND_API zend_resource* zend_register_resource(void *rsrc_pointer, int rsrc_type)
參數 描述
rsrc_pointer 資源數據指針
rsrc_type 注冊資源類型時獲得的資源類型 id
在自定義的 file_open 函數中實現資源的注冊
[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 宏取代標準 C 文件操作函數
    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 作為返回值。也就是說該函數的返回值為zval 指針。RETURN_RES(zend_register_resource(fp, le_tipi_file)) 會將返回值的 value.res 設為 fpu1.type_info 設為 IS_RESOURCE_EX 。大家可以根據源碼非常直觀的了解到,這里不粘貼代碼詳細說明了。

使用資源 使用資源API

在 PHP 7 中刪除了原有的 ZEND_FETCH_RESOURCE 宏,直接使用函數 zend_fetch_resource ,而且解析方式也變得簡單了很多,相比 PHP 5 要高效很多,后面我們再通過繪圖的形式分析對比。

[c]
ZEND_API void *zend_fetch_resource(zend_resource *res, const char *resource_type_name, int resource_type)
參數 描述
res 資源指針
resource_type_name 該類資源的字符串別名
resource_type 該類資源的類型 id
解析資源的實現

當我們要實現文件的讀取時,最終還是需要使用原生的 fread 函數,所以這里需要通過 zend_fetch_resourcezend_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] = "