Parameters of function
The simplest way to obtain the parameters passed by the function caller is to use the zend_parse_parameters() function. We can directly generate the first few parameters of the zend_parse_parameters() function using macros in the kernel, in the form: ZEND_NUM_ARGS() TSRMLS_CC. Note that there is a space between the two, but there is no comma. As can be seen from the name, ZEND_NUM_ARGS() represents the number of parameters. The next parameter that needs to be passed to the zend_parse_parameters() function is a string used for formatting, just like the first parameter of printf. Several of the most commonly used symbols are represented below.
type_spec is a format string, its common meaning is as follows:
Parameter represents the type
b Boolean
l Integer integer type
d Floating point floating point type
s String string
r Resource resource
a Array array
o Object instance Object
O Object instance of a specified type Object of a specific type
z Non-specific zval Any type ~
Z zval** type
f Represents function and method names. It seems that there is no such thing in PHP5.1... .. .
This function is just like the printf() function. The following parameters correspond to the format in the format string one-to-one. Some basic types of data will be directly mapped to types in the C language.
ZEND_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 is: %ldn", foo);
RETURN_TRUE;
}
Generally speaking, the data of the two data types int and long are often the same, but there are exceptions. Therefore, we should not put the long array in an int, especially on a 64-bit platform, which will cause some bugs that are not easy to troubleshoot. Therefore, when receiving parameters through the zend_parse_parameter() function, we should use variables of the types agreed by the kernel as carriers.
Parameter corresponds to the data type in C
b zend_bool
l long
d double
s char*, int The former receives the pointer, the latter receives the length
r zval*
a zval*
o zval*
O zval*, zend_class_entry*
z zval*
Z zval**
Note that all composite type parameters in the PHP language require the zval* type as a carrier, because they are some data structures customized by the kernel. We must confirm that the type of the parameter and the carrier are consistent. If necessary, it can perform type conversion, such as converting the array into a stdClass object. The s and O (capital letter European) types need to be mentioned separately, because they both require two carriers. We will learn about the specific implementation of objects in PHP in the next chapters. So let's rewrite a function we defined in Chapter 5:
function sample_hello_world($name) { echo "Hello $name!n";
}
When writing extensions, we need to use zend_parse_parameters() To receive this string:
ZEND_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 the number of parameters passed to the function is less than the number of parameters to be received by zend_parse_parameters(), it The execution will fail and FAILURE will be returned.
If we need to receive multiple parameters, we can directly list the receiving carriers in the parameters of zend_parse_paramenters(), such as:
function sample_hello_world($name, $greeting) { echo "Hello $greeting $name !n";
}
sample_hello_world('John Smith', 'Mr.');
It should be implemented like this in the PHP extension:
ZEND_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 parameters defined above, there are The other three parameters are used to enhance our ability to receive parameters, as follows: Type Modifier Meaning
| The parameters before it are all necessary, and the following parameters are not necessary, that is, they have default values.
! If you receive a null variable in PHP language, it will be directly converted into NULL in C language instead of encapsulating it into a zval of IS_NULL type.
/ If the passed variable shares a zval with other variables and is not a reference, it will be forced to separate. The new zval’s is_ref__gc==0, and refcount__gc==1.
The default value of the function parameter
Now let’s continue Rewrite sample_hello_world(), next we use the default values of some parameters, which is like the following in php language:
function sample_hello_world($name, $greeting='Mr./Ms.') { echo " Hello $greeting $name!n";
}
sample_hello_world('Ginger Rogers','Ms.');
sample_hello_world('Fred Astaire');
At this time, you can only pass one parameter to sample_hello_world, or you can pass Complete two parameters. So how can we implement the same function in the extension function? We need to use the (|) parameter in zend_parse_parameters. The parameters before this parameter are considered necessary, and the parameters after this parameter are considered non-essential. If not passed, the carrier will not be modified.
ZEND_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");
}
If you do not pass the second parameter, the extension function will be considered as default without modifying the carrier. Therefore, we need to pre-set the value of the carrier ourselves, which is often NULL, or a value related to function logic. Each zval, including the IS_NULL type zval, needs to occupy a certain amount of memory space, and requires CPU computing resources to apply for memory for it, initialize it, and release it after they complete their work. But a lot of code doesn't realize this. There is a lot of code that wraps a null value into the IS_NULL type of zval. This operation can be optimized in extended development. We can receive the parameter as NULL in the C language. Let’s look at the following code on this issue:
ZEND_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) {
val = php_sample_make_defaultval(TSRMLS_C);
}
...
}
ZEND_FUNCTION(sample_arg_nullok) {
zval *val;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z! ",
val(TSRMLS_C);
}
}
At first glance, these two pieces of code don’t look like much. Different, but the first piece of code does require more cpu and memory resources. Maybe this technique is not very useful in normal times, but it is better to know it than not knowing it.
Forced Separation
When a variable is passed to a function, whether it is referenced or not, its refcoung__gc attribute will be incremented by one to at least 2. One copy is itself and the other is the copy passed to the function. Before changing this zval, sometimes it is necessary to divide it into two actual copies in advance. This is the role of the "/" format character. It will split the copy-on-write zval into two complete and independent copies in advance, so that we can operate on it at will in the following code. Otherwise, we may need to constantly remind ourselves to separate the received parameters and other operations. Like the NULL flag, this modifier goes after the type it means to impact. Also like the NULL flag, you won't know you need this feature until you actually have a use for it.
zend_get_arguments()
If you want If your extension is compatible with older versions of PHP, or if you only want to use zval as a carrier to receive parameters, you can consider using the zend_get_parameters() function to receive parameters. zend_get_parameters() is different from zend_parse_parameters(). As we can see from the name, it obtains directly without parsing. First of all, it will not automatically perform type conversion. The carriers of all parameters in the extended implementation need to be of zval type. Let us look at the simplest example:
ZEND_FUNCTION(sample_onearg) {
zval *firstarg;
if { Do something with firstarg.. . */
}
Secondly, zend_get_parameters() will not throw an error by itself when receiving fails, nor can it conveniently handle parameters with default values. The last point is that it is different from zend_parse_parameters in that it will automatically forcefully separate all zvals that comply with copy-on-write, and generate a new copy and send it to the function. If you want to use its other features but don't need this function, you can try using the zend_get_parameters_ex() function to receive parameters. In order not to separate the variables of copy-on-write, the parameters of zend_get_parameters_ex() are of type zval** instead of zval*. This function is not used very often. You may only think of it when you encounter some extreme problems, but it is very simple to use:
ZEND_FUNCTION(sample_onearg) {
zval **firstarg;
if (zend_get_parameters_ex(1, &firstarg) == FAILURE) {
WRONG_PARAM_COUNT;
}
/* Do something with firstarg... */
}
Note that zend_get_parameters_ex does not require ZEND_NUM_ARGS() as a parameter, because it is added at a later stage, and that parameter has already been No more.
The WRONG_PARAM_COUNT macro is also used in the above example. Its function is to throw an E_WARNING level error message and automatically return it.
Variable parameters
There are two other zend_get_parameter_** functions, which are specially used to solve the problem of many parameters or the number of parameters cannot be known in advance. Think about the usage of the var_dump() function in the PHP language. We can pass any number of parameters to it. Its implementation in the kernel is actually like this:
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 ) {
WRONG_PARAM_COUNT;
efree(args);
}
The program first obtains the number of parameters, and then applies for a corresponding size of memory through the safe_emalloc function to store these zval** type parameters. The zend_get_parameters_array_ex() function is used here to fill the parameters passed to the function into args. You may have immediately thought that there is also a function called zend_get_parameters_array(). The only difference is that it fills zval* type parameters into args and requires ZEND_NUM_ARGS() as a parameter.