php扩展开发-资源类型
资源类型在内核中的结构
//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扩展开发-资源类型的更多相关文章
- iOS开发系列--App扩展开发
概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...
- Chrome扩展开发之二——Chrome扩展中脚本的运行机制和通信方式
目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...
- PHP扩展开发相关总结
1.线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, ...
- Chrome扩展开发之一——Chrome扩展的文件结构
目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...
- 【干货】Chrome插件(扩展)开发全攻略(不点进来看看你肯定后悔)
写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...
- Chrome浏览器扩展开发系列之一:初识Google Chrome扩展
1. Google Chrome扩展简介 Google Chrome扩展是一种软件,以增强Chrome浏览器的功能. Google Chrome扩展使用HTML.JavaScript.CS ...
- 【干货】Chrome插件(扩展)开发全攻略
写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...
- PHP扩展开发教程(总结)
PHP是一种解释型的语言,对于用户而言,我们精心的控制内存意味着easier prototyping和更少的崩溃!当我们深入到内核之后,所有的安全防线都已经被越过,最终还是要依赖于真正有责任心的软件工 ...
- skipper filter 扩展开发
skipper 的扩展包含filter类型的,以及Predicates ,当然script(lua)脚本也是 这次主要是filter类型的开发 filter 接口约定 格式 filter 至少需要包含 ...
随机推荐
- windows常用命令行总结
cmd下的命令行 1.查看当前目录下的子目录 dir 或 dir /b 类似Linux 下的 ls 或者 ls -l 2.盘符切换 d: 3.进入目录 cd [目录名] 退一个目录 cd .. ...
- 日期API
Java 8 在包java.time下包含了一组全新的时间日期API.下面的例子展示了这组新API里最重要的一些部分: 1.Clock 时钟 Clock类提供了访问当前日期和时间的方法,Clock是时 ...
- MyEclipse快捷键大全,很实用
Eclipse本身很快的,但是加上了myeclipse后,就狂占内存,而且速度狂慢,那如何让Eclipse拖着myeclipse狂飚呢?这里提供一个: 技巧:取消自动validation valid ...
- (C#) Handling and Raising Events
Handling and Raising Events .NET Framework 4.5 Other Versions 6 out of 20 rated this helpful - ...
- django orm 时间字段讲解
创建django的model时,有DateTimeField.DateField和TimeField三种类型可以用来创建日期字段,其值分别对应着datetime().date().time()三中对象 ...
- Sigrity PowerDC是如何计算IR Drop Margin?
IR Drop仿真是一个系统层面的问题,需要考虑完整的Power Distribution System(PDS)链路上所有压降,并以此来优化每颗器件所接收到的供电电压. 在设计设计中所有的电源供电芯 ...
- ownCloud-9.1.1 (Ubuntu 16.04)
平台: Ubuntu 类型: 虚拟机镜像 软件包: owncloud-9.1.1 commercial content management open-source owncloud storage ...
- PHP文件是什么?如何打开PHP文件?
在平时我们可能会碰到过php文件,可是很多用户不知道php文件是什么文件?也不知道怎么打开php文件?为了满足一些用户的好奇心,小编现在就给大家讲解php文件以及如何打开php文件的方法. 1.PHP ...
- 屏蔽各类弹窗广告(WPS、智能云输入法)
托盘中的广告“领取双11红包,最高1111元”的罪魁祸首是“智能云输入法” 广告在托盘中闪动: 结束SCSkinInst.exe后,托盘中的广告消失: 智能云输入法的安装路径可参考: C:\Progr ...
- SSH连接linux时,长时间不操作就断开的解决方案(增强版)
1.第一次尝试失败 修改/etc/ssh/sshd_config文件, 找到 ClientAliveInterval 0 ClientAliveCountMax 3 并将注释符号("#&qu ...