资源类型在内核中的结构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; }