I publish a topic for which I received an invite to Habr =)
I have long thought to share the experience of developing extensions for PHP, but I forgot all the time =)
Now, seeing Habratopik about the basics of creating extensions for PHP in VS2008, I finally decided to do it.
Since the basics were outlined in this topic, I will immediately move on to more subtle points.
')
Text output
If you want to display text, instead of the standard printf () function, you should use the function zend_printf (). If the script that called us is running to process an HTTP request, the information output via zend_printf () will be sent directly to the client. When you run php in standalone mode, it will display text on the screen.
Passing function parameters by reference
Since starting PHP 5.3.0, passing the function argument by reference when calling a function is deprecated, the argument should be declared as passed by reference in the function declaration. In the case when we declare a function in PHP, everything is simple - it is enough to put an ampersand in front of the argument name, for example like this:
function ReadData($id,&$data)
If we declare a function in an extension, everything is somewhat more complicated.
First you need to describe the function arguments:
ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)
ZEND_ARG_INFO(pass_by_ref, name)
...
ZEND_END_ARG_INFO()
Macro parameters ZEND_BEGIN_ARG_INFO_EX ():
name - The name of the structure that describes this argument list. Must be unique. For example, for the ReadData () function, the structure can be called arginfo_readdata.
required_num_args - The number of required function arguments.
pass_rest_by_reference - Whether the remaining (optional) arguments should be passed by reference (0 - no, 1 - yes).
return_reference - Whether the return value is passed by reference.
Macro parameters ZEND_ARG_INFO ():
pass_by_ref - Whether to pass this argument by reference.
name - The name of the argument.
All arguments are listed in the same order in which they are passed to the function.
An example of declaring the same ReadData function:
ZEND_BEGIN_ARG_INFO_EX(arginfo_readdata,0,0,2)
ZEND_ARG_INFO(0,id)
ZEND_ARG_INFO(1,data)
ZEND_END_ARG_INFO()
After this, when declaring a function, it suffices to specify the name of our argument description structure as the second parameter of the PHP_FE () macro. For example:
PHP_FE(readdata,arginfo_readdata)
What is zval and what it is eaten with
zval is a structure containing a PHP variable representation. It can contain both a scalar value (number, string, etc.), and an array or a resource handle.
If we want to accept an extension function as an argument or return an array from a function, we will have to work with this particular zval.
We get zval
For example, we want to get an associative array as a function argument and get values from it for some indices.
To do this: declare a pointer to zval; when calling the zend_parse_parameters function, specify the type of the argument “a”; pass it the pointer to the declared pointer to zval.
Immediately sample code:
PHP_FUNCTION(func)
{
zval *arr;
char buf[128];
unsigned long x,y;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a" , &arr)==FAILURE) {
WRONG_PARAM_COUNT;
}
...
}
* This source code was highlighted with Source Code Highlighter .
Get the values from the array
Let's continue the function from the previous example, get the values in the x and y indices from the array passed to us, then form and output a string like "x = ..., y = ...". In this example, these values will be of type long, but with minor modifications, it will also be possible to get other types.
PHP_FUNCTION(func)
{
zval *arr,**val;
char buf[128];
unsigned long x,y;
HashTable *hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a" , &arr)==FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_P(arr)!=IS_ARRAY) {
zend_error(E_ERROR, "%s(): Supplied argument is not an array." ,get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
hash=Z_ARRVAL_P(arr);
if (zend_hash_find(hash, "x" ,1,( void **)&val)!=SUCCESS) {
zend_error(E_ERROR, "%s(): There is no x index." ,get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
if (Z_TYPE_PP(val)!=IS_LONG) {
zend_error(E_ERROR, "%s(): Wrong type of x." ,get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
x=Z_LVAL_PP(val);
if (zend_hash_find(hash, "y" ,1,( void **)&val)!=SUCCESS) {
zend_error(E_ERROR, "%s(): There is no y index." ,get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
if (Z_TYPE_PP(val)!=IS_LONG) {
zend_error(E_ERROR, "%s(): Wrong type of y." ,get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
y=Z_LVAL_PP(val);
zend_printf( "x = %u, y = %u\n" ,x,y);
}
* This source code was highlighted with Source Code Highlighter .
In this example, Zend macros are widely used.
Z_TYPE _ * () returns the type contained in the zval passed to it. Values, as you might guess, are of the form IS_type. For example, IS_ARRAY or IS_STRING.
Z_ARRVAL _ * () returns a pointer to a HashTable structure, which is an internal representation of the PHP array.
Z_LVAL _ * () returns the number contained in the given zval.
* - can be P or PP, depending on whether we are passing a pointer to a zval or a pointer to a pointer to a zval.
The zend_hash_find (zval *) function searches the specified HashTable for the specified index.
The rest should be clear.
The given code is, of course, ugly and non-optimal, it is here for example only =)
Conclusion
I don’t know how interesting this topic is for the habrasoobshchestvo, so for now I’m not going to paint further.
If the topic turns out to be interesting, I will be happy to talk about other subtleties of creating functions in extensions.
I will be glad to hear any comments on the topic and style, all the same this is my first habratopik =)