0

0

php扩展与嵌入资源数据类型2

php中文网

php中文网

发布时间:2016-06-23 13:58:24

|

1068人浏览过

|

来源于php中文网

原创

在资源变量中存储的复杂的数据类型通常在初始化时需要一些内存分配,CPU时间或网络通信。但是在请求之间保留类似于数据库连接这种资源,必须要做到持久。资源是否持久是一个必须要考虑到的因素。


首先看内存分配的问题:

   

     在使用php的时候,偏向使用emalloc因为它是malloc的带回收的版本。但是持久化的资源必须在请求间都存在。对于一个文件句柄类的资源来说,如果要加入一个存储文件名的需求,那么必须在头文件中加入如下的代码:

typedef struct _php_sample_descriptor_data {    char *filename;    FILE *fp;} php_sample_descriptor_data;

    利用这个结构可以存储文件名和文件句柄资源,从而能够在不同的请求之间进行共享。


   对应的,要在源文件中进行相应的更改:

static void php_sample_descriptor_dtor( //这个是进行资源回收的回调函数,定义在资源的初始化处。                    zend_rsrc_list_entry *rsrc TSRMLS_DC){    php_sample_descriptor_data *fdata =                (php_sample_descriptor_data*)rsrc->ptr;    fclose(fdata->fp);    efree(fdata->filename);    efree(fdata);}

    这个静态函数用来进行资源的回收,需要在初始化资源的时候进行指定回调。


    进行修改后的文件打开函数,需要增加给资源分配空间的操作:

PHP_FUNCTION(sample_fopen) //修改后的fopen{    php_sample_descriptor_data *fdata;    FILE *fp;    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;    }    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;    }    <strong>fdata = emalloc(sizeof(php_sample_descriptor_data)); //给包含了文件资源和文件名的结构分配空间    fdata->fp = fp;    fdata->filename = estrndup(filename, filename_len);</strong>    ZEND_REGISTER_RESOURCE(return_value, fdata,                                le_sample_descriptor); // 注册资源}

    对于文件写入函数fwrite同样需要修改:

PHP_FUNCTION(sample_fwrite){    php_sample_descriptor_data *fdata;    zval *file_resource;    char *data;    int data_len;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",            &file_resource, &data, &data_len) == FAILURE ) {        RETURN_NULL();    }    <strong>ZEND_FETCH_RESOURCE(fdata, php_sample_descriptor_data*,        &file_resource, -1,        PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);</strong>    RETURN_LONG(fwrite(data, 1, data_len, fdata->fp));} 

    对于sample_fclose函数并不需要改变什么,因为它没有操作实际的资源。下面这个函数可以从资源中拿到原本的文件名:

PHP_FUNCTION(sample_fname){    php_sample_descriptor_data *fdata;    zval *file_resource;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",            &file_resource) == FAILURE ) {        RETURN_NULL();    }    <strong>ZEND_FETCH_RESOURCE(fdata, php_sample_descriptor_data*,        &file_resource, -1,        PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);</strong>    RETURN_STRING(fdata->filename, 1);} 


在完成了内存分配之后,由于必须保持持久化,所以必须 延迟析构


    对于非持久的资源来说,一旦存放着资源id的变量被unset或fallen out of scope了,那么它们就被从EG(regular_list)中去除掉了。 而EG(persistent_list)中使用的索引是键值类的,元素在请求的最后不会不会被自动的去除掉。只有在zend_hash_del()调用或线程/进程完全关闭的情况下才会消除。

    EG(persistent_list)也有dtor方法,但是是zend_register_list_descructors_ex()的第二个参数。一般来说,非持久和持久的资源会被注册成两种类型,有的时候也可以合二为一。现在在sample.c中添加一个持久的资源类型。

    static int le_sample_descriptor_persist;    static void php_sample_descriptor_dtor_persistent(                    zend_rsrc_list_entry *rsrc TSRMLS_DC){<strong>//这是一个持久化的资源析构函数</strong>    php_sample_descriptor_data *fdata =                (php_sample_descriptor_data*)rsrc->ptr;    <strong>fclose(fdata->fp);    pefree(fdata->filename, 1);    pefree(fdata, 1);</strong>}PHP_MINIT_FUNCTION(sample){    le_sample_descriptor =     zend_register_list_destructors_ex(            php_sample_descriptor_dtor, NULL,            PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number);    <strong>le_sample_descriptor_persist =                        zend_register_list_destructors_ex(            NULL, php_sample_descriptor_dtor_persistent,            PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number);//注册一个持久化的资源</strong>    return SUCCESS;} 

    下面的这个fopen函数就兼容了持久与非持久的两个资源类型:

Destoon B2B网站
Destoon B2B网站

Destoon B2B网站管理系统是一套完善的B2B(电子商务)行业门户解决方案。系统基于PHP+MySQL开发,采用B/S架构,模板与程序分离,源码开放。模型化的开发思路,可扩展或删除任何功能;创新的缓存技术与数据库设计,可负载千万级别数据容量及访问。 系统特性1、跨平台。支持Linux/Unix/Windows服务器,支持Apache/IIS/Zeus等2、跨浏览器。基于最新Web标准构建,在

下载

PHP_FUNCTION(sample_fopen){    php_sample_descriptor_data *fdata;    FILE *fp;    char *filename, *mode;    int filename_len, mode_len;    zend_bool persist = 0;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"ss|b",                &filename, &filename_len, &mode, &mode_len,                &persist) == FAILURE) {        RETURN_NULL();    }    if (!filename_len || !mode_len) {        php_error_docref(NULL TSRMLS_CC, E_WARNING,                "Invalid filename or mode length");        RETURN_FALSE;    }    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;    }    if (!persist) {//非持久化的资源        fdata = emalloc(sizeof(php_sample_descriptor_data));        fdata->filename = estrndup(filename, filename_len);//这个做了申请内存和赋值两步操作         fdata->fp = fp;        ZEND_REGISTER_RESOURCE(return_value, fdata,                                le_sample_descriptor);    } else {//持久化的资源        list_entry le;        char *hash_key;                  int hash_key_len;        fdata =pemalloc(sizeof(php_sample_descriptor_data),1);        fdata->filename = pemalloc(filename_len + 1, 1);        memcpy(fdata->filename, filename, filename_len + 1);        fdata->fp = fp;        ZEND_REGISTER_RESOURCE(return_value, fdata,                        le_sample_descriptor_persist);        /* Store a copy in the persistent_list 在persistent_list存储一份副本 */        le.type = le_sample_descriptor_persist;        le.ptr = fdata;        hash_key_len = spprintf(&hash_key, 0,                "sample_descriptor:%s:%s", filename, mode);        zend_hash_update(&EG(persistent_list),            hash_key, hash_key_len + 1,            (void*)&le, sizeof(list_entry), NULL);        efree(hash_key);    }} 

    对于非持久化的资源,给定了一个数字的索引,并存放在了跟请求依存的list中。

立即学习PHP免费学习笔记(深入)”;

    对于持久化的资源, 给定了一个键值类型,这个hashkey可以在接下来的请求中被重新得到。然后把资源放进了persistentlist中。 当一个持久的资源out of scope的时候,EG(regular_list)的析构函数会为le_sample_descriptro_persist检查registerlist析构。发现是NULL的话不会有任何的操作。从而也就保证了持久的资源不会被释放掉。 当资源被从EG(persistent_list)中去除的时候,要么是线程进程结束了,要么是故意删除掉了。这时候就会去找持久化的析构函数。



资源被申请为持久化的原因就是为了在其他的请求中可以 复用


    如果想要复用持久化的资源,那就一定要用到hash_key, 当sample_fopen被调用的时候,函数会利用请求的文件名和模式重新创建hash_key,然后尝试在persistent_list中找到它。

PHP_FUNCTION(sample_fopen){    php_sample_descriptor_data *fdata;    FILE *fp;    char *filename, *mode, *hash_key;    int filename_len, mode_len, hash_key_len;    zend_bool persist = 0; //判断是否持久    list_entry *existing_file;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"ss|b",                &filename, &filename_len, &mode, &mode_len,                &persist) == FAILURE) {        RETURN_NULL();    }    if (!filename_len || !mode_len) {        php_error_docref(NULL TSRMLS_CC, E_WARNING,                "Invalid filename or mode length");        RETURN_FALSE;    }    <strong>/* 通过获得一个hash_key尝试寻找一个已经打开的文件 */    hash_key_len = spprintf(&hash_key, 0,            "sample_descriptor:%s:%s", filename, mode);                                                                                                                  if (zend_hash_find(&EG(persistent_list), hash_key,            hash_key_len + 1, (void **)&existing_file) == SUCCESS) {        /* 成功的找到了这个已经打开的文件句柄资源 */        ZEND_REGISTER_RESOURCE(return_value,            existing_file->ptr, le_sample_descriptor_persist);        efree(hash_key);        return;    }</strong>    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;    }    if (!persist) {        fdata = emalloc(sizeof(php_sample_descriptor_data));        fdata->filename = estrndup(filename, filename_len);        fdata->fp = fp;        ZEND_REGISTER_RESOURCE(return_value, fdata,                                le_sample_descriptor);    } else {        list_entry le;        fdata =pemalloc(sizeof(php_sample_descriptor_data),1);        fdata->filename = pemalloc(filename_len + 1, 1);        memcpy(data->filename, filename, filename_len + 1);        fdata->fp = fp;        ZEND_REGISTER_RESOURCE(return_value, fdata,                        le_sample_descriptor_persist);        /* Store a copy in the persistent_list */        le.type = le_sample_descriptor_persist;        le.ptr = fdata;        /* hash_key has already been created by now */        zend_hash_update(&EG(persistent_list),            hash_key, hash_key_len + 1,            (void*)&le, sizeof(list_entry), NULL);    }    efree(hash_key);}

    注意由于所有的扩展都使用相同的哈希表单去存储资源,所以命名很重要。一般都是用扩展和资源类型名作为前缀。 



检查资源可用性:


    尽管像文件这种资源可以长期打开,但是类似远程网络资源这种如果在请求之间长期不用的话就有问题。 所以在使用一个persistent资源之前,要先确定可用性。

if (zend_hash_find(&EG(persistent_list), hash_key,        hash_key_len + 1, (void**)&socket) == SUCCESS) {    if (php_sample_socket_is_alive(socket->ptr)) {        ZEND_REGISTER_RESOURCE(return_value,                    socket->ptr, le_sample_socket);        return;    }    zend_hash_del(&EG(persistent_list),                            hash_key, hash_key_len + 1); //这里会去调用之前注册好的析构函数}








   


相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

616

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

194

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

91

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

20

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

54

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

29

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

598

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

56

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 5.7万人学习

PostgreSQL 教程
PostgreSQL 教程

共48课时 | 9.3万人学习

Git 教程
Git 教程

共21课时 | 3.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号