zoukankan      html  css  js  c++  java
  • 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 < 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 内存泄漏

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

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

    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("
    from 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;
    }

     

  • 相关阅读:
    自定义标签的作用
    自定义标签处理器类的生命周期
    自定义标签的执行过程
    自定义标签入门案例
    JSTL核心标签库详解
    JSTL标签(核心标准库)
    动作标签
    jsp标签
    EL表达式
    JSP学习案例--,竞猜游戏
  • 原文地址:https://www.cnblogs.com/wuhen781/p/6218165.html
Copyright © 2011-2022 走看看