Manuel PHP
Précédent | Chapitre 32. Accepting Arguments | Suivant

# 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 Listing 9.7. Don't worry if you don't know all the functions used in this source yet; we'll get to them shortly.

Figure 32-2. Listing 9.7. 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) ((*args[4])->value.dval * 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]);
            (*args[3])->value.str.val = empty_string;
            (*args[3])->value.str.len = 0;
            (*args[3])->type = IS_STRING;
            /* fall-through */
        case 3:
            if (!PZVAL_IS_REF(*args[2])) {
                php_error(E_WARNING,"error argument to fsockopen not passed by reference");
                return;
            }
            (*args[2])->type = IS_LONG;
            (*args[2])->value.lval = 0;
            break;
    }

    convert_to_string_ex(args[0]);
    convert_to_long_ex(args[1]);
    portno = (unsigned short) (*args[1])->value.lval;

    key = emalloc((*args[0])->value.str.len + 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.

Précédent | Sommaire | Suivant
Old way of retrieving arguments (deprecated) | Niveau supérieur | Accessing Arguments