PHP Extension Development Articles, I have updated to< TIPI>
Before reading the following, let's assume that you are right. PHP 7 Basic Data Structure They all have a general understanding, which is the premise of reading the following content.
We are divided into two parts:
Firstly, it implements a custom file operation expansion of opening, reading, writing and closing.
Then the implementation principle behind each operation is analyzed, and the implementation of some parts of it will be compared with the use of resources in PHP 5.3.
Generating Extended Skeleton by Prototype
First enter the ext directory of the source directory and add a prototype file for file operation
[shell] [root@localhost php-src-php-7.0.3]# cd ext/ [root@localhost ext]# vim tipi_file.proto
Edited as
[shell] resource file_open(string filename, string mode) string file_read(resource filehandle, int size) bool file_write(resource filehandle, string buffer) bool file_close(resource filehandle)
And then generate the skeleton, as we said before, let's not go into detail.
[shell] [root@localhost ext]# ./ext_skel --extname=tipi_file --proto=./tipi_file.proto
Complete code tipi_file.c You can have a general understanding first, so that when you read later, you may have a much clearer idea.
Registered resource type
Understanding API of Registered Resource Types
[c] ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, const char *type_name, int module_number)
parameter | describe |
---|---|
ld | The function that is called when the resource is released. |
pld | Release functions for persistent resources that always exist in different requests. |
type_name | String aliases for descriptive type names. |
module_number | For internal use of the engine, it has been defined, such as in the PHP_FUNCTION macro. |
The API returns a resource type id that should be stored as a global variable in the extension to be passed to other resource APIs when necessary.
Add resource release callback function
This method indicates that the open file descriptor needs to be closed when releasing this type of resource.
[c] static void tipi_file_dtor(zend_resource *rsrc TSRMLS_DC){ FILE *fp = (FILE *) rsrc->ptr; fclose(fp); }
We find that the parameter type of the function is zend_resource. This is a new data structure for PHP 7, and zend_rsrc_list_entry for PHP 5. We'll leave the details behind.
Register resource types in PHP_MINIT_FUNCTION
We know that in the PHP life cycle, when PHP is loaded, PHP_MINIT_FUNCTION (module startup function) is called by the engine.
This allows the engine to do some initialization such as resource type, registering INI variables, etc.
So we need to register the resource type here by zend_register_list_destructors_ex in PHP_MINIT_FUNCTION.
[c] PHP_MINIT_FUNCTION(tipi_file) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ le_tipi_file = zend_register_list_destructors_ex(tipi_file_dtor, NULL, TIPI_FILE_TYPE, module_number); return SUCCESS; }
NOTICE, where TIPI_FILE_TYPE has been defined earlier, is an alias for the extension. Refer specifically to the complete code sample given at the beginning of this section.
Registered resources
The first step is to register a new resource type, and then you need to register a resource of that type.
Registered Resource API
In PHP 7, the original ZEND_REGISTER_RESOURCE macro was deleted and the zend_register_resource function was used directly.
[c] ZEND_API zend_resource* zend_register_resource(void *rsrc_pointer, int rsrc_type)
parameter | describe |
---|---|
rsrc_pointer | Resource Data Pointer |
rsrc_type | The resource type id obtained when registering the resource type |
Implementation of Resource Registration in the Self-defined file_open Function
[c] PHP_FUNCTION(file_open) { char *filename = NULL; char *mode = NULL; int argc = ZEND_NUM_ARGS(); size_t filename_len; size_t mode_len; #ifndef FAST_ZPP if (zend_parse_parameters(argc TSRMLS_CC, "ss", &filename, &filename_len, &mode, &mode_len) == FAILURE) return; #else ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STRING(filename, filename_len) Z_PARAM_STRING(mode, mode_len) ZEND_PARSE_PARAMETERS_END(); #endif // Replacing Standard C File Operating Function with VCWD Macro FILE *fp = VCWD_FOPEN(filename, mode); if (fp == NULL) { RETURN_FALSE; } RETURN_RES(zend_register_resource(fp, le_tipi_file)); }
The function of the RETURN_RES macro is to add the returned zend_resource to zval, and then take the last zval as the return value. That is to say, the return value of the function is a zval pointer. RETURN_RES (zend_register_resource (fp, le_tipi_file) sets the value.res of the return value to FP and u1.type_info to IS_RESOURCE_EX. You can learn from the source code very intuitively, here do not paste code details.
Use of resources
Use resource API
In PHP 7, the original ZEND_FETCH_RESOURCE macro is deleted, and the function zend_fetch_resource is used directly, and the parsing method is much simpler. Compared with PHP 5, it is much more efficient. Later, we will analyze and compare it by drawing.
[c] ZEND_API void *zend_fetch_resource(zend_resource *res, const char *resource_type_name, int resource_type)
parameter | describe |
---|---|
res | Resource pointer |
resource_type_name | String aliases for such resources |
resource_type | Type id of this kind of resource |
Implementation of parsing resources
When we want to read a file, we still need to use the native fread function, so we need to parse zend_resource into the original FILE* pointer of the resource package through zend_fetch_resource.
[c] PHP_FUNCTION(file_read) { int argc = ZEND_NUM_ARGS(); int filehandle_id = -1; zend_long size; zval *filehandle = NULL; FILE *fp = NULL; char *result; size_t bytes_read; #ifndef FAST_ZPP if (zend_parse_parameters(argc TSRMLS_CC, "rl", &filehandle, &size) == FAILURE) return; #else ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_RESOURCE(filehandle) Z_PARAM_LONG(size) ZEND_PARSE_PARAMETERS_END(); #endif if ((fp = (FILE *)zend_fetch_resource(Z_RES_P(filehandle), TIPI_FILE_TYPE, le_tipi_file)) == NULL) { RETURN_FALSE; } result = (char *) emalloc(size+1); bytes_read = fread(result, 1, size, fp); result[bytes_read] = '\0'; RETURN_STRING(result, 0); }
It should be noted here that ZEND_FETCH_RESOURCE is still used in the extension code automatically generated by the script, which is a BUG because the script automatically generated (ext/skeleton/create_stubs) has not been updated.
Similar to the write operation of a similar file, the code will not be copied here.
Deletion of resources
Resource Delete API
[c] ZEND_API int zend_list_close(zend_resource *res)
Enter the resources that need to be deleted. The API seems to be very simple, and a lot of work has been done in practice. The following principles are analyzed in detail.
Implementation of resource deletion
We need to call the resource deletion API in the function file_close
[c] PHP_FUNCTION(file_close) { int argc = ZEND_NUM_ARGS(); int filehandle_id = -1; zval *filehandle = NULL; #ifndef FAST_ZPP if (zend_parse_parameters(argc TSRMLS_CC, "r", &filehandle) == FAILURE) return; #else ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_RESOURCE(filehandle) ZEND_PARSE_PARAMETERS_END(); #endif zend_list_close(Z_RES_P(filehandle)); RETURN_TRUE; }
Compile, install and test
For compiled code, please refer to the first section of this chapter, which will not be explained here. Let's talk about testing. To test directly with PHP script, we can write test samples for each function and modify the tipi_file.php file.
[php] $fp = file_open("./CREDITS","r+"); var_dump($fp); var_dump(file_read($fp,6)); var_dump(file_write($fp,"zhoumengakng")); var_dump(file_close($fp));
Then execute from the command line
[shell] php7 -d"extension=tipi_file.so" tipi_file.php