search
Homephp教程php手册php扩展与嵌入--资源数据类型2

在资源变量中存储的复杂的数据类型通常在初始化时需要一些内存分配,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函数就兼容了持久与非持久的两个资源类型:
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,"ssb",
                &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中。 对于持久化的资源,给定了一个键值类型,这个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,"ssb",
                &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); //这里会去调用之前注册好的析构函数
}











Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
如何在Zend框架中使用ACL(Access Control List)进行权限控制如何在Zend框架中使用ACL(Access Control List)进行权限控制Jul 29, 2023 am 09:24 AM

如何在Zend框架中使用ACL(AccessControlList)进行权限控制导言:在一个Web应用程序中,权限控制是至关重要的一项功能。它可以确保用户只能访问其有权访问的页面和功能,并防止未经授权的访问。Zend框架提供了一种方便的方法来实现权限控制,即使用ACL(AccessControlList)组件。本文将介绍如何在Zend框架中使用ACL

PHP实现框架:Zend Framework入门教程PHP实现框架:Zend Framework入门教程Jun 19, 2023 am 08:09 AM

PHP实现框架:ZendFramework入门教程ZendFramework是PHP开发的一种开源网站框架,目前由ZendTechnologies维护,ZendFramework采用了MVC设计模式,提供了一系列可重用的代码库,服务于实现Web2.0应用程序和Web服务。ZendFramework深受PHP开发者的欢迎和推崇,拥有广泛

Window2003 IIS+MySQL+PHP+Zend环境如何配置Window2003 IIS+MySQL+PHP+Zend环境如何配置Jun 02, 2023 pm 09:56 PM

  Windows2003安装包中包含了Zend,PHP5.2.17,PHPWind8.7和PHPMyadmin3.5.2,您可以直接下载安装包,节约搜索资源的时间。  但是,由于MySQL超出了上传限制,您需要另行前往MySQL官网下载。然后解压拷贝到D盘,如下图:  MySQLinDdisk  安装与配置WindowsIIS+FTP  单击开始>控制面板>添加或删除程序。  AddingordeletingaPG  单击添加/删除Windows组件(A)。  Addingorde

PHP无法识别ZendOptimizer,如何解决?PHP无法识别ZendOptimizer,如何解决?Mar 19, 2024 pm 01:09 PM

PHP无法识别ZendOptimizer,如何解决?在PHP开发中,有时可能会遇到PHP无法识别ZendOptimizer的情况,这会导致部分PHP代码无法正常运行。在这种情况下,我们需要采取一些措施来解决这个问题。下面将介绍一些可能的解决方法,并附上具体的代码示例。1.确认ZendOptimizer是否正确安装:首先,我们需要确认ZendOptimize

如何使用PHP框架Zend开发一个高效的ERP管理平台如何使用PHP框架Zend开发一个高效的ERP管理平台Jun 26, 2023 pm 11:00 PM

随着信息技术的飞速发展,越来越多的企业开始意识到信息化管理的必要性。ERP(企业资源计划)管理平台是现代企业管理的重要工具,可以帮助企业实现资源的规划、协同、控制、优化和管理。其中,PHP框架Zend作为一款优秀的开发工具,可以帮助开发者快速高效地实现ERP系统的开发。本文将介绍如何使用Zend开发一个高效的ERP管理平台。一、确定需求分析在开始开发过程之前

Laravel vs Zend:哪个框架更适合开发大型应用?Laravel vs Zend:哪个框架更适合开发大型应用?Jun 19, 2023 am 08:52 AM

随着互联网应用的不断发展,大型应用的开发需求也不断增加。在这样的背景下,选择适合自己的开发框架显得尤为重要。Laravel和Zend是两个广泛使用的PHP框架,他们各有优势,但哪一个更适合开发大型应用呢?Laravel是一个广受欢迎的开发框架,已经成为PHP开发人员的首选框架之一。它采用了现代化的设计理念,内置多种强大的功能和工具,例如EloquentOR

优化Zend框架的性能的方法优化Zend框架的性能的方法Jan 22, 2024 am 11:25 AM

Zend框架是一个基于PHP语言的开源Web应用程序框架,被广泛应用于企业级Web应用的开发中。尽管Zend框架凭借其高度模块化、可扩展性和代码重用性等优势在市场上占据重要地位,但这并不意味着它的性能就一定高效。事实上,如何优化Zend框架的性能一直是开发者们关注的重点之一。本文将从多个方面探讨如何提高Zend框架的性能。一、合理使用Zend框架的缓存机制Z

使用PHP框架Zend开发一个高性能的搜索引擎使用PHP框架Zend开发一个高性能的搜索引擎Jun 27, 2023 am 08:36 AM

随着互联网信息的爆炸式增长,搜索引擎已经成为人们获取信息的首选方式之一。而现在,随着网站数量的不断增加,搜索引擎的快速响应和准确性变得越来越重要,而这就要求搜索引擎必须具备高性能。在这篇文章中,我将介绍如何使用PHP框架Zend来开发一个高性能的搜索引擎。一、为什么使用Zend框架Zend框架是一个高性能的PHP框架,它在性能和可扩展性方面都有非常出色的表现

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
3 weeks agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

SecLists

SecLists

SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

SublimeText3 English version

SublimeText3 English version

Recommended: Win version, supports code prompts!