Complex data types that are stored in resource variables typically require some memory allocation, CPU time, or network traffic when initializing. However, it is necessary to persist a resource that is similar to a database connection between requests. The sustainability of resources is a factor that must be taken into account.
first look at the memory allocation problem:
When using PHP, the bias is to use emalloc because it is a recycled version of malloc. However, persistent resources must exist between requests. For a resource of a file handle class, if you want to add a requirement to store the file name, you must include the following code in the header file:
typedef struct _PHP_SAMPLE_DESCRIPTOR_DATA { char *filename; FILE *FP;} Php_sample_descriptor_data;
This structure allows you to store file names and file handle resources so that they can be shared between different requests.
To make the corresponding changes in the source file:
static void Php_sample_descriptor_dtor (//This is a callback function for resource recycling, defined at the initialization of the resource. 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 for resource reclamation and requires a callback to be specified at the time of initialization of the resource.
After the modified file opening function, you need to increase the operation of the resource allocation space:
Php_function (Sample_fopen)//modified 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) {//Get filename and file length 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; }fdata = emalloc (sizeof (php_sample_descriptor_data));//Assign space FDATA->FP = FP to the structure that contains the file resource and filename; Fdata->filename = estrndup (filename, filename_len);Zend_register_resource (Return_value, Fdata, le_sample_descriptor); Register Resource}
For file write functions fwrite also need 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 (); } Zend_fetch_resource (Fdata, php_sample_descriptor_data*, &file_resource,-1, Php_sample_ Descriptor_res_name, le_sample_descriptor);
For the Sample_fclose function there is no need to change anything because it does not manipulate the 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 (); } Zend_fetch_resource (Fdata, php_sample_descriptor_data*, &file_resource,-1, Php_sample_ Descriptor_res_name, le_sample_descriptor);
After the memory allocation has been completed, the destructor must be deferred because it must be persisted:
For non-persistent resources, once the variables that hold the resource ID are unset or fallen out of scope, they are removed from the eg (regular_list). The index used in eg (persistent_list) is the key value class, and the element will not be automatically removed at the end of the request. This is only eliminated if the Zend_hash_del () call or thread/process is completely closed.
EG (Persistent_list) also has a dtor method, but it is the second parameter of ZEND_REGISTER_LIST_DESCRUCTORS_EX (). In general, non-persistent and persistent resources are registered as two types, sometimes in a merged form. Now add a persistent resource type to the SAMPLE.C.
static int le_sample_descriptor_persist; static void Php_sample_descriptor_dtor_persistent ( zend_rsrc_list_entry *rsrc tsrmls_dc) {//This is a persisted resource destructor Php_sample_descriptor_data *fdata = (php_sample_descriptor_data*) rsrc->ptr; fclose (FDATA->FP); Pefree (Fdata->filename, 1); Pefree (Fdata, 1);} Php_minit_function (sample) { Le_sample_descriptor = zend_register_list_destructors_ex ( php_sample _descriptor_dtor, NULL, php_sample_descriptor_res_name, module_number); le_sample_descriptor_persist = zend_register_list_destructors_ex ( NULL, Php_sample_descriptor_ Dtor_persistent, php_sample_descriptor_res_name, module_number);//Register a persistent resource
The following fopen function is compatible with two resource types that are persistent and non-persistent:
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, &A Mp;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) {///non-persisted resource fdata = Emalloc (sizeof (php_sample_descriptor_data)); Fdata->filename = estrndup (filename, filename_len);//This makes the application memory and assigns a value of two step operation FDATA->FP = FP; Zend_register_resource (Return_value, Fdata, Le_sample_descriptor); } else {//persistent resource 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 stores a replica in 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_e ntry), NULL); Efree (Hash_key); }}
For non-persisted resources, the index of a number is given and stored in a list that is dependent on the request.
For a persisted resource, given a key-value type, the HashKey can be re-obtained in the subsequent request. And then put the resources into the persistentlist. When a persistent resource is out of scope, the destructor of EG (Regular_list) examines registerlist for le_sample_descriptro_persist. If found to be null, there will be no action. This ensures that the persistent resources are not released. When a resource is removed from eg (persistent_list), either the thread process ends or is deliberately deleted. This is where you'll find the persistence destructor.
The reason the resource is requested for persistence is to be reused in other requests:
If you want to reuse persistent resources, you must use Hash_key, and when Sample_fopen is called, the function will recreate the Hash_key with the requested file name and schema, and then try to find it in 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; Judging whether it lasts list_entry *existing_file; if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "ss|b", &filename, &filename_len, &mode, &A Mp;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; }/* By getting a hash_key try to find an already open file */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_fil e) = = SUCCESS) {/* successfully found this open file handle resource */Zend_register_resource (Return_value, Existing_file-> ;p TR, le_sample_descriptor_persist); Efree (Hash_key); Return } 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 have already been created by now */ Zend_hash_update (&eg (persistent_list), Hash_key, Hash_key_len + 1, (void*) &le, sizeof (l Ist_entry), NULL); } efree (Hash_key);}
Note Because all extensions use the same hash table to store resources, naming is important. The extension and resource type names are generally used as prefixes.
Check the availability of resources:
Although resources such as files can be opened for a long time, similar to remote network resources is problematic if there is a long-term need between requests. So before you can use a persistent resource, 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);//This will call the previously registered destructor}