Home  >  Article  >  Backend Development  >  PHP extension and embedding--Parameters of PHP extension_PHP tutorial

PHP extension and embedding--Parameters of PHP extension_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 10:42:24990browse

In previous articles, functions are relatively simple in terms of the parameters they receive and the types they return, but what is often encountered in practice is more complex. This article mainly talks about how to receive parameters from user space in PHP extension development, and check the type, number and other information of these parameters accordingly.


1. Use zend_parse_parameters() for automatic type conversion

In PHP extensions, the easiest way to get input parameters is to use the zend_parse_parameters() function.

The first argument to a call to this function is always: ZEND_NUM_ARGS() TSRMLS_CC. This argument returns the number of input arguments as an int.
The second parameter is the format parameter, which is composed of string types, corresponding to different types supported by Zend Engine.
The following figure shows the possible types of the format parameter:
The following parameters depend on the type requested previously. For relatively simple types, this parameter is usually a referenced primitive, as shown in the following example:
PHP_FUNCTION(sample_getlong)
{
    long foo;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
                         "l", &foo) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("The integer value of the parameter you "
             "passed is: %ld\n", foo);
    RETURN_TRUE;
}
Here is l, which is a long type, so accordingly a long foo parameter is declared in advance, and then the value is passed in by reference. The following gives a more detailed correspondence between parameters and types in the C language: b ------ zend_bool l ------- long d ------- double s ------- char* , int r ------- zval* a ------ zval* o ------ zval* O ----- zval*, zend_class_entry* z ------ zval* Z ----- zval**
Note that for complex types, simple zval* types are used. This is the same as the reason why there is no RETURN_* when returning a complex type. What ZPP does is ensure that the received zval* is of the correct type. If necessary, it will also perform implicit conversions, such as converting arrays to stdClass objects.
For the s type, it is quite special, one char* one int. This is mainly because of the special structure of strings in php:
function sample_hello_world($name) {
    echo "Hello $name!\n";
}
In C language, the zend_parse_parameters function is used:
PHP_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
                        &name, &name_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}

If there are multiple parameters, zend_parse_parameters will extract these parameters from left to right:
function sample_hello_world($name, $greeting) {
    echo "Hello $greeting $name!\n";
}
sample_hello_world('John Smith', 'Mr.');

Or:
PHP_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;
    char *greeting;
    int greeting_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
      &name, &name_len, &greeting, &greeting_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(greeting, greeting_len);
    php_printf(" ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}

In addition to the type identifier, there are three metacharacters that modify how parameters are processed: | : If you see it, it means that the previous parameters are required and the following parameters are optional! : If a null variable in the PHP language is received, it will be directly converted to NULL in the C language instead of Encapsulated into zval
of type IS_NULL /: If the passed variable shares a zval with other variables and is not a true reference, it must be forcibly separated. The new zval's is_ref__gc = 0, refcount__gc = 1 Optional parameters: Default values ​​can be provided for parameters in php:
function sample_hello_world($name, $greeting='Mr./Ms.') {
    echo "Hello $greeting $name!\n";
}
At this time, when calling, you do not need to provide the second parameter:
sample_hello_world('Ginger Rogers','Ms.');
sample_hello_world('Fred Astaire');

In the C interpretation, there is a similar implementation:
PHP_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;
    char *greeting = "Mr./Mrs.";
    int greeting_len = sizeof("Mr./Mrs.") - 1;//给定默认值,找出默认的长度
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
      &name, &name_len, &greeting, &greeting_len) == FAILURE) {//特殊的元字符|立刻就用上了
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(greeting, greeting_len);
    php_printf(" ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}

Optional parameters generally have no value unless specified, so it is important to provide default parameters. In most cases it is NULL/0


IS_NULL VS NULL:
Each zval type, even the simplest IS_NULL type, occupies a certain amount of memory space, and it also takes time to apply for and release them. Therefore, there is no need to use this type in many cases. The comparison is given in the following two pieces of code:

PHP_FUNCTION(sample_arg_fullnull)
{
    zval *val;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",
                                    &val) == FAILURE) {
        RETURN_NULL();
    }
    if (Z_TYPE_P(val) == IS_NULL) {//使用zval检查为空的方式
        val = php_sample_make_defaultval(TSRMLS_C);
    }
...
PHP_FUNCTION(sample_arg_nullok)
{
    zval *val;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!",
                                    &val) == FAILURE) {
        RETURN_NULL();
    }
    if (!val) {// c语言风格的检查为空的方式
        val = php_sample_make_defaultval(TSRMLS_C);
    }
...

Forced Seperation:

When a variable is passed into a function, regardless of whether it is passed by reference or not, the refcount is always at least 2. One is itself and the other is the copy passed into the function. Before making changes to this zval, it is necessary to separate it from a non-referenced collection.
It is very convenient to use /, which will automatically separate any variables referenced by copy-on-write (that is, false references).
This feature is the same as the NULL flag and is only used when needed.


zend_get_parameters():
If you want to be compatible with older versions of PHP or just want to use zval as a carrier to receive parameters, you can consider using the zend_get_parameters() function to receive parameters
Compared with zend_parse_parameters(), it is obtained directly without parsing. Type conversion will not be performed automatically. The carriers of all parameters in the extended implementation are zval.

ZEND_FUNCTION(sample_onearg) {
      zval *firstarg;
    if (zend_get_parameters(ZEND_NUM_ARGS(), 1, &firstarg)== FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,"Expected at least 1 parameter.");
        RETURN_NULL();
    }
    /* Do something with firstarg... */
}

At the same time, it will not throw an error when receiving fails, nor can it handle parameters with default values. The last difference from parse is that it will automatically forcefully separate all zvals that comply with copy-on-write. Generate a fresh copy and send it inside the function.
If you don’t need this function, you can use zend_get_parameters_ex() whose parameters are zval**

ZEND_FUNCTION(sample_onearg) {
    zval **firstarg;
    if (zend_get_parameters_ex(1, &firstarg) == FAILURE) {
        WRONG_PARAM_COUNT;抛出一个E_WARNING级别的错误信息,并自动return。    }
    /*



Variable parameters, handle any number of parameters:

There are also two zend_get_parameters_** functions, which are specially used to solve situations where there are many or the number of parameters cannot be known in advance. The var_dump() function in the PHP language can input any number of parameters.

ZEND_FUNCTION(var_dump) {
    int i, argc = ZEND_NUM_ARGS();
    zval ***args;
 
    args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
    if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE) {
        efree(args);
        WRONG_PARAM_COUNT;
    }
    for (i=0; i<br>
程序首先获取参数数量,然后通过safe_emalloc函数申请相应大小的内存来存储这些zval**的参数。这里使用zend_get_parameters_array_ex()函数来把传递给函数的参数填充到args中。提醒一下,还存在一个zend_get_parameters_array()函数,唯一不同是它将zval*类型的参数填充到args中,并且需要ZEND_NUM_ARGS()作为参数。<br>
</p>
<p><br>
</p>
<p><br>
</p>
<p><strong>2. Arg info参数和类型的绑定</strong></p>
<p>这个arg info结构是ZE2才有的。每一个arg info声明都由一个ZEND_BEGIN_ARG_INFO()或ZEND_BEGIN_ARG_INFO_EX()宏组成,后面跟着0个或多个ZEND_ARG_*INFO(), 然后最后以ZEND_END_ARG_INFO()作为结尾。<br>
假定要重写count()函数:<br>
</p>
<p><pre class="code">PHP_FUNCTION(sample_count_array)
{
    zval *arr;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a",
                                    &arr) == FAILURE) {
        RETURN_NULL();
    }
    RETURN_LONG(zend_hash_num_elements(Z_ARRVAL_P(arr)));
}

zend_parse_parameters()会确保输入到你函数中的参数是一个数组。但是如果你需要用zend_get_parameter()的话,那么就需要自己在函数内部内建类型检查。除非使用类型绑定:

ZEND_BEGIN_ARG_INFO(php_sample_array_arginfo, 0)
         ZEND_ARG_ARRAY_INFO(0, "arr", 0)
     ZEND_END_ARG_INFO()
。。。     PHP_FE(sample_count_array, php_sample_array_arginfo)  。。。

通过这种方式,zend engine就会帮你进行类型检查了。同时还给了参数一个名字,从而使得产生的错误信息更加具有可读性。

而对于对象来说,也可以通过arg info进行限定:

ZEND_BEGIN_ARG_INFO(php_sample_class_arginfo, 0)
         ZEND_ARG_OBJECT_INFO(1, "obj", "stdClass", 0)
     ZEND_END_ARG_INFO()

这里第一个参数被设为1,表示是引用方式传递,但是对象其实在ZE2中都是引用传递的。不要忘记了array和object的allow_null选项。
如果使用的是php4的话,只能用PHP_TYPE_P()进行检查,或使用convert_to_type()方法进行类型转换。









www.bkjia.comtruehttp://www.bkjia.com/PHPjc/635059.htmlTechArticle之前的文章中,函数在接收的参数和返回的类型上都比较简单,但是往往实际中所遇到的都更加复杂一些。这篇文章主要说一下如何在php扩...
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