Sophie

Sophie

distrib > Mandriva > 2008.1 > x86_64 > by-pkgid > 05cd670d8a02b2b4a0ffb1756f2e8308 > files > 13148

php-manual-zh-5.2.4-1mdv2008.1.noarch.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML
><HEAD
><TITLE
>Accepting Arguments</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
REL="HOME"
TITLE="PHP 手册"
HREF="index.html"><LINK
REL="UP"
TITLE="Zend API:深入 PHP 内核"
HREF="zend.html"><LINK
REL="PREVIOUS"
TITLE="Source Discussion"
HREF="zend.structure.html"><LINK
REL="NEXT"
TITLE="Creating Variables"
HREF="zend.variables.html"><META
HTTP-EQUIV="Content-type"
CONTENT="text/html; charset=UTF-8"></HEAD
><BODY
CLASS="sect1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>PHP 手册</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="zend.structure.html"
ACCESSKEY="P"
>上一页</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>章 46. Zend API:深入 PHP 内核</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="zend.variables.html"
ACCESSKEY="N"
>下一页</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="sect1"
><H1
CLASS="sect1"
><A
NAME="zend.arguments"
>Accepting Arguments</A
></H1
><P
>&#13;   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. 
  </P
><P
>&#13;   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.
  </P
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.count"
>Determining the Number of Arguments</A
></H2
><P
>&#13;    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
    <TT
CLASS="literal"
>ZEND_NUM_ARGS</TT
> 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, <CODE
CLASS="envar"
>ht</CODE
>, which is passed in the
    <TT
CLASS="literal"
>INTERNAL_FUNCTION_PARAMETERS</TT
> list. As
    <CODE
CLASS="envar"
>ht</CODE
> itself now contains the number of arguments that
    have been passed to the function, <TT
CLASS="literal"
>ZEND_NUM_ARGS</TT
>
    has been stripped down to a dummy macro (see its definition in
    <TT
CLASS="filename"
>zend_API.h</TT
>). But it's still good practice to
    use it, to remain compatible with future changes in the call
    interface. <SPAN
CLASS="emphasis"
><I
CLASS="emphasis"
>Note:</I
></SPAN
> The old PHP equivalent of
    this macro is <TT
CLASS="literal"
>ARG_COUNT</TT
>.
   </P
><P
>&#13;    The following code checks for the correct number of arguments:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>if(ZEND_NUM_ARGS() != 2) WRONG_PARAM_COUNT;</PRE
></TD
></TR
></TABLE
> 
    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 <TT
CLASS="literal"
>WRONG_PARAM_COUNT</TT
>,
    which can be used to generate a standard error message like:

"Warning: Wrong parameter count for firstmodule() in /home/www/htdocs/firstmod.php on line 5"

   </P
><P
>&#13;    This macro prints a default error message and then returns to the caller.
    Its definition can also be found in <TT
CLASS="filename"
>zend_API.h</TT
> and looks
    like this: 
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>ZEND_API void wrong_param_count(void);

#define WRONG_PARAM_COUNT { wrong_param_count(); return; }</PRE
></TD
></TR
></TABLE
>
    As you can see, it calls an internal function
    named <B
CLASS="function"
>wrong_param_count()</B
> that's responsible for printing
    the warning. For details on generating customized error
    messages, see the later section "Printing Information."
   </P
></DIV
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.retrieval"
>Retrieving Arguments</A
></H2
><DIV
CLASS="note"
><BLOCKQUOTE
CLASS="note"
><P
><B
>New parameter parsing API: </B
>
     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 .
    </P
></BLOCKQUOTE
></DIV
><P
>&#13;    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.
   </P
><P
>&#13;    The prototype for parameter parsing function looks like this:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...);</PRE
></TD
></TR
></TABLE
>
    The first argument to this function is supposed to be the number
    of actual parameters passed to your function, so
    <TT
CLASS="literal"
>ZEND_NUM_ARGS()</TT
> can be used for that. The
    second parameter should always be <TT
CLASS="literal"
>TSRMLS_CC</TT
>
    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.
   </P
><P
>&#13;    <A
HREF="zend-api.zend-parse-parameters.html"
><B
CLASS="function"
>zend_parse_parameters()</B
></A
> 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.
   </P
><P
>&#13;    If the parameters could be obtained successfully and there were no
    errors during type conversion, the function will return
    <TT
CLASS="literal"
>SUCCESS</TT
>, otherwise it will return
    <TT
CLASS="literal"
>FAILURE</TT
>.  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.
   </P
><P
>&#13;    Here are some sample error messages:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="screen"
>Warning - ini_get_all() requires at most 1 parameter, 2 given
     Warning - wddx_deserialize() expects parameter 1 to be string, array given</PRE
></TD
></TR
></TABLE
>
    Of course each error message is accompanied by the filename and
    line number on which it occurs.
   </P
><P
>&#13;    Here is the full list of type specifiers:
    <P
></P
><UL
><LI
><P
><TT
CLASS="literal"
>l</TT
> - long</P
></LI
><LI
><P
><TT
CLASS="literal"
>d</TT
> - double</P
></LI
><LI
><P
><TT
CLASS="literal"
>s</TT
> - string (with possible null bytes) and its length</P
></LI
><LI
><P
><TT
CLASS="literal"
>b</TT
> - boolean</P
></LI
><LI
><P
><TT
CLASS="literal"
>r</TT
> - resource, stored in <TT
CLASS="literal"
>zval*</TT
></P
></LI
><LI
><P
><TT
CLASS="literal"
>a</TT
> - array, stored in <TT
CLASS="literal"
>zval*</TT
></P
></LI
><LI
><P
><TT
CLASS="literal"
>o</TT
> - object (of any class), stored in <TT
CLASS="literal"
>zval*</TT
></P
></LI
><LI
><P
><TT
CLASS="literal"
>O</TT
> - object (of class specified by class entry), stored in <TT
CLASS="literal"
>zval*</TT
></P
></LI
><LI
><P
><TT
CLASS="literal"
>z</TT
> - the actual <TT
CLASS="literal"
>zval*</TT
></P
></LI
></UL
>
    The following characters also have a meaning in the specifier
    string:
    <P
></P
><UL
><LI
><P
>&#13;       <TT
CLASS="literal"
>|</TT
> - 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.
      </P
></LI
><LI
><P
>&#13;       <TT
CLASS="literal"
>/</TT
> - the parsing function will
       call <B
CLASS="function"
>SEPARATE_ZVAL_IF_NOT_REF()</B
> on
       the parameter it follows, to provide a copy of the
       parameter, unless it's a reference.  
      </P
></LI
><LI
><P
>&#13;       <TT
CLASS="literal"
>!</TT
> - the parameter it follows can
       be of specified type or <TT
CLASS="literal"
>NULL</TT
> (only
       applies to a, o, O, r, and z). If <TT
CLASS="literal"
>NULL</TT
>
       value is passed by the user, the storage pointer will be
       set to <TT
CLASS="literal"
>NULL</TT
>.
      </P
></LI
></UL
>
   </P
><P
>&#13;    The best way to illustrate the usage of this function is through
    examples:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>/* 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", &#38;l, &#38;s, &#38;s_len, &#38;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", &#38;obj, my_ce, &#38;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", &#38;obj, &#38;arr) == FAILURE) {
    return;
}

/* Gets a separated array. */
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &#38;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!", &#38;z, &#38;b, &#38;r) == FAILURE) {
    return;
}</PRE
></TD
></TR
></TABLE
>
   </P
><P
>&#13;    Note that in the last example we pass 3 for the number of received
    parameters, instead of <A
HREF="zend-macro.zend-num-args.html"
><B
CLASS="function"
>ZEND_NUM_ARGS()</B
></A
>. 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
    <A
HREF="zend-api.zend-get-parameters-array-ex.html"
><B
CLASS="function"
>zend_get_parameters_array_ex()</B
></A
> to obtain
    them.
   </P
><P
>&#13;    The parsing function has an extended version that allows for an
    additional flags argument that controls its actions.
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);</PRE
></TD
></TR
></TABLE
>
   </P
><P
>&#13;    The only flag you can pass currently is <TT
CLASS="literal"
>ZEND_PARSE_PARAMS_QUIET</TT
>,
    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.
   </P
><P
>&#13;    For example, here is how you would get either a set of three longs
    or a string:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>long l1, l2, l3;
char *s;
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
                             ZEND_NUM_ARGS() TSRMLS_CC,
                             "lll", &#38;l1, &#38;l2, &#38;l3) == SUCCESS) {
    /* manipulate longs */
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
                                    ZEND_NUM_ARGS(), "s", &#38;s, &#38;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;
}</PRE
></TD
></TR
></TABLE
>
   </P
><P
>&#13;    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.
   </P
></DIV
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.deprecated-retrieval"
>Old way of retrieving arguments (deprecated)</A
></H2
><DIV
CLASS="note"
><BLOCKQUOTE
CLASS="note"
><P
><B
>Deprecated parameter parsing API: </B
>
     This API is deprecated and superseded by the new ZEND
     parameter parsing API.
    </P
></BLOCKQUOTE
></DIV
><P
>&#13;    After having checked the number of arguments, you need to get access
    to the arguments themselves. This is done with the help of 
    <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
>: 
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>zval **parameter;

if(zend_get_parameters_ex(1, &#38;parameter) != SUCCESS)
  WRONG_PARAM_COUNT;</PRE
></TD
></TR
></TABLE
>
    All arguments are stored in a <CODE
CLASS="envar"
>zval</CODE
> container,
    which needs to be pointed to <SPAN
CLASS="emphasis"
><I
CLASS="emphasis"
>twice</I
></SPAN
>. The snippet above
    tries to retrieve one argument and make it available to us via the
    <CODE
CLASS="envar"
>parameter</CODE
> pointer.
   </P
><P
>&#13;    <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> 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
    <CODE
CLASS="envar"
>zval</CODE
>s. (Confusing, isn't it?) All these pointers
    are required because Zend works internally with
    <CODE
CLASS="envar"
>**zval</CODE
>; to adjust a local <CODE
CLASS="envar"
>**zval</CODE
> in
    our function,<A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> requires
    a pointer to it.
   </P
><P
>&#13;    The return value of <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
>
    can either be <TT
CLASS="literal"
>SUCCESS</TT
> or
    <TT
CLASS="literal"
>FAILURE</TT
>, 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
    <TT
CLASS="literal"
>WRONG_PARAM_COUNT</TT
>.
   </P
><P
>&#13;    To retrieve more than one argument, you can use a similar snippet: 
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>zval **param1, **param2, **param3, **param4;
     
if(zend_get_parameters_ex(4, &#38;param1, &#38;param2, &#38;param3, &#38;param4) != SUCCESS)
    WRONG_PARAM_COUNT;</PRE
></TD
></TR
></TABLE
>
   </P
><P
>&#13;    <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> 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 <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
>, you won't
    get an error but will get the first three parameters instead.
    Subsequent calls of <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
>
    won't retrieve the remaining arguments, but will get the same
    arguments again.
   </P
></DIV
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.variable"
>Dealing with a Variable Number of Arguments/Optional Parameters</A
></H2
><P
>&#13;    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
    <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> for every possible
    number of arguments, which is often unsatisfying.
   </P
><P
>&#13;    For this case, you can use the
    function <A
HREF="zend-api.zend-get-parameters-array-ex.html"
><B
CLASS="function"
>zend_get_parameters_array_ex()</B
></A
>, which accepts the
    number of parameters to retrieve and an array in which to store them: 
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>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 &#60; 2 || argument_count &#62; 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;</PRE
></TD
></TR
></TABLE
>
    First, the number of arguments is checked to make sure that it's in the accepted range. After that,
    <A
HREF="zend-api.zend-get-parameters-array-ex.html"
><B
CLASS="function"
>zend_get_parameters_array_ex()</B
></A
> is used to
    fill <CODE
CLASS="envar"
>parameter_array</CODE
> with valid pointers to the argument
    values.
   </P
><P
>&#13;    A very clever implementation of this can be found in the code
    handling PHP's <B
CLASS="function"
>fsockopen()</B
> located in
    <TT
CLASS="filename"
>ext/standard/fsock.c</TT
>, as shown in 
    <A
HREF="zend.arguments.html#example.fsockopen"
>例 46-6</A
>. Don't worry if you don't know all the functions used in this
    source yet; we'll get to them shortly.
   </P
><TABLE
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
CLASS="EXAMPLE"
><TR
><TD
><DIV
CLASS="example"
><A
NAME="example.fsockopen"
></A
><P
><B
>例 46-6. PHP's implementation of variable arguments in fsockopen().</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>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 &#62; 5 || arg_count &#60; 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);</PRE
></TD
></TR
></TABLE
></DIV
></TD
></TR
></TABLE
><P
>&#13;    <B
CLASS="function"
>fsockopen()</B
> 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 <TT
CLASS="literal"
>switch()</TT
> statement
    to deal with all arguments. The  <TT
CLASS="literal"
>switch()</TT
>
    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 <TT
CLASS="literal"
>break</TT
> keyword in all stages. After
    having processed the last case, it exits the
    <TT
CLASS="literal"
>switch()</TT
> statement and does the minimal
    argument processing needed if the function is invoked with only
    two arguments.
   </P
><P
>&#13;    This multiple-stage type of processing, similar to a stairway, allows
    convenient processing of a variable number of arguments.
   </P
></DIV
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.access"
>Accessing Arguments</A
></H2
><P
>&#13;    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.
   </P
><P
>&#13;    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
    <A
HREF="zend.arguments.html#tab.arg-conv"
>表 46-4</A
>).
   </P
><P
>&#13;    <SPAN
CLASS="emphasis"
><I
CLASS="emphasis"
>Note:</I
></SPAN
> All conversion functions expect a
    <CODE
CLASS="envar"
>**zval</CODE
> as parameter.
   </P
><DIV
CLASS="table"
><A
NAME="tab.arg-conv"
></A
><P
><B
>表 46-4. Argument Conversion Functions</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL
WIDTH="1.02*"
TITLE="col1"><COL
WIDTH="1*"
TITLE="col2"><TBODY
><TR
><TD
>Function</TD
><TD
>Description</TD
></TR
><TR
><TD
><B
CLASS="function"
>convert_to_boolean_ex()</B
></TD
><TD
>&#13;         Forces conversion to a Boolean type. Boolean values remain
         untouched. Longs, doubles, and strings containing
         <TT
CLASS="literal"
>0</TT
> as well as NULL values will result in
         Boolean <TT
CLASS="literal"
>0</TT
> (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 <TT
CLASS="literal"
>1</TT
> (TRUE).
        </TD
></TR
><TR
><TD
><B
CLASS="function"
>convert_to_long_ex()</B
></TD
><TD
>&#13;         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 <TT
CLASS="literal"
>0</TT
>.
         Arrays and objects are converted to <TT
CLASS="literal"
>0</TT
> if
         empty,  <TT
CLASS="literal"
>1</TT
> otherwise.
        </TD
></TR
><TR
><TD
><B
CLASS="function"
>convert_to_double_ex()</B
></TD
><TD
>&#13;         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 <TT
CLASS="literal"
>0.0</TT
>. Arrays and
         objects are converted to <TT
CLASS="literal"
>0.0</TT
> if empty,
         <TT
CLASS="literal"
>1.0</TT
> otherwise.
        </TD
></TR
><TR
><TD
><B
CLASS="function"
>convert_to_string_ex()</B
></TD
><TD
>&#13;         Forces conversion to a string. Strings remain untouched. NULL
         values are converted to an empty string. Booleans containing
         TRUE are converted to <TT
CLASS="literal"
>"1"</TT
>, otherwise
         resulting in an empty string. Longs and doubles are converted
         to their corresponding string representation. Arrays are
         converted to the string <TT
CLASS="literal"
>"Array"</TT
> and
         objects to the string <TT
CLASS="literal"
>"Object"</TT
>.
        </TD
></TR
><TR
><TD
><TT
CLASS="literal"
>convert_to_array_ex(value)</TT
></TD
><TD
>&#13;         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 <TT
CLASS="literal"
>0</TT
>.
        </TD
></TR
><TR
><TD
><TT
CLASS="literal"
>convert_to_object_ex(value)</TT
></TD
><TD
>&#13;         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 <TT
CLASS="literal"
>scalar</TT
> , having the
         corresponding source value as content.
        </TD
></TR
><TR
><TD
><TT
CLASS="literal"
>convert_to_null_ex(value)</TT
></TD
><TD
>Forces the type to become a NULL value, meaning empty.</TD
></TR
></TBODY
></TABLE
></DIV
><DIV
CLASS="note"
><BLOCKQUOTE
CLASS="note"
><P
><B
>注意: </B
>
     You can find a demonstration of the behavior in
     <TT
CLASS="filename"
>cross_conversion.php</TT
> on the accompanying
     CD-ROM. <A
HREF="zend.arguments.html#fig.cross-convert"
>图 46-2</A
> shows the output.
    </P
></BLOCKQUOTE
></DIV
><DIV
CLASS="figure"
><A
NAME="fig.cross-convert"
></A
><P
><B
>图 46-2. Cross-conversion behavior of PHP.</B
></P
><P
><IMG
SRC="figures/zendapi.zend.04-cross-converter.png"></P
></DIV
><P
>&#13;    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,
    <TT
CLASS="literal"
>0</TT
> for numeric values, <TT
CLASS="literal"
>FALSE</TT
>
    for Booleans) to ensure a defined state.
   </P
><P
>&#13;    Following is a quote from the sample module discussed
    previously, which makes use of the conversion functions: 
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>zval **parameter;

if((ZEND_NUM_ARGS() != 1) || (zend_get_parameters_ex(1, &#38;parameter) != SUCCESS))
{
    WRONG_PARAM_COUNT;
}

convert_to_long_ex(parameter);

RETURN_LONG(Z_LVAL_P(parameter));</PRE
></TD
></TR
></TABLE
>
    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 <CODE
CLASS="envar"
>zval</CODE
> type, whose definition is shown in <A
HREF="zend.arguments.html#example.zval-typedef"
>例 46-7</A
>.
   </P
><TABLE
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
CLASS="EXAMPLE"
><TR
><TD
><DIV
CLASS="example"
><A
NAME="example.zval-typedef"
></A
><P
><B
>例 46-7. PHP/Zend <CODE
CLASS="envar"
>zval</CODE
> type definition.</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>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;
};</PRE
></TD
></TR
></TABLE
></DIV
></TD
></TR
></TABLE
><P
>&#13;    Actually, <CODE
CLASS="envar"
>pval</CODE
> (defined in <TT
CLASS="filename"
>php.h</TT
>) is
    only an alias of <CODE
CLASS="envar"
>zval</CODE
> (defined in <TT
CLASS="filename"
>zend.h</TT
>),
    which in turn refers to <CODE
CLASS="envar"
>_zval_struct</CODE
>. This is a most interesting
    structure. <CODE
CLASS="envar"
>_zval_struct</CODE
> is the "master" structure, containing
    the value structure, type, and reference information. The substructure
    <CODE
CLASS="envar"
>zvalue_value</CODE
> 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 
    <A
HREF="zend.arguments.html#tab.struct-zval"
>表 46-5</A
>,
    <A
HREF="zend.arguments.html#tab.struct-zvalue-value"
>表 46-6</A
> and
    <A
HREF="zend.arguments.html#tab.ztype-constants"
>表 46-7</A
>.
   </P
><DIV
CLASS="table"
><A
NAME="tab.struct-zval"
></A
><P
><B
>表 46-5. Zend <CODE
CLASS="envar"
>zval</CODE
> Structure</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL
WIDTH="1*"
TITLE="col1"><COL
WIDTH="1.66*"
TITLE="col2"><TBODY
><TR
><TD
>Entry</TD
><TD
>Description</TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>value</CODE
></TD
><TD
>&#13;         Union containing this variable's contents. See 
         <A
HREF="zend.arguments.html#tab.struct-zvalue-value"
>表 46-6</A
> for a description.
        </TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>type</CODE
></TD
><TD
>&#13;         Contains this variable's type. For a list of available
         types, see <A
HREF="zend.arguments.html#tab.ztype-constants"
>表 46-7</A
>.
        </TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>is_ref</CODE
></TD
><TD
>&#13;         0 means that this variable is not a reference; 1 means that this variable is a reference to another variable.
        </TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>refcount</CODE
></TD
><TD
>&#13;         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.
        </TD
></TR
></TBODY
></TABLE
></DIV
><DIV
CLASS="table"
><A
NAME="tab.struct-zvalue-value"
></A
><P
><B
>表 46-6. Zend <CODE
CLASS="envar"
>zvalue_value</CODE
> Structure</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL
WIDTH="1*"
TITLE="col1"><COL
WIDTH="1.66*"
TITLE="col2"><TBODY
><TR
><TD
>Entry</TD
><TD
>Description</TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>lval</CODE
></TD
><TD
>Use this property if the variable is of the
         type <TT
CLASS="literal"
>IS_LONG</TT
>,
         <TT
CLASS="literal"
>IS_BOOLEAN</TT
>, or <TT
CLASS="literal"
>IS_RESOURCE</TT
>.</TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>dval</CODE
></TD
><TD
>Use this property if the variable is of the
         type <TT
CLASS="literal"
>IS_DOUBLE</TT
>.</TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>str</CODE
></TD
><TD
>&#13;         This structure can be used to access variables of
         the type <TT
CLASS="literal"
>IS_STRING</TT
>. The member <CODE
CLASS="envar"
>len</CODE
> contains the
         string length; the member <CODE
CLASS="envar"
>val</CODE
> points to the string itself. Zend
         uses C strings; thus, the string length contains a trailing
         <TT
CLASS="literal"
>0x00</TT
>.</TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>ht</CODE
></TD
><TD
>This entry points to the variable's hash table entry if the variable is an array.</TD
></TR
><TR
><TD
><CODE
CLASS="envar"
>obj</CODE
></TD
><TD
>Use this property if the variable is of the
         type <TT
CLASS="literal"
>IS_OBJECT</TT
>.</TD
></TR
></TBODY
></TABLE
></DIV
><DIV
CLASS="table"
><A
NAME="tab.ztype-constants"
></A
><P
><B
>表 46-7. Zend Variable Type Constants</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL
WIDTH="1*"
TITLE="col1"><COL
WIDTH="1.65*"
TITLE="col2"><TBODY
><TR
><TD
>Constant</TD
><TD
>Description</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_NULL</TT
></TD
><TD
>Denotes a NULL (empty) value.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_LONG</TT
></TD
><TD
>A long (integer) value.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_DOUBLE</TT
></TD
><TD
>A double (floating point) value.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_STRING</TT
></TD
><TD
>A string.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_ARRAY</TT
></TD
><TD
>Denotes an array.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_OBJECT</TT
></TD
><TD
>An object.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_BOOL</TT
></TD
><TD
>A Boolean value.</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_RESOURCE</TT
></TD
><TD
>A resource (for a discussion of resources, see the
         appropriate section below).</TD
></TR
><TR
><TD
><TT
CLASS="literal"
>IS_CONSTANT</TT
></TD
><TD
>A constant (defined) value.</TD
></TR
></TBODY
></TABLE
></DIV
><P
>&#13;    To access a long you access <CODE
CLASS="envar"
>zval.value.lval</CODE
>, to
    access a double you use <CODE
CLASS="envar"
>zval.value.dval</CODE
>, and so on.
    Because all values are stored in a union, trying to access data
    with incorrect union members results in meaningless output.
   </P
><P
>&#13;    Accessing arrays and objects is a bit more complicated and
    is discussed later.
   </P
></DIV
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.by-reference"
>Dealing with Arguments Passed by Reference</A
></H2
><P
>&#13;    If your function accepts arguments passed by reference that you
    intend to modify, you need to take some precautions.
   </P
><P
>&#13;    What we didn't say yet is that under the circumstances presented so
    far, you don't have write access to any <CODE
CLASS="envar"
>zval</CODE
> containers
    designating function parameters that have been passed to you. Of course, you
    can change any <CODE
CLASS="envar"
>zval</CODE
> containers that you created within
    your function, but you mustn't change any <CODE
CLASS="envar"
>zval</CODE
>s that refer to
    Zend-internal data!
   </P
><P
>&#13;    We've only discussed the so-called <B
CLASS="function"
>*_ex()</B
> API
    so far. You may have noticed that the API functions we've used are
    called <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> instead of
    <A
HREF="zend-api.zend-get-parameters.html"
><B
CLASS="function"
>zend_get_parameters()</B
></A
>,
    <B
CLASS="function"
>convert_to_long_ex()</B
> instead of
    <B
CLASS="function"
>convert_to_long()</B
>, etc. The
    <B
CLASS="function"
>*_ex()</B
> 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.
   </P
><P
>&#13;    Because Zend works internally with references, different variables
    may reference the same value. Write access to a
    <CODE
CLASS="envar"
>zval</CODE
> container requires this container to contain
    an isolated value, meaning a value that's not referenced by any
    other containers. If a <CODE
CLASS="envar"
>zval</CODE
> container were
    referenced by other containers and you changed the referenced
    <CODE
CLASS="envar"
>zval</CODE
>, you would automatically change the contents
    of the other containers referencing this <CODE
CLASS="envar"
>zval</CODE
>
    (because they'd simply point to the changed value and thus change
    their own value as well).
   </P
><P
>&#13;    <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> doesn't care about
    this situation, but simply returns a pointer to the desired
    <CODE
CLASS="envar"
>zval</CODE
> containers, whether they consist of references
    or not. Its corresponding function in the traditional API,
    <A
HREF="zend-api.zend-get-parameters.html"
><B
CLASS="function"
>zend_get_parameters()</B
></A
>, immediately checks for
    referenced values. If it finds a reference, it creates a new,
    isolated <CODE
CLASS="envar"
>zval</CODE
> container; copies the referenced data
    into this newly allocated space; and then returns a pointer to the
    new, isolated value.
   </P
><P
>&#13;    This action is called <SPAN
CLASS="emphasis"
><I
CLASS="emphasis"
>zval separation</I
></SPAN
>
    (or pval separation). Because the <B
CLASS="function"
>*_ex()</B
> API
    doesn't perform zval separation, it's considerably faster, while
    at the same time disabling write access.
   </P
><P
>&#13;    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 <CODE
CLASS="envar"
>$parameter</CODE
> is being passed as an isolated value, rendering it
    to a write-safe state:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>my_function(&#38;$parameter);</PRE
></TD
></TR
></TABLE
>
   </P
><P
>&#13;    But this <SPAN
CLASS="emphasis"
><I
CLASS="emphasis"
>is not</I
></SPAN
> the case with regular parameters!
    All other parameters that are not passed by reference are in a read-only
    state.
   </P
><P
>&#13;    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
    <TT
CLASS="literal"
>PZVAL_IS_REF</TT
>. This macro accepts a <TT
CLASS="literal"
>zval*</TT
>
    to check if it is a reference or not. Examples are given in
    in <A
HREF="zend.arguments.html#example.pass-by-ref"
>例 46-8</A
>.
   </P
><TABLE
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
CLASS="EXAMPLE"
><TR
><TD
><DIV
CLASS="example"
><A
NAME="example.pass-by-ref"
></A
><P
><B
>例 46-8. Testing for referenced parameter passing.</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>zval *parameter;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &#38;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);</PRE
></TD
></TR
></TABLE
><P
><IMG
SRC="figures/zendapi.zend.05-reference-test.png"></P
></DIV
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="sect2"
><H2
CLASS="sect2"
><A
NAME="zend.arguments.write-safety"
>Assuring Write Safety for Other Parameters</A
></H2
><P
>&#13;    You might run into a situation in which you need write access to a
    parameter that's retrieved with <A
HREF="zend-api.zend-get-parameters-ex.html"
><B
CLASS="function"
>zend_get_parameters_ex()</B
></A
> 
    but not passed by reference. For this case, you can use the macro
    <TT
CLASS="literal"
>SEPARATE_ZVAL</TT
>, which does a zval separation on the provided
    container. The newly generated <CODE
CLASS="envar"
>zval</CODE
> 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:
    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
CELLPADDING="5"
><TR
><TD
><PRE
CLASS="programlisting"
>zval **parameter;
     
/* retrieve parameter */
zend_get_parameters_ex(1, &#38;parameter);

/* at this stage, &#60;parameter&#62; still is connected */
/* to Zend's internal data buffers */

/* make &#60;parameter&#62; write-safe */
SEPARATE_ZVAL(parameter);

/* now we can safely modify &#60;parameter&#62; */
/* without implying global changes */</PRE
></TD
></TR
></TABLE
>
    <TT
CLASS="literal"
>SEPARATE_ZVAL</TT
> uses <B
CLASS="function"
>emalloc()</B
>
    to allocate the new <CODE
CLASS="envar"
>zval</CODE
> 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.
   </P
><P
>&#13;    <SPAN
CLASS="emphasis"
><I
CLASS="emphasis"
>Note:</I
></SPAN
> As you can easily work around the lack
    of write access in the "traditional" API (with
    <A
HREF="zend-api.zend-get-parameters.html"
><B
CLASS="function"
>zend_get_parameters()</B
></A
> and so on), this API
    seems to be obsolete, and is not discussed further in this
    chapter.
   </P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="zend.structure.html"
ACCESSKEY="P"
>上一页</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>起始页</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="zend.variables.html"
ACCESSKEY="N"
>下一页</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Source Discussion</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="zend.html"
ACCESSKEY="U"
>上一级</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Creating Variables</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>