php-doc-en/internals/zendapi/arguments.xml
Gabor Hojtsy 811a7db395 New internals documentation section added:
- PHP 3 development appendix moved to this part
 - Streams API docs moved to this part
 - ZendAPI docs with figures integrated in this part

More info: http://news.php.net/php.doc/969369673


git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@194089 c90b9560-bf6c-de11-be94-00142212c4b1
2005-08-21 16:27:09 +00:00

1014 lines
39 KiB
XML

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!-- $Revision: 1.1 $ -->
<sect1 id="zend.arguments">
<title>Accepting Arguments</title>
<para>
One of the most important issues for language extensions is
accepting and dealing with data passed via arguments. Most
extensions are built to deal with specific input data (or require
parameters to perform their specific actions), and function
arguments are the only real way to exchange data between the PHP
level and the C level. Of course, there's also the possibility of
exchanging data using predefined global values (which is also
discussed later), but this should be avoided by all means, as it's
extremely bad practice.
</para>
<para>
PHP doesn't make use of any formal function declarations; this is
why call syntax is always completely dynamic and never checked for
errors. Checking for correct call syntax is left to the user code.
For example, it's possible to call a function using only one
argument at one time and four arguments the next time - both
invocations are syntactically absolutely correct.
</para>
<sect2 id="zend.arguments.count">
<title>Determining the Number of Arguments</title>
<para>
Since PHP doesn't have formal function definitions with support
for call syntax checking, and since PHP features variable
arguments, sometimes you need to find out with how many arguments
your function has been called. You can use the
<literal>ZEND_NUM_ARGS</literal> macro in this case. In previous
versions of PHP, this macro retrieved the number of arguments with
which the function has been called based on the function's hash
table entry, <envar>ht</envar>, which is passed in the
<literal>INTERNAL_FUNCTION_PARAMETERS</literal> list. As
<envar>ht</envar> itself now contains the number of arguments that
have been passed to the function, <literal>ZEND_NUM_ARGS</literal>
has been stripped down to a dummy macro (see its definition in
<filename>zend_API.h</filename>). But it's still good practice to
use it, to remain compatible with future changes in the call
interface. <emphasis>Note:</emphasis> The old PHP equivalent of
this macro is <literal>ARG_COUNT</literal>.
</para>
<para>
The following code checks for the correct number of arguments:
<programlisting>
if(ZEND_NUM_ARGS() != 2) WRONG_PARAM_COUNT;
</programlisting>
If the function is not called with two
arguments, it exits with an error message. The code snippet above
makes use of the tool macro <literal>WRONG_PARAM_COUNT</literal>,
which can be used to generate a standard error message (see <xref linkend='fig.wrong-param-count'/>).
</para>
<figure id='fig.wrong-param-count'>
<title><literal>WRONG_PARAM_COUNT</literal> in action.</title>
<graphic fileref="figures/zend.03-wrong-parameter-count.png"/>
</figure>
<para>
This macro prints a default error message and then returns to the caller.
Its definition can also be found in <filename>zend_API.h</filename> and looks
like this:
<programlisting>
ZEND_API void wrong_param_count(void);
#define WRONG_PARAM_COUNT { wrong_param_count(); return; }
</programlisting>
As you can see, it calls an internal function
named <function>wrong_param_count</function> that's responsible for printing
the warning. For details on generating customized error
messages, see the later section "Printing Information."
</para>
</sect2>
<sect2 id="zend.arguments.retrieval">
<title>Retrieving Arguments</title>
<note>
<title>
New parameter parsing API
</title>
<para>
This chapter documents the new Zend parameter parsing API
introduced by Andrei Zmievski. It was introduced in the
development stage between PHP 4.0.6 and 4.1.0 .
</para>
</note>
<para>
Parsing parameters is a very common operation and it may get a bit
tedious. It would also be nice to have standardized error checking
and error messages. Since PHP 4.1.0, there is a way to do just
that by using the new parameter parsing API. It greatly simplifies
the process of receiving parameteres, but it has a drawback in
that it can't be used for functions that expect variable number of
parameters. But since the vast majority of functions do not fall
into those categories, this parsing API is recommended as the new
standard way.
</para>
<para>
The prototype for parameter parsing function looks like this:
<programlisting>
int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...);
</programlisting>
The first argument to this function is supposed to be the number
of actual parameters passed to your function, so
<literal>ZEND_NUM_ARGS()</literal> can be used for that. The
second parameter should always be <literal>TSRMLS_CC</literal>
macro. The third argument is a string that specifies the number
and types of arguments your function is expecting, similar to how
printf format string specifies the number and format of the output
values it should operate on. And finally the rest of the arguments
are pointers to variables which should receive the values from the
parameters.
</para>
<para>
<function>zend_parse_parameters</function> also performs type
conversions whenever possible, so that you always receive the data
in the format you asked for. Any type of scalar can be converted
to another one, but conversions between complex types (arrays,
objects, and resources) and scalar types are not allowed.
</para>
<para>
If the parameters could be obtained successfully and there were no
errors during type conversion, the function will return
<literal>SUCCESS</literal>, otherwise it will return
<literal>FAILURE</literal>. The function will output informative
error messages, if the number of received parameters does not
match the requested number, or if type conversion could not be
performed.
</para>
<para>
Here are some sample error messages:
<screen>
Warning - ini_get_all() requires at most 1 parameter, 2 given
Warning - wddx_deserialize() expects parameter 1 to be string, array given
</screen>
Of course each error message is accompanied by the filename and
line number on which it occurs.
</para>
<para>
Here is the full list of type specifiers:
<itemizedlist>
<listitem>
<para><literal>l - </literal>long</para>
</listitem>
<listitem>
<para><literal>d - </literal>double</para>
</listitem>
<listitem>
<para><literal>s - </literal>string (with possible null bytes) and its length</para>
</listitem>
<listitem>
<para><literal>b - </literal>boolean</para>
</listitem>
<listitem>
<para><literal>r - </literal>resource, stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>a - </literal>array, stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>o - </literal>object (of any class), stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>O - </literal>object (of class specified by class entry), stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>z - </literal>the actual <literal>zval*</literal></para>
</listitem>
</itemizedlist>
The following characters also have a meaning in the specifier
string:
<itemizedlist>
<listitem>
<para>
<literal>| - </literal>indicates that the remaining
parameters are optional. The storage variables
corresponding to these parameters should be initialized to
default values by the extension, since they will not be
touched by the parsing function if the parameters are not
passed.
</para>
</listitem>
<listitem>
<para>
<literal>/ - </literal>the parsing function will
call <function>SEPARATE_ZVAL_IF_NOT_REF</function> on
the parameter it follows, to provide a copy of the
parameter, unless it's a reference.
</para>
</listitem>
<listitem>
<para>
<literal>! - </literal>the parameter it follows can
be of specified type or <literal>NULL</literal> (only
applies to a, o, O, r, and z). If <literal>NULL</literal>
value is passed by the user, the storage pointer will be
set to <literal>NULL</literal>.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The best way to illustrate the usage of this function is through
examples:
<programlisting>
<![CDATA[
/* Gets a long, a string and its length, and a zval. */
long l;
char *s;
int s_len;
zval *param;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"lsz", &l, &s, &s_len, &param) == FAILURE) {
return;
}
/* Gets an object of class specified by my_ce, and an optional double. */
zval *obj;
double d = 0.5;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"O|d", &obj, my_ce, &d) == FAILURE) {
return;
}
/* Gets an object or null, and an array.
If null is passed for object, obj will be set to NULL. */
zval *obj;
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) {
return;
}
/* Gets a separated array. */
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) {
return;
}
/* Get only the first three parameters (useful for varargs functions). */
zval *z;
zend_bool b;
zval *r;
if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) {
return;
}
]]>
</programlisting>
</para>
<para>
Note that in the last example we pass 3 for the number of received
parameters, instead of <function>ZEND_NUM_ARGS</function>. What
this lets us do is receive the least number of parameters if our
function expects a variable number of them. Of course, if you want
to operate on the rest of the parameters, you will have to use
<function>zend_get_parameters_array_ex</function> to obtain
them.
</para>
<para>
The parsing function has an extended version that allows for an
additional flags argument that controls its actions.
<programlisting>
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
</programlisting>
</para>
<para>
The only flag you can pass currently is <literal>ZEND_PARSE_PARAMS_QUIET</literal>,
which instructs the function to not output any error messages
during its operation. This is useful for functions that expect
several sets of completely different arguments, but you will have
to output your own error messages.
</para>
<para>
For example, here is how you would get either a set of three longs
or a string:
<programlisting>
<![CDATA[
long l1, l2, l3;
char *s;
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS() TSRMLS_CC,
"lll", &l1, &l2, &l3) == SUCCESS) {
/* manipulate longs */
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS(), "s", &s, &s_len) == SUCCESS) {
/* manipulate string */
} else {
php_error(E_WARNING, "%s() takes either three long values or a string as argument",
get_active_function_name(TSRMLS_C));
return;
}]]>
</programlisting>
</para>
<para>
With all the abovementioned ways of receiving function parameters
you should have a good handle on this process. For even more
example, look through the source code for extensions that are
shipped with PHP - they illustrate every conceivable situation.
</para>
</sect2>
<sect2 id="zend.arguments.deprecated-retrieval">
<title>Old way of retrieving arguments (deprecated)</title>
<note>
<title>
Deprecated parameter parsing API
</title>
<para>
This API is deprecated and superseded by the new ZEND
parameter parsing API.
</para>
</note>
<para>
After having checked the number of arguments, you need to get access
to the arguments themselves. This is done with the help of
<function>zend_get_parameters_ex</function>:
<programlisting>
<![CDATA[
zval **parameter;
if(zend_get_parameters_ex(1, &parameter) != SUCCESS)
WRONG_PARAM_COUNT;
]]>
</programlisting>
All arguments are stored in a <envar>zval</envar> container,
which needs to be pointed to <emphasis>twice</emphasis>. The snippet above
tries to retrieve one argument and make it available to us via the
<envar>parameter</envar> pointer.
</para>
<para>
<function>zend_get_parameters_ex</function> accepts at least two
arguments. The first argument is the number of arguments to
retrieve (which should match the number of arguments with which
the function has been called; this is why it's important to check
for correct call syntax). The second argument (and all following
arguments) are pointers to pointers to pointers to
<envar>zval</envar>s. (Confusing, isn't it?) All these pointers
are required because Zend works internally with
<envar>**zval</envar>; to adjust a local <envar>**zval</envar> in
our function,<function>zend_get_parameters_ex</function> requires
a pointer to it.
</para>
<para>
The return value of <function>zend_get_parameters_ex</function>
can either be <literal>SUCCESS</literal> or
<literal>FAILURE</literal>, indicating (unsurprisingly) success or
failure of the argument processing. A failure is most likely
related to an incorrect number of arguments being specified, in
which case you should exit with
<literal>WRONG_PARAM_COUNT</literal>.
</para>
<para>
To retrieve more than one argument, you can use a similar snippet:
<programlisting>
<![CDATA[
zval **param1, **param2, **param3, **param4;
if(zend_get_parameters_ex(4, &param1, &param2, &param3, &param4) != SUCCESS)
WRONG_PARAM_COUNT;
]]>
</programlisting>
</para>
<para>
<function>zend_get_parameters_ex</function> only checks whether
you're trying to retrieve too many parameters. If the function is
called with five arguments, but you're only retrieving three of
them with <function>zend_get_parameters_ex</function>, you won't
get an error but will get the first three parameters instead.
Subsequent calls of <function>zend_get_parameters_ex</function>
won't retrieve the remaining arguments, but will get the same
arguments again.
</para>
</sect2>
<sect2 id="zend.arguments.variable">
<title>Dealing with a Variable Number of Arguments/Optional Parameters</title>
<para>
If your function is meant to accept a variable number of
arguments, the snippets just described are sometimes suboptimal
solutions. You have to create a line calling
<function>zend_get_parameters_ex</function> for every possible
number of arguments, which is often unsatisfying.
</para>
<para>
For this case, you can use the
function <function>zend_get_parameters_array_ex</function>, which accepts the
number of parameters to retrieve and an array in which to store them:
<programlisting>
<![CDATA[
zval **parameter_array[4];
/* get the number of arguments */
argument_count = ZEND_NUM_ARGS();
/* see if it satisfies our minimal request (2 arguments) */
/* and our maximal acceptance (4 arguments) */
if(argument_count < 2 || argument_count > 4)
WRONG_PARAM_COUNT;
/* argument count is correct, now retrieve arguments */
if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS)
WRONG_PARAM_COUNT;
]]>
</programlisting>
First, the number of arguments is checked to make sure that it's in the accepted range. After that,
<function>zend_get_parameters_array_ex</function> is used to
fill <envar>parameter_array</envar> with valid pointers to the argument
values.
</para>
<para>
A very clever implementation of this can be found in the code
handling PHP's <function>fsockopen</function> located in
<filename>ext/standard/fsock.c</filename>, as shown in
<xref linkend='example.fsockopen'/>. Don't worry if you don't know all the functions used in this
source yet; we'll get to them shortly.
</para>
<example id='example.fsockopen'>
<title>PHP's implementation of variable arguments in fsockopen().</title>
<programlisting>
<![CDATA[
pval **args[5];
int *sock=emalloc(sizeof(int));
int *sockp;
int arg_count=ARG_COUNT(ht);
int socketd = -1;
unsigned char udp = 0;
struct timeval timeout = { 60, 0 };
unsigned short portno;
unsigned long conv;
char *key = NULL;
FLS_FETCH();
if (arg_count > 5 || arg_count < 2 || zend_get_parameters_array_ex(arg_count,args)==FAILURE) {
CLOSE_SOCK(1);
WRONG_PARAM_COUNT;
}
switch(arg_count) {
case 5:
convert_to_double_ex(args[4]);
conv = (unsigned long) (Z_DVAL_PP(args[4]) * 1000000.0);
timeout.tv_sec = conv / 1000000;
timeout.tv_usec = conv % 1000000;
/* fall-through */
case 4:
if (!PZVAL_IS_REF(*args[3])) {
php_error(E_WARNING,"error string argument to fsockopen not passed by reference");
}
pval_copy_constructor(*args[3]);
ZVAL_EMPTY_STRING(*args[3]);
/* fall-through */
case 3:
if (!PZVAL_IS_REF(*args[2])) {
php_error(E_WARNING,"error argument to fsockopen not passed by reference");
return;
}
ZVAL_LONG(*args[2], 0);
break;
}
convert_to_string_ex(args[0]);
convert_to_long_ex(args[1]);
portno = (unsigned short) Z_LVAL_P(args[1]);
key = emalloc(Z_STRLEN_P(args[0]) + 10);
]]>
</programlisting>
</example>
<para>
<function>fsockopen</function> accepts two, three, four, or five
parameters. After the obligatory variable declarations, the
function checks for the correct range of arguments. Then it uses a
fall-through mechanism in a <literal>switch()</literal> statement
to deal with all arguments. The <literal>switch()</literal>
statement starts with the maximum number of arguments being passed
(five). After that, it automatically processes the case of four
arguments being passed, then three, by omitting the otherwise
obligatory <literal>break</literal> keyword in all stages. After
having processed the last case, it exits the
<literal>switch()</literal> statement and does the minimal
argument processing needed if the function is invoked with only
two arguments.
</para>
<para>
This multiple-stage type of processing, similar to a stairway, allows
convenient processing of a variable number of arguments.
</para>
</sect2>
<sect2 id="zend.arguments.access">
<title>Accessing Arguments</title>
<para>
To access arguments, it's necessary for each argument to have a
clearly defined type. Again, PHP's extremely dynamic nature
introduces some quirks. Because PHP never does any kind of type
checking, it's possible for a caller to pass any kind of data to
your functions, whether you want it or not. If you expect an
integer, for example, the caller might pass an array, and vice
versa - PHP simply won't notice.
</para>
<para>
To work around this, you have to use a set of API functions to
force a type conversion on every argument that's being passed (see
<xref linkend='tab.arg-conv'/>).
</para>
<para>
<emphasis>Note:</emphasis> All conversion functions expect a
<envar>**zval</envar> as parameter.
</para>
<table id='tab.arg-conv'>
<title>Argument Conversion Functions</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.02*"/>
<colspec colnum="2" colname="col2" colwidth="1.00*"/>
<tbody>
<row>
<entry colname="col1">Function</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_boolean_ex</function></entry>
<entry colname="col2">
Forces conversion to a Boolean type. Boolean values remain
untouched. Longs, doubles, and strings containing
<literal>0</literal> as well as NULL values will result in
Boolean <literal>0</literal> (FALSE). Arrays and objects are
converted based on the number of entries or properties,
respectively, that they have. Empty arrays and objects are
converted to FALSE; otherwise, to TRUE. All other values
result in a Boolean <literal>1</literal> (TRUE).
</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_long_ex</function></entry>
<entry colname="col2">
Forces conversion to a long, the default integer type. NULL
values, Booleans, resources, and of course longs remain
untouched. Doubles are truncated. Strings containing an
integer are converted to their corresponding numeric
representation, otherwise resulting in <literal>0</literal>.
Arrays and objects are converted to <literal>0</literal> if
empty, <literal>1</literal> otherwise.
</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_double_ex</function></entry>
<entry colname="col2">
Forces conversion to a double, the default floating-point
type. NULL values, Booleans, resources, longs, and of course
doubles remain untouched. Strings containing a number are
converted to their corresponding numeric representation,
otherwise resulting in <literal>0.0</literal>. Arrays and
objects are converted to <literal>0.0</literal> if empty,
<literal>1.0</literal> otherwise.
</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_string_ex</function></entry>
<entry colname="col2">
Forces conversion to a string. Strings remain untouched. NULL
values are converted to an empty string. Booleans containing
TRUE are converted to <literal>"1"</literal>, otherwise
resulting in an empty string. Longs and doubles are converted
to their corresponding string representation. Arrays are
converted to the string <literal>"Array"</literal> and
objects to the string <literal>"Object"</literal>.
</entry>
</row>
<row>
<entry colname="col1"><literal>convert_to_array_ex(value)</literal></entry>
<entry colname="col2">
Forces conversion to an array. Arrays remain untouched.
Objects are converted to an array by assigning all their
properties to the array table. All property names are used as
keys, property contents as values. NULL values are converted
to an empty array. All other values are converted to an array
that contains the specific source value in the element with
the key <literal>0</literal>.
</entry>
</row>
<row>
<entry colname="col1"><literal>convert_to_object_ex(value)</literal></entry>
<entry colname="col2">
Forces conversion to an object. Objects remain untouched.
NULL values are converted to an empty object. Arrays are
converted to objects by introducing their keys as properties
into the objects and their values as corresponding property
contents in the object. All other types result in an object
with the property <literal>scalar</literal> , having the
corresponding source value as content.
</entry>
</row>
<row>
<entry colname="col1"><literal>convert_to_null_ex(value)</literal></entry>
<entry colname="col2">Forces the type to become a NULL value, meaning empty.</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
You can find a demonstration of the behavior in
<filename>cross_conversion.php</filename> on the accompanying
CD-ROM. <xref linkend='fig.cross-convert'/> shows the output.
</para>
</note>
<figure id='fig.cross-convert'>
<title>Cross-conversion behavior of PHP.</title>
<graphic fileref="figures/zend.04-cross-converter.png"/>
</figure>
<para>
Using these functions on your arguments will ensure type safety
for all data that's passed to you. If the supplied type doesn't
match the required type, PHP forces dummy contents on the
resulting value (empty strings, arrays, or objects,
<literal>0</literal> for numeric values, <literal>FALSE</literal>
for Booleans) to ensure a defined state.
</para>
<para>
Following is a quote from the sample module discussed
previously, which makes use of the conversion functions:
<programlisting>
<![CDATA[
zval **parameter;
if((ZEND_NUM_ARGS() != 1) || (zend_get_parameters_ex(1, &parameter) != SUCCESS))
{
WRONG_PARAM_COUNT;
}
convert_to_long_ex(parameter);
RETURN_LONG(Z_LVAL_P(parameter));
]]>
</programlisting>
After retrieving the parameter pointer, the parameter value is
converted to a long (an integer), which also forms the return value of
this function. Understanding access to the contents of the value requires a
short discussion of the <envar>zval</envar> type, whose definition is shown in <xref linkend='example.zval-typedef'/>.
</para>
<example id='example.zval-typedef'>
<title>PHP/Zend <envar>zval</envar> type definition.</title>
<programlisting>
<![CDATA[
typedef pval zval;
typedef struct _zval_struct zval;
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
struct {
zend_class_entry *ce;
HashTable *properties;
} obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
unsigned char type; /* active type */
unsigned char is_ref;
short refcount;
};
]]>
</programlisting>
</example>
<para>
Actually, <envar>pval</envar> (defined in <filename>php.h</filename>) is
only an alias of <envar>zval</envar> (defined in <filename>zend.h</filename>),
which in turn refers to <envar>_zval_struct</envar>. This is a most interesting
structure. <envar>_zval_struct</envar> is the "master" structure, containing
the value structure, type, and reference information. The substructure
<envar>zvalue_value</envar> is a union that contains the variable's contents.
Depending on the variable's type, you'll have to access different members of
this union. For a description of both structures, see
<xref linkend='tab.struct-zval'/>,
<xref linkend='tab.struct-zvalue-value'/> and
<xref linkend='tab.ztype-constants'/>.
</para>
<table id='tab.struct-zval'>
<title>Zend <envar>zval</envar> Structure</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.00*"/>
<colspec colnum="2" colname="col2" colwidth="1.66*"/>
<tbody>
<row>
<entry colname="col1">Entry</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><envar>value</envar></entry>
<entry colname="col2">
Union containing this variable's contents. See
<xref linkend='tab.struct-zvalue-value'/> for a description.
</entry>
</row>
<row>
<entry colname="col1"><envar>type</envar></entry>
<entry colname="col2">
Contains this variable's type. For a list of available
types, see <xref linkend='tab.ztype-constants'/>.
</entry>
</row>
<row>
<entry colname="col1"><envar>is_ref</envar></entry>
<entry colname="col2">
0 means that this variable is not a reference; 1 means that this variable is a reference to another variable.
</entry>
</row>
<row>
<entry colname="col1"><envar>refcount</envar></entry>
<entry colname="col2">
The number of references that exist for this variable. For
every new reference to the value stored in this variable,
this counter is increased by 1. For every lost reference,
this counter is decreased by 1. When the reference counter
reaches 0, no references exist to this value anymore, which
causes automatic freeing of the value.
</entry>
</row>
</tbody>
</tgroup>
</table>
<table id='tab.struct-zvalue-value'>
<title>Zend <envar>zvalue_value</envar> Structure</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.00*"/>
<colspec colnum="2" colname="col2" colwidth="1.66*"/>
<tbody>
<row>
<entry colname="col1">Entry</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><envar>lval</envar></entry>
<entry colname="col2">Use this property if the variable is of the
type <literal>IS_LONG</literal>,
<literal>IS_BOOLEAN</literal>, or <literal>IS_RESOURCE</literal>.</entry>
</row>
<row>
<entry colname="col1"><envar>dval</envar></entry>
<entry colname="col2">Use this property if the variable is of the
type <literal>IS_DOUBLE</literal>.</entry>
</row>
<row>
<entry colname="col1"><envar>str</envar></entry>
<entry colname="col2">
This structure can be used to access variables of
the type <literal>IS_STRING</literal>. The member <envar>len</envar> contains the
string length; the member <envar>val</envar> points to the string itself. Zend
uses C strings; thus, the string length contains a trailing
<literal>0x00</literal>.</entry>
</row>
<row>
<entry colname="col1"><envar>ht</envar></entry>
<entry colname="col2">This entry points to the variable's hash table entry if the variable is an array.</entry>
</row>
<row>
<entry colname="col1"><envar>obj</envar></entry>
<entry colname="col2">Use this property if the variable is of the
type <literal>IS_OBJECT</literal>.</entry>
</row>
</tbody>
</tgroup>
</table>
<table id='tab.ztype-constants'>
<title>Zend Variable Type Constants</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.00*"/>
<colspec colnum="2" colname="col2" colwidth="1.65*"/>
<tbody>
<row>
<entry colname="col1">Constant</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><literal>IS_NULL</literal></entry>
<entry colname="col2">Denotes a NULL (empty) value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_LONG</literal></entry>
<entry colname="col2">A long (integer) value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_DOUBLE</literal></entry>
<entry colname="col2">A double (floating point) value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_STRING</literal></entry>
<entry colname="col2">A string.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_ARRAY</literal></entry>
<entry colname="col2">Denotes an array.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_OBJECT</literal></entry>
<entry colname="col2">An object.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_BOOL</literal></entry>
<entry colname="col2">A Boolean value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_RESOURCE</literal></entry>
<entry colname="col2">A resource (for a discussion of resources, see the
appropriate section below).</entry>
</row>
<row>
<entry colname="col1"><literal>IS_CONSTANT</literal></entry>
<entry colname="col2">A constant (defined) value.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
To access a long you access <envar>zval.value.lval</envar>, to
access a double you use <envar>zval.value.dval</envar>, and so on.
Because all values are stored in a union, trying to access data
with incorrect union members results in meaningless output.
</para>
<para>
Accessing arrays and objects is a bit more complicated and
is discussed later.
</para>
</sect2>
<sect2 id="zend.arguments.by-reference">
<title>Dealing with Arguments Passed by Reference</title>
<para>
If your function accepts arguments passed by reference that you
intend to modify, you need to take some precautions.
</para>
<para>
What we didn't say yet is that under the circumstances presented so
far, you don't have write access to any <envar>zval</envar> containers
designating function parameters that have been passed to you. Of course, you
can change any <envar>zval</envar> containers that you created within
your function, but you mustn't change any <envar>zval</envar>s that refer to
Zend-internal data!
</para>
<para>
We've only discussed the so-called <function>*_ex</function> API
so far. You may have noticed that the API functions we've used are
called <function>zend_get_parameters_ex</function> instead of
<function>zend_get_parameters</function>,
<function>convert_to_long_ex</function> instead of
<function>convert_to_long</function>, etc. The
<function>*_ex</function> functions form the so-called new
"extended" Zend API. They give a minor speed increase over the old
API, but as a tradeoff are only meant for providing read-only
access.
</para>
<para>
Because Zend works internally with references, different variables
may reference the same value. Write access to a
<envar>zval</envar> container requires this container to contain
an isolated value, meaning a value that's not referenced by any
other containers. If a <envar>zval</envar> container were
referenced by other containers and you changed the referenced
<envar>zval</envar>, you would automatically change the contents
of the other containers referencing this <envar>zval</envar>
(because they'd simply point to the changed value and thus change
their own value as well).
</para>
<para>
<function>zend_get_parameters_ex</function> doesn't care about
this situation, but simply returns a pointer to the desired
<envar>zval</envar> containers, whether they consist of references
or not. Its corresponding function in the traditional API,
<function>zend_get_parameters</function>, immediately checks for
referenced values. If it finds a reference, it creates a new,
isolated <envar>zval</envar> container; copies the referenced data
into this newly allocated space; and then returns a pointer to the
new, isolated value.
</para>
<para>
This action is called <emphasis>zval separation</emphasis>
(or pval separation). Because the <function>*_ex</function> API
doesn't perform zval separation, it's considerably faster, while
at the same time disabling write access.
</para>
<para>
To change parameters, however, write access is required. Zend deals
with this situation in a special way: Whenever a parameter to a function is
passed by reference, it performs automatic zval separation. This means that
whenever you're calling a function like
this in PHP, Zend will automatically ensure
that <envar>$parameter</envar> is being passed as an isolated value, rendering it
to a write-safe state:
<programlisting>
my_function(&amp;$parameter);
</programlisting>
</para>
<para>
But this <emphasis>is not</emphasis> the case with regular parameters!
All other parameters that are not passed by reference are in a read-only
state.
</para>
<para>
This requires you to make sure that you're really working with a
reference - otherwise you might produce unwanted results. To check for a
parameter being passed by reference, you can use the macro
<literal>PZVAL_IS_REF</literal>. This macro accepts a <literal>zval*</literal>
to check if it is a reference or not. Examples are given in
in <xref linkend='example.pass-by-ref'/>.
</para>
<example id='example.pass-by-ref'>
<title>Testing for referenced parameter passing.</title>
<programlisting>
<![CDATA[
zval *parameter;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &parameter) == FAILURE)
return;
/* check for parameter being passed by reference */
if (!PZVAL_IS_REF(parameter)) {
{
zend_error(E_WARNING, "Parameter wasn't passed by reference");
RETURN_NULL();
}
/* make changes to the parameter */
ZVAL_LONG(parameter, 10);
]]>
</programlisting>
<graphic fileref="figures/zend.05-reference-test.png"/>
</example>
</sect2>
<sect2 id="zend.arguments.write-safety">
<title>Assuring Write Safety for Other Parameters</title>
<para>
You might run into a situation in which you need write access to a
parameter that's retrieved with <function>zend_get_parameters_ex</function>
but not passed by reference. For this case, you can use the macro
<literal>SEPARATE_ZVAL</literal>, which does a zval separation on the provided
container. The newly generated <envar>zval</envar> is detached from internal
data and has only a local scope, meaning that it can be changed or destroyed
without implying global changes in the script context:
<programlisting>
<![CDATA[
zval **parameter;
/* retrieve parameter */
zend_get_parameters_ex(1, &parameter);
/* at this stage, <parameter> still is connected */
/* to Zend's internal data buffers */
/* make <parameter> write-safe */
SEPARATE_ZVAL(parameter);
/* now we can safely modify <parameter> */
/* without implying global changes */
]]>
</programlisting>
<literal>SEPARATE_ZVAL</literal> uses <function>emalloc</function>
to allocate the new <envar>zval</envar> container, which means that even if you
don't deallocate this memory yourself, it will be destroyed automatically upon
script termination. However, doing a lot of calls to this macro
without freeing the resulting containers will clutter up your RAM.
</para>
<para>
<emphasis>Note:</emphasis> As you can easily work around the lack
of write access in the "traditional" API (with
<function>zend_get_parameters</function> and so on), this API
seems to be obsolete, and is not discussed further in this
chapter.
</para>
</sect2>
</sect1>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"../../../manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->