Programming PHPProgramming PHPSearch this book

14.6. The pval/zval Data Type

Throughout 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.

[9]Binary-safe, sometimes referred to as 8-bit clean, means that a string can contain any of the 256 ASCII values, including the ASCII value 0.

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.4. Accessor Macros

A 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

Long

Boolean

Double

String value

String length

Z_LVAL( )
Z_BVAL( )
Z_DVAL( )
Z_STRVAL( )
Z_STRLEN( )
Z_LVAL_P( )
Z_BVAL_P( )
Z_DVAL_P( )
Z_STRVAL_P( )
Z_STRLEN_P( )
Z_LVAL_PP( )
Z_BVAL_PP( )
Z_DVAL_PP( )
Z_STRVAL_PP( )
Z_STRLEN_PP( )

HashTable

Object

Object properties

Object class entry

Resource value

Z_ARRVAL( )
Z_OBJ( )
Z_OBJPROP( )
Z_OBJCE( )
Z_RESVAL( )
Z_ARRVAL_P( )
Z_OBJ_P( )
Z_OBJPROP_P( )
Z_OBJCE_P( )
Z_RESVAL_P( )
Z_ARRVAL_PP( )
Z_OBJ_PP( )
Z_OBJPROP_PP( )
Z_OBJCE_PP( )
Z_RESVAL_PP( )

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:

  • IS_LONG

  • IS_BOOL

  • IS_DOUBLE

  • IS_STRING

  • IS_ARRAY

  • IS_OBJECT

  • IS_RESOURCE

  • IS_NULL

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.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.