Dealing with a Variable Number of Arguments/Optional Parameters
    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
    zend_get_parameters_ex() for every possible
    number of arguments, which is often unsatisfying.
   
    For this case, you can use the
    function zend_get_parameters_array_ex(), which accepts the
    number of parameters to retrieve and an array in which to store them: 
    
| 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 > 5)
    WRONG_PARAM_COUNT;
/* argument count is correct, now retrieve arguments */
if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS)
    WRONG_PARAM_COUNT; | 
    First, the number of arguments is checked to make sure that it's in the accepted range. After that,
    
zend_get_parameters_array_ex() is used to
    fill 
parameter_array with valid pointers to the argument
    values.
   
    A very clever implementation of this can be found in the code
    handling PHP's fsockopen() located in
    ext/standard/fsock.c, as shown in 
    例子 32-1. Don't worry if you don't know all the functions used in this
    source yet; we'll get to them shortly.
   
| 例子 32-1. PHP's implementation of variable arguments in fsockopen(). | 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_P(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); | 
 | 
    fsockopen() 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 switch() statement
    to deal with all arguments. The  switch()
    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 break keyword in all stages. After
    having processed the last case, it exits the
    switch() statement and does the minimal
    argument processing needed if the function is invoked with only
    two arguments.
   
    This multiple-stage type of processing, similar to a stairway, allows
    convenient processing of a variable number of arguments.