Home  >  Article  >  Backend Development  >  PHP extension and embedding--resource data type 2_PHP tutorial

PHP extension and embedding--resource data type 2_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 10:31:32728browse

Complex data types stored in resource variables often require some memory allocation, CPU time, or network communication during initialization. But retaining resources like database connections between requests must be durable. Durability of resources is a factor that must be considered.
First look at the issue of memory allocation: When using PHP, I prefer to use emalloc because it is the recycled version of malloc. But persistent resources must exist between requests. For a file handle resource, if you want to add a requirement to store file names, you must add the following code to the header file:

typedef struct _php_sample_descriptor_data {
    char *filename;
    FILE *fp;
} php_sample_descriptor_data;
Use this structure to store filename and file handle resources so they can be shared between different requests.
Correspondingly, make corresponding changes in the source file:
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);
}
This static function is used to recycle resources and requires a specified callback when initializing resources.
To perform the modified file opening function, you need to add the operation of allocating space to resources:
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); // 注册资源
}

The file writing function fwrite also needs to be modified:
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));
} 

Nothing needs to be changed for the sample_fclose function because it does not operate on actual resources. The following function can get the original file name from the resource:
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);
} 


After the memory allocation is completed, since persistence must be maintained, it must be delayed destruction:
For non-persistent resources, once the variables storing the resource id are unset or fall out of scope, they are removed from the EG (regular_list). The index used in EG (persistent_list) is key-value type, and the elements will not be automatically removed at the end of the request. Only eliminated if zend_hash_del() is called or the thread/process is shut down completely. EG (persistent_list) also has a dtor method, but it is the second parameter of zend_register_list_descructors_ex(). Generally speaking, non-persistent and persistent resources will be registered as two types, and sometimes they can be combined into one. Now add a persistent resource type in 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;
} 

The fopen function below is compatible with both persistent and non-persistent resource types:
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);
    }
} 

For non-persistent resources, a numeric index is given and stored in a request-dependent list. For persistent resources, given a key value type, the hashkey can be retrieved on subsequent requests. Then put the resource into the persistent list. When a persistent resource goes out of scope, the destructor of EG (regular_list) checks the registerlist destructor for le_sample_descriptro_persist. If it is found to be NULL, no operation will be performed. This ensures that persistent resources will not be released. When a resource is removed from the EG (persistent_list), either the thread process ends or it is deleted intentionally. At this time, you will look for the persistent destructor.

The reason why resources are applied for persistence is to reuse in other requests:
If you want to reuse persistent resources, you must use hash_key. When sample_fopen is called, the function will recreate the hash_key using the requested file name and mode, and then try to find it in the 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);
}

Note that naming is important since all extensions use the same hash form to store resources. Generally, extension and resource type names are used as prefixes.

Check resource availability:
While resources like files can be open for long periods of time, resources like remote network resources are problematic if they are not used for long periods of time between requests. Therefore, before using a persistent resource, you must first determine the availability.
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); //这里会去调用之前注册好的析构函数
}









www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/762935.htmlTechArticleComplex data types stored in resource variables usually require some memory allocation, CPU time or network communication during initialization . But retain resources like database connections between requests...
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