mirror of
https://github.com/sigmasternchen/php-doc-en
synced 2025-03-27 14:28:56 +00:00

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@205753 c90b9560-bf6c-de11-be94-00142212c4b1
1013 lines
39 KiB
XML
1013 lines
39 KiB
XML
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
|
<!-- $Revision: 1.3 $ -->
|
|
<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 like:
|
|
<![CDATA[
|
|
"Warning: Wrong parameter count for firstmodule() in /home/www/htdocs/firstmod.php on line 5"
|
|
]]>
|
|
</para>
|
|
<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 parameters, 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, ¶m) == 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, ¶meter) != 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, ¶m1, ¶m2, ¶m3, ¶m4) != 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, ¶meter) != 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(&$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", ¶meter) == 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, ¶meter);
|
|
|
|
/* 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
|
|
-->
|