php擴展開發-資源類型

資源類型在內核中的結構php

//zend_list.h
typedef struct _zend_rsrc_list_entry {
    void *ptr;
    int type;
    int refcount;
} zend_rsrc_list_entry;

資源類型的使用mysql

int le_hello_person; //定義一個全局變量,保存建立的資源類型
#define PHP_HELLO_PERSON_RES_NAME "Person Data"  //資源類型名稱
le_hello_person = zend_register_list_destructors_ex(NULL, NULL, PHP_HELLO_PERSON_RES_NAME, module_number);//資源類型的建立
參數說明:
1,普通資源的析構函數
2,長久資源的析構函數
3,資源的名稱
4,固定寫法
//資源類型的建立必須在MINIT階段,因此是這樣的

PHP_MINIT_FUNCTION(myext)
{
le_hello_person = zend_register_list_destructors_ex(NULL, NULL, PHP_HELLO_PERSON_RES_NAME, module_number);
return SUCCESS;
}sql

代碼示例函數

//建立一個結構體,保存在資源裏
typedef struct _php_hello_person {
    char *name;
    int name_len;
    long age;
} php_hello_person;//php_myext.h

//三個函數申明php_myext.h
PHP_MINIT_FUNCTION(myext);
PHP_FUNCTION(myext_example_resource_new);//
PHP_FUNCTION(myext_example_resource_use);//

#define PHP_HELLO_PERSON_RES_NAME "Person Data"
int le_hello_person;//php_myext.h

    PHP_FE(myext_example_resource_new, NULL)//每一個函數一行,第一個參數與PHP_FUNCTION(name)的name同樣
    PHP_FE(myext_example_resource_use, NULL)//每一個函數一行,第一個參數與PHP_FUNCTION(name)的name同樣

//增長MINIT函數
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    "myext",//擴展名稱
    myext_functions,//zend_function_entry myext_functions 定義好的函數擴展變量
    PHP_MINIT(myext),//MINIT_FUNCTION,把默認的NULL替換成PHP_MINIT(myext)
    NULL,//MSHUTDOWN_FUNCTION
    NULL,//RINIT_FUNCTION
    NULL,//RSHUTDOWN_FUNCTION
    NULL,//MINFO_FUNCTION
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MYEXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};


PHP_MINIT_FUNCTION(myext)
{
    le_hello_person = zend_register_list_destructors_ex(NULL, NULL, PHP_HELLO_PERSON_RES_NAME, module_number);
    return SUCCESS;
}

//建立資源的函數
PHP_FUNCTION(myext_example_resource_new)
{
    php_hello_person *person;
    char *name;
    int name_len;
    long age;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &name, &name_len, &age) == FAILURE) {
        RETURN_FALSE;
    }

    if (name_len < 1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "No name given, person resource not created.");
        RETURN_FALSE;
    }

    if (age < 0 || age > 255) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Nonsense age (%d) given, person resource not created.", age);
        RETURN_FALSE;
    }

    person = emalloc(sizeof(php_hello_person));
    person->name = estrndup(name, name_len);
    person->name_len = name_len;
    person->age = age;

    ZEND_REGISTER_RESOURCE(return_value, person, le_hello_person);
}
//使用資源的函數
PHP_FUNCTION(myext_example_resource_use)
{
    php_hello_person *person;
    zval *zperson;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zperson) == FAILURE) {
        RETURN_FALSE;
    }

    ZEND_FETCH_RESOURCE(person, php_hello_person*, &zperson, -1, PHP_HELLO_PERSON_RES_NAME, le_hello_person);

    php_printf("Hello ");
    PHPWRITE(person->name, person->name_len);
    php_printf("!According to my records, you are %d years old.", person->age);

    RETURN_TRUE;
}

$resource = myext_example_resource_new('zhangxiaomin',31);
myext_example_resource_use($resource);

//結果輸出
Hello zhangxiaomin!According to my records, you are 31 years old.

/home/zhangxiaomin/study/php-5.6.27/ext/myext/myext.c(223) : Freeing 0x7F1763CF76B8 (24 bytes), script=/data1/home/zhangxiaomin/study/php-5.6.27/ext/myext/test.php 內存泄漏

在剛纔的代碼中,咱們註冊了一個本身的資源類型,實現了在函數調用中,返回資源類型,而後使用它,可是結果中報了內存泄漏,如今咱們增長一個析構函數,處理這個問題。fetch

//增長一個析構函數,一般用來釋放資源
static void php_hello_person_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
    php_hello_person *person = (php_hello_person*)rsrc->ptr;

    if (person) {
        if (person->name) {
            efree(person->name);
        }
        efree(person);
    }
}

PHP_MINIT_FUNCTION(myext)
{
    le_hello_person = zend_register_list_destructors_ex(php_hello_person_dtor, NULL, PHP_HELLO_PERSON_RES_NAME, module_number);//把默認的NULL,改爲php_hello_person_dtor
    return SUCCESS;
}

咱們來看一下,當須要使用資源時,用了一個宏來獲取this

//zend_list.h
#define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type)  \
    rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC, default_id, resource_type_name, NULL, 1, resource_type);    \
    ZEND_VERIFY_RESOURCE(rsrc);

#define ZEND_VERIFY_RESOURCE(rsrc)      \
    if (!rsrc) {                        \
        RETURN_FALSE;                   \
    }


zend_fetch_resource()是對zend_hash_find()的一層封裝,它使用一個數字Key去一個專門保存資源的HashTable中查找咱們須要的資源數據。找到以後,接着對它作了一個校驗。

參數說明:
1,實際存儲資源的類型變量
2,類型
3,存儲資源的zval變量
4,-1
5,資源類型名稱
6,資源類型數據

ZEND_FETCH_RESOURCE(person, php_hello_person*, &zperson, -1, PHP_HELLO_PERSON_RES_NAME, le_hello_person);
等價於
int rsyc_type;//rsyc_type會等於le_hello_person
person = (php_hello_person *)zend_list_find(Z_RESVAL_P(zperson),&rsrc_type);

雖然上面咱們建立了析構函數,可是不少場景下,咱們須要手動即便釋放資源,因此跟建立想對應須要有一個類型是close的資源釋放函數spa

PHP_FUNCTION(myext_example_resource_close)
{       
    php_hello_person *person;
    zval *zperson;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zperson) == FAILURE) {
        RETURN_FALSE;
    }       
            
    zend_hash_index_del(&EG(regular_list),Z_RESVAL_P(zperson));
    
    RETURN_TRUE;
}  

 

有時候咱們但願可以保持一個長久的資源,避免不斷的分配開銷,類型與mysql_pconnect(),申請長資源和普通資源的步驟相似,咱們來看下面的代碼code

int le_hello_person_persist;

static void php_hello_person_persist_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_hello_person *person = (php_hello_person*)rsrc->ptr; if (person) { if (person->name) { pefree(person->name, 1); } pefree(person, 1); } } //在MINIT函數中 le_hello_person_persist = zend_register_list_destructors_ex (NULL, php_hello_person_persist_dtor, PHP_HELLO_PERSON_RES_NAME, module_number);//這時把析構函數放在第二個參數上  PHP_FUNCTION(myext_example_resource_pnew) { php_hello_person *person; char *name; int name_len; long age; int key_len; char *key; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &name, &name_len, &age) == FAILURE) { RETURN_FALSE; } if (name_len < 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "No name given, person resource not created."); RETURN_FALSE; } if (age < 0 || age > 255) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Nonsense age (%d) given, person resource not created.", age); RETURN_FALSE; } zend_rsrc_list_entry *le; /* Look for an established resource */ key_len = spprintf(&key, 0, "hello_person_%s_%d", name, age); if (zend_hash_find(&EG(persistent_list), key, key_len + 1, &le) == SUCCESS) {//從哈希表裏獲取以前寫入的數據,第一次這裏則爲空 /* An entry for this person already exists */ ZEND_REGISTER_RESOURCE(return_value, le->ptr, le_hello_person_persist); efree(key); return; } /* New person, allocate a structure */ person = pemalloc(sizeof(php_hello_person), 1); person->name = pemalloc(name_len + 1, 1); memcpy(person->name, name, name_len + 1); person->name_len = name_len; person->age = age; ZEND_REGISTER_RESOURCE(return_value, person, le_hello_person_persist); /* Store a reference in the persistence list */ zend_rsrc_list_entry new_le; new_le.ptr = person; new_le.type = le_hello_person_persist; zend_hash_add(&EG(persistent_list), key, key_len + 1, &new_le, sizeof(zend_rsrc_list_entry), NULL);//保存數據到哈希表裏,下一次直接獲取  efree(key); } //咱們須要修改一下,調用的函數,讓它能夠同時處理兩個類型的資源,只須要改ZEND_FETCH_RESOURCE部分就能夠了 PHP_FUNCTION(myext_example_resource_use) { php_hello_person *person; zval *zperson; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zperson) == FAILURE) { RETURN_FALSE; } if(Z_TYPE_P(zperson) != IS_RESOURCE){ php_printf("參數不是資源類型"); RETURN_FALSE; } //ZEND_FETCH_RESOURCE(person, php_hello_person*, &zperson, -1, PHP_HELLO_PERSON_RES_NAME, le_hello_person);獲取type爲le_hello_person的資源 ZEND_FETCH_RESOURCE2(person, php_hello_person*, &zperson, -1, PHP_HELLO_PERSON_RES_NAME, le_hello_person,le_hello_person_persist);//獲取type爲le_heel_person 或 le_hello_person_persist的資源  php_printf("Hello "); PHPWRITE(person->name, person->name_len); php_printf("!According to my records, you are %d years old.", person->age); /* int rsrc_type; person = (php_hello_person *)zend_list_find(Z_RESVAL_P(zperson),&rsrc_type); if(!person || rsrc_type != le_hello_person){ php_printf("zend_list_find error"); } php_printf("\nfrom zend_list_find Hello "); PHPWRITE(person->name, person->name_len); php_printf("!According to my records, you are %d years old.", person->age); */ RETURN_TRUE; }
相關文章
相關標籤/搜索