首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 資源型別深度解析:註冊、管理與析構機制

     編輯:PHP 綜合知識

PHP 資源型別 是連結 C 層面不透明指標(如 FILE*、DB 連線、GD 圖片句柄)至 PHP 變數的橋樑。本文完整解析資源型別定義、註冊、析構器機制與實戰範例。

資源型別本質

  • 在 zval 中儲存 **整數 ID**(資源索引)。
  • 全域資源表 EG(regular_list) (HashTable) 以 ID 為鍵,值為 zend_rsrc_list_entry:
typedef struct _zend_rsrc_list_entry {
    void *ptr;           // 實際資源指標
    int type;            // 資源型別 ID
    int refcount;        // 參考計數
} zend_rsrc_list_entry;

定義資源型別 (MINIT 階段)

static int le_sample_descriptor; // 全域變數儲存資源型別 ID

PHP_MINIT_FUNCTION(sample) {
    // 註冊資源型別,取得唯一 ID
    le_sample_descriptor = zend_register_list_destructors_ex(
        NULL,                    // 非持久資源析構器
        NULL,                    // 持久資源析構器
        PHP_SAMPLE_DESCRIPTOR_RES_NAME, // 型別名稱 (除錯用)
        module_number            // 模組編號
    );
    return SUCCESS;
}

// 模組結構中掛載 MINIT
zend_module_entry sample_module_entry = {
    // ...
    PHP_MINIT(sample),  // MINIT
    // ...
};

// 頭文件定義型別名稱
#define PHP_SAMPLE_DESCRIPTOR_RES_NAME "Sample File Descriptor"

建立資源並返回給 PHP (ZEND_REGISTER_RESOURCE)

PHP_FUNCTION(sample_fopen) {
    char *filename, *mode;
    int filename_len, mode_len;
    
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", 
                              &filename, &filename_len, &mode, &mode_len) == FAILURE) {
        RETURN_NULL();
    }
    
    if (!filename_len || !mode_len) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename or mode length");
        RETURN_FALSE;
    }
    
    FILE *fp = fopen(filename, mode);
    if (!fp) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s using mode %s", filename, mode);
        RETURN_FALSE;
    }
    
    // 關鍵:將 FILE* 註冊為資源,ID 存入 return_value
    ZEND_REGISTER_RESOURCE(return_value, fp, le_sample_descriptor);
}

取得資源並操作 (ZEND_FETCH_RESOURCE)

PHP_FUNCTION(sample_fread) {
    zval *res;
    long length = 8192;
    
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &res, &length) == FAILURE) {
        RETURN_NULL();
    }
    
    FILE *fp;
    // 從資源 ID 取得 FILE*,並驗證型別
    ZEND_FETCH_RESOURCE(fp, FILE*, &res, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);
    
    char *buf = emalloc(length + 1);
    size_t read_len = fread(buf, 1, length, fp);
    buf[read_len] = '\0';
    
    RETVAL_STRINGL(buf, read_len, 0); // 0=不複製,直接接管記憶體
    efree(buf); // 注意:RETVAL_STRINGL(...,0) 會接管 buf,此處 efree 會導致 double free,實際應省略或 dup=1
}

資源析構器實作

// 非持久資源析構器 (請求結束調用)
void php_sample_descriptor_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
    FILE *fp = (FILE*)rsrc->ptr;
    if (fp) {
        fclose(fp);
    }
}

// 持久資源析構器 (模組關閉調用)
void php_sample_descriptor_persist_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
    FILE *fp = (FILE*)rsrc->ptr;
    if (fp) {
        fclose(fp);
    }
}

// MINIT 註冊時指定析構器
le_sample_descriptor = zend_register_list_destructors_ex(
    php_sample_descriptor_dtor,        // 非持久
    php_sample_descriptor_persist_dtor, // 持久
    PHP_SAMPLE_DESCRIPTOR_RES_NAME,
    module_number
);

資源生命週期流程圖

PHP 腳本呼叫 sample_fopen()
        │
        ▼
fopen() 取得 FILE* 指標
        │
        ▼
ZEND_REGISTER_RESOURCE()
        │
        ├─► zend_hash_next_free_element(EG(regular_list)) → 取得新 ID
        │
        ├─► 建立 zend_rsrc_list_entry {ptr=FILE*, type=le_sample_descriptor, refcount=1}
        │
        ├─► 存入 EG(regular_list)[ID] = entry
        │
        └─► return_value = ID (IS_RESOURCE 型別)
        │
        ▼
PHP 變數持有資源 ID
        │
        ▼
請求結束 (RSHUTDOWN)
        │
        ▼
zend_hash_graceful_reverse_destroy(EG(regular_list))
        │
        ▼
呼叫對應型別的非持久析構器 → fclose(fp)
        │
        ▼
釋放 entry 記憶體

進階:存儲複雜結構體

typedef struct {
    FILE *fp;
    char *filename;
    int mode;
} sample_file_handle;

PHP_FUNCTION(sample_fopen_ex) {
    // ... 解析參數 ...
    sample_file_handle *handle = pemalloc(sizeof(sample_file_handle), 0);
    handle->fp = fopen(filename, mode);
    handle->filename = estrdup(filename);
    handle->mode = (strcmp(mode, "w") == 0) ? 1 : 0;
    
    ZEND_REGISTER_RESOURCE(return_value, handle, le_sample_handle);
}

// 析構器需釋放所有子記憶體
void php_sample_handle_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
    sample_file_handle *h = (sample_file_handle*)rsrc->ptr;
    if (h) {
        if (h->fp) fclose(h->fp);
        if (h->filename) efree(h->filename);
        efree(h);
    }
}

關鍵宏總結

宏用途
ZEND_REGISTER_RESOURCE(return_value, ptr, type)註冊資源,ID 存入 return_value
ZEND_FETCH_RESOURCE(var, c_type, zval_ptr, id, name, type)從 zval 取得資源指標並驗證型別
zend_register_list_destructors_ex(ndtor, pdtor, name, module)註冊資源型別,取得型別 ID
zend_list_insert(ptr, type)低層註冊,手動管理 ID
zend_list_delete(id)手動刪除資源 (遞減 refcount)
zend_list_find(id, type)查找資源指標
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved