The Way to Advance PHP - The Implementation of Third Party Extension with Resource Packaging in PHP 7

Keywords: PHP shell vim

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

Posted by hthighway on Thu, 13 Jun 2019 18:23:43 -0700