82-Resource Resource type

Source: Internet
Author: User
Tags scalar

82-Resource Resource type

Before you tell, describe the structure of the following {resource} type in the kernel:

//每一个资源都是通过它来实现的。typedef struct _zend_rsrc_list_entry{    void *ptr;    int type;    int refcount;}zend_rsrc_list_entry;

In the real world, we often need to manipulate some data that is not good for scalar values, such as a handle to a file, and for C it is just a pointer.

#include <stdio.h>int main(void){    FILE *fd;    fd = fopen("/home/jdoe/.plan", "r");    fclose(fd);    return 0;}

The stdio file descriptor in C is a variable that matches each open file, and it actually has a pointer of 11 file types that will be used when the program interacts with the hardware. We can use the fopen function to open a file to get a handle, and then simply pass the handle to the functions such as feof (), Fread (), fwrite (), fclose (), and you can follow up on the file. Since this data is not directly represented in the C language by scalar data, how do we encapsulate it to ensure that the user can use it in the PHP language as well? This is the role of the resource type variable in PHP! So it is encapsulated by a zval structure.

The implementation of a resource type is not complicated, its value is actually just an integer, and the kernel will look for the data that is needed in a similar resource pool based on this integer value.

Use of resource type variables

A variable of a resource type is also type-differentiated in implementation! To differentiate between different types of resources, such as a file handle and a MySQL link, we need to give it a different category name. First, we need to add this category to the program first. The operation of this step can be done in Minit:

#define PHP_SAMPLE_DESCRIPTOR_RES_NAME "山寨文件描述符"static int le_sample_descriptor;ZEND_MINIT_FUNCTION(sample){    le_sample_descriptor = zend_register_list_destructors_ex(NULL, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME,module_number);    return SUCCESS;}//附加资料#define register_list_destructors(ld, pld) zend_register_list_destructors((void (*)(void *))ld, (void (*)(void *))pld, module_number);ZEND_API int zend_register_list_destructors(void (*ld)(void *), void (*pld)(void *), int module_number);ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number);

Next, we add the defined minit stage function to the extended module_entry, simply replace the original "NULL,/*/minit */" line:

ZEND_MINIT(sample), /* MINIT */

Zend_minit_function () macros are used to help us define the functions of the minit phase. Seeing the ZEND_REGISTER_LIST_DESTRUCTORS_EX () function, you must recall that there is also a zend_register_list_destructors () function? Yes, there is a function that does not have the name of the resource class in its arguments. So what's the difference between these two?

eaco $re_1;//resource(4) of type (山寨版File句柄)echo $re_2;//resource(4) of type (Unknown)
Create a resource

We have registered a new resource type in the kernel above and we can create this type of resource variable next. Next, let's simply re-implement a fopen function, now called Sample_open:

PHP_FUNCTION(sample_fopen){    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;    }    //将fp添加到资源池中去,并标记它为le_sample_descriptor类型的。    ZEND_REGISTER_RESOURCE(return_value,fp,le_sample_descriptor);}

If you have read the previous chapters, you should be able to guess what the last line of code is. It creates a new resource of type Le_sample_descriptor, the value of which is FP, and it adds this resource to the hashtable of a storage resource and assigns this resource the corresponding number key to Return_value.

Resources are not limited to file handles, we can request a piece of memory, and it points to its pointer as a resource. So resources can correspond to any type of data.

Destroying resources

All things in the world are mixed with sorrow and birth, and it is time for us to explore how to destroy resources. The simplest one is not to write a sample_close () function in the form of fclose, which implements the release of a certain {resource: The value that is represented by the resource type variable of PHP.

But what if the user-side script releases a variable of a resource type through the unset () function? They do not know that its value eventually corresponds to a file* pointer, so it is not possible to use the fclose () function to release it, the file* handle is likely to persist in memory until the PHP program hangs up and is recycled by the OS. But in a normal web environment, our servers will run for a long time.

Isn't there a solution? Of course not, the answer is in that null parameter, that's what we're doing. In order to generate a new resource type, the first and second arguments of the ZEND_REGISTER_LIST_DESTRUCTORS_EX () function are called. Both of these parameters each represent a callback parameter. The first callback function is triggered when a resource variable of the appropriate type in the script is freed, such as when the scope ends or is unset () dropped.

The second callback function is used on a resource similar to a long link type, where the resource is created and will persist in memory without being freed after the request ends. It will be called when the Web server process terminates, rather than being called by the kernel during the Mshutdown phase.

Let's start by defining the first callback function.

static void php_sample_descriptor_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC){    FILE *fp = (FILE*)rsrc->ptr;    fclose(fp);}

Then use it to replace the first parameter of the ZEND_REGISTER_LIST_DESTRUCTORS_EX () function null:

    le_sample_descriptor = zend_register_list_destructors_ex(    php_sample_descriptor_dtor,    NULL,    PHP_SAMPLE_DESCRIPTOR_RES_NAME,    module_number);

Now, if a resource variable of the above type is obtained in the script, it will be preprocessed by the kernel by invoking the underlying php_sample_descriptor_dtor when it is unset, or when the scope is freed by the kernel. In this way, it seems that we do not need sample_close () function at all!

<?php  $fp = sample_fopen("/home/jdoe/notes.txt", "r");  unset($fp);?>

Unset ($FP) executes, the kernel automatically calls the Php_sample_descriptor_dtor function to clean up some data corresponding to this variable. Of course, things are definitely not so simple, let's first remember this question and continue looking down.

Decoding Resources

We compare resource variables to bookmarks, but there is absolutely no effect if there are only bookmarks! We need to bookmark the corresponding page to find the line. For resource variables, we must be able to find the corresponding final data through it!

ZEND_FUNCTION(sample_fwrite){    FILE *fp;    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();    }    /* Use the zval* to verify the resource type and     * retrieve its pointer from the lookup table */    ZEND_FETCH_RESOURCE(fp,FILE*,&file_resource,-1,PHP_SAMPLE_DESCRIPTOR_RES_NAME,le_sample_descriptor);    /* Write the data, and     * return the number of bytes which were     * successfully written to the file */    RETURN_LONG(fwrite(data, 1, data_len, fp));}

The R placeholder in the Zend_parse_parameters () function represents the variable that receives the resource type, and its carrier is a zval*. Then let's take a look at the Zend_fetch_resource () macro function.

#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);//在我们的例子中,它是这样的:fp = (FILE*) zend_fetch_resource(&file_descriptor TSRMLS_CC, -1,PHP_SAMPLE_DESCRIPTOR_RES_NAME, NULL,1, le_sample_descriptor);if (!fp){    RETURN_FALSE;}

Zend_fetch_resource () is a layer of zend_hash_find (), which uses a numeric key to go to a hashtable that holds various {resources} to find the data that is ultimately needed, and then we use the Zend_verify_ The RESOURCE () macro function verifies this data. As we can see from the code above, NULL, 0 is absolutely not a resource.

In the above example, the Zend_fetch_resource () function first gets the resource type represented by Le_sample_descriptor, and if the resource does not exist or the Zval received is not a variable of the resource type, it returns null and throws the corresponding error message.

The final Zend_verify_resource () macro function automatically returns if an error is detected, and is the main logic that we can get out of the error detection and focus more on the program. Now that we've got the corresponding file*, let's use fwrite () to write the point data like this!

We can also get the data we want in the end by another way.

ZEND_FUNCTION(sample_fwrite){    FILE *fp;    zval *file_resource;    char *data;    int data_len, rsrc_type;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",&file_resource, &data, &data_len) == FAILURE ) {        RETURN_NULL();    }    fp = (FILE*)zend_list_find(Z_RESVAL_P(file_resource),&rsrc_type);    if (!fp || rsrc_type != le_sample_descriptor) {        php_error_docref(NULL TSRMLS_CC, E_WARNING,"Invalid resource provided");        RETURN_FALSE;    }    RETURN_LONG(fwrite(data, 1, data_len, fp));}

You can choose which form to use according to your own habits, but we recommend using the Zend_fetch_resource () macro function.

Forcing destruction

In the above we have a question not resolved, similar to our implementation of the unset ($FP) is really omnipotent? Of course not, look at the following code:

<?php  $fp = sample_fopen("/home/jdoe/world_domination.log", "a");  $evil_log = $fp;  unset($fp);?>

This f p < Span style= "Font-family:stixgeneral,"arial Unicode ms",serif; font-size:81%; Font-style:normal; Font-weight:normal; " > and Evil_log shared a zval, though F PwasInterpretationputthe,butis aitof thez vaLandNowill bewasInterpretationput,becauseto be Evil_log is still in use. In other words, now the file handle represented by $evil_log is still writable! So in order to avoid this error, we really need to manually come close it! The Sample_close () function must exist!

PHP_FUNCTION(sample_fclose){    FILE *fp;    zval *file_resource;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",&file_resource) == FAILURE ) {        RETURN_NULL();    }    /* While it‘s not necessary to actually fetch the     * FILE* resource, performing the fetch provides     * an opportunity to verify that we are closing     * the correct resource type. */    ZEND_FETCH_RESOURCE(fp, FILE*, &file_resource, -1,PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);    /* Force the resource into self-destruct mode */    zend_hash_index_del(&EG(regular_list),Z_RESVAL_P(file_resource));    RETURN_TRUE;}

This deletion also shows again that the resource data is stored in Hashtable. Although we can manipulate the hashtable of this storage resource through functions such as zend_hash_index_find () or Zend_hash_next_index_insert (), this is by no means a good idea because in subsequent releases, PHP may modify the implementation of this part of the way, until then the above method will not work, so for better compatibility, use standard macro functions or API functions.

When we delete the data in the EG (regular_list) Hashtable, the callback uses a dtor function, which calls the corresponding Dtor function implementation according to the category of the resource variable, that is, we call Zend_register_list_ The first argument of the DESTRUCTORS_EX () function.

82-Resource Resource type

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.