资源类型在内核中的结构

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

资源类型的使用

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

代码示例

//创建一个结构体,保存在资源里
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 < ) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "No name given, person resource not created.");
RETURN_FALSE;
} if (age < || age > ) {
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, -, 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 内存泄漏

在刚才的代码中,我们注册了一个自己的资源类型,实现了在函数调用中,返回资源类型,然后使用它,但是结果中报了内存泄漏,现在我们增加一个析构函数,处理这个问题。

//增加一个析构函数,通常用来释放资源
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;
}

我们来看一下,当需要使用资源时,用了一个宏来获取

//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, , resource_type); \
ZEND_VERIFY_RESOURCE(rsrc); #define ZEND_VERIFY_RESOURCE(rsrc) \
if (!rsrc) { \
RETURN_FALSE; \
} zend_fetch_resource()是对zend_hash_find()的一层封装,它使用一个数字Key去一个专门保存资源的HashTable中查找我们需要的资源数据。找到之后,接着对它做了一个校验。 参数说明:
,实际存储资源的类型变量
,类型
,存储资源的zval变量
,-
,资源类型名称
,资源类型数据 ZEND_FETCH_RESOURCE(person, php_hello_person*, &zperson, -, 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的资源释放函数

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(),申请长资源和普通资源的步骤类似,我们来看下面的代码

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

php扩展开发-资源类型的更多相关文章

  1. iOS开发系列--App扩展开发

    概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...

  2. Chrome扩展开发之二——Chrome扩展中脚本的运行机制和通信方式

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  3. PHP扩展开发相关总结

    1.线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, ...

  4. Chrome扩展开发之一——Chrome扩展的文件结构

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  5. 【干货】Chrome插件(扩展)开发全攻略(不点进来看看你肯定后悔)

    写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...

  6. Chrome浏览器扩展开发系列之一:初识Google Chrome扩展

    1.       Google Chrome扩展简介 Google Chrome扩展是一种软件,以增强Chrome浏览器的功能. Google Chrome扩展使用HTML.JavaScript.CS ...

  7. 【干货】Chrome插件(扩展)开发全攻略

    写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...

  8. PHP扩展开发教程(总结)

    PHP是一种解释型的语言,对于用户而言,我们精心的控制内存意味着easier prototyping和更少的崩溃!当我们深入到内核之后,所有的安全防线都已经被越过,最终还是要依赖于真正有责任心的软件工 ...

  9. skipper filter 扩展开发

    skipper 的扩展包含filter类型的,以及Predicates ,当然script(lua)脚本也是 这次主要是filter类型的开发 filter 接口约定 格式 filter 至少需要包含 ...

随机推荐

  1. 云计算&大数据相关知识

    1.极客学院云计算&大数据总链接:http://wiki.jikexueyuan.com/list/cloud/ 一.NSQ相关参考资料: 1.极客学院NSQ指南:http://wiki.ji ...

  2. Annotation(注解)的概念、作用及常用注解

    Annotation的概念: 能够添加到 Java 源代码的语法元数据.类.方法.变量.参数.包都可以被注解,可用来将信息元数据与程序元素进行关联.Annotation 中文常译为“注解”. 从JDK ...

  3. BZOJ1563: [NOI2009]诗人小G(决策单调性 前缀和 dp)

    题意 题目链接 Sol 很显然的一个dp方程 \(f_i = min(f_j + (sum_i - sum_j - 1 - L)^P)\) 其中\(sum_i = \sum_{j = 1}^i len ...

  4. Python列表边遍历边修改问题解决方案:alist[:]

    最近在看python,遇到个简单的问题:删除列表中指定的重复元素,发现一些实用并且有趣的东西. 1.错误示范 alist = [1,1,2,2,3,3,2,2,1,1] for i in alist: ...

  5. HCNA配置console线路密码aaa认证

    Please check whether system data has been changed, and save data in time Configuration console time ...

  6. NFS笔记(一)NFS服务器工作原理及详细配置

    一.NFS工作原理 1.什么是NFS服务器 NFS就是Network File System的缩写,它最大的功能就是可以通过网络,让不同的机器.不同的操作系统可以共享彼此的文件. NFS服务器可以让P ...

  7. frame、window和dialog区别

    属性 Window Frame Dialog 模式化 不是 不是 不是(可设置) 可调大小 不可 可 可 标题栏 无 有 有 边界 无 有 有 标题 无 有 有 菜单栏 无 有 无 焦点管理器 有 有 ...

  8. 小程序wx.request的POST方法的参数传输服务器接收不到

    这是API里面的例子: 而实际这样,服务端拿到的是空值. 将header更改一下,application/x-www-form-urlencoded,则可以让服务器收到数据

  9. 指定类型的成员XX”不支持实体LINQ。只有初始化,成员单位,和实体导航性能的支持。

    The specified type member 'DeleteFlag' is not supported in LINQ to Entities. Only initializers, enti ...

  10. Cydia Tweak--Cydia Substrate

    http://www.jianshu.com/p/8982e9670fc6 Cydia Substrate.MobileHooker MSHookMessageEx MSHookFunction Mo ...