14.6. The pval/zval Data TypeThroughout the PHP source code, you will see references to both pval and zval. They are the same thing and can be used interchangeably. The pval/zval is the basic data container in PHP. All data that is passed between the extension API and the user-level script is passed in this container. You can dig into the header files further yourself, but in simple terms, this container is a union that can hold either a long, a double, a string including the string length, an array, or an object. The union looks like this: typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object obj; } zvalue_value; The main things to learn from this union are that all integers are stored as longs, all floating-point values are stored in double-precision, and every string has an associated string length value, which, if properly checked everywhere, makes strings in PHP binary-safe.[9] Strings do not need to be null-terminated, but since most third-party libraries expect null-terminated strings it is a good idea to always null-terminate any string you create.
Along with this union, each container has a flag that holds the currently active type, whether it is a reference or not, and the reference count. So the actual pval/zval struct looks like this: struct _zval_struct { zvalue_value value; zend_uchar type; zend_uchar is_ref; zend_ushort refcount; }; Because this structure could change in future versions of PHP, be sure to use the various access functions and macros described in the following sections, rather than directly manipulating the container. 14.6.1. MAKE_STD_ZVAL( )The most basic of the pval/zval access macros provided by the extension API is the MAKE_STD_ZVAL( ) macro: zval *var; MAKE_STD_ZVAL(var); This does the following:
At this point, the container has no value—effectively, its value is null. In Section 14.6.4 section, we'll see how to set a container's value. 14.6.2. SEPARATE_ZVAL( )Another important macro is SEPARATE_ZVAL( ), used when implementing copy-on-write kinds of behavior. This macro creates a separate copy of a zval container only if the structure to be changed has a reference count greater than 1. A reference count of 1 means that nothing else has a pointer to this zval, so we can change it directly and don't need to copy off a new zval to change. Assuming a copy needs to be made, SEPARATE_ZVAL( ) decrements the reference count on the existing zval, allocates a new one, and does a deep copy of whatever value is stored in the original zval to the fresh copy. It then sets the reference count to 1 and is_ref to 0, just like MAKE_STD_ZVAL( ). 14.6.3. zval_copy_ctor( )If you just want to make a deep copy directly and manage your own reference counts, you can call the zval_copy_ctor( ) function directly. For example: zval **old, *new; *new = **old; zval_copy_ctor(new); Here old is a populated zval container; for example, a container passed to a function that we want to modify. Our rot13 example did this in a higher-level way, which we will explore next. 14.6.4. Accessor MacrosA large set of macros makes it easy to access fields of a zval. For example: zval foo; char *string; /* initialize foo and string */ Z_STRVAL(foo) = string; The Z_STRVAL( ) macro accesses the string field of a zval. There are accessor macros for every data type that can be stored in a zval. Because you often have pointers to zvals, and sometimes even pointers to pointers to zvals, each macro comes in three flavors, as shown in Table 14-1. Table 14-1. zval accessor macros
There are macros to identify the active type of a zval (or zval *, or zval **). They are Z_TYPE( ), Z_TYPE_P( ), and Z_TYPE_PP( ). The possible return values are:
The following code shows the rot13( ) function rewritten using low-level functions: PHP_FUNCTION(rot13) { zval **arg; char *ch, cap; int i; if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } SEPARATE_ZVAL(arg); convert_to_string_ex(arg); for(i=0, ch=Z_STRVAL_PP(arg); i<Z_STRLEN_PP(arg); i++, ch++) { cap = *ch & 32; *ch &= ~cap; *ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26+'A') : *ch) | cap; } RETURN_STRINGL(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), 1); } Rather than using the handy zend_parse_parameters( ) function, we fetch the zval directly using zend_get_parameters_ex( ). We then create a separate copy so that we can modify this copy without changing the passed container directly. Then we return it. Note that this is not an improvement on our function, merely a rewrite to show how you might use the various accessor macros. Here's an even lower-level approach that skips the SEPARATE_ZVAL( ) approach and goes right to a zval_copy_ctor( ): PHP_FUNCTION(rot13) { zval **arg; char *ch, cap; int i; if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } *return_value = **arg; zval_copy_ctor(return_value); convert_to_string(return_value); for(i=0, ch=return_value->value.str.val; i<return_value->value.str.len; i++, ch++) { cap = *ch & 32; *ch &= ~cap; *ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26 + 'A') : *ch) | cap; } } The value returned from a PHP function is returned in a special zval container called return_value, which is automatically allocated. In the example, we assign return_value to the passed arg container, call zval_copy_ctor( ) to make a copy, and ensure that we convert the data to a string. We also skipped the zval dereferencing convenience macros Z_STRVAL_PP( ) and Z_STRLEN_PP( ) and instead dereferenced the return_value zval container manually. Going this low-level is not recommended, however, as changes in the underlying data structures could break your extension. Copyright © 2003 O'Reilly & Associates. All rights reserved. |
|