Table of Contents

Request for Comments: zend_parse_parameters() improvements

Proposes two improvements to zend_parse_parameters(): allow ! for non pointer types and expose zend_parse_arg() as zend_parse_parameter().

Introduction

The native function zend_parse_parameters() (henceforth zpp) is used by internal PHP functions to parse the function arguments into native types or to merely extract the zvals from the stack. It's a variadic function that takes a format string and a matching list of pointers that serve as output parameters.

The conversions that zpp does to transform the passed zvals into native types are not trivial. For instance, it can convert a string into a “long”, but not all strings will be accepted and some will be accepted but will emit a warning. As many variables end up at some point as arguments to internal functions, the behavior of zpp ends affecting how PHP's users perceive PHP's type system to work. Consequently, consistency in internal function parameter parsing is highly desirable. As zpp behavior is not trivial, achieving such consistency depends on PHP internal functions' implementations using zpp correctly.

Unfortunately, zpp is also inflexible. One is restricted to a single format string, which makes implementing overloads difficult. Common solutions include calling zpp quietly several times until it succeeds (signaling a correct set of arguments) and fetching the zvals without conversion, following with explicit conversions (hopefully with convert_to_xx_ex(), not simply convert_to_xx()). These can cause several problems -- confusing error messages (or absence of them) and unanticipated side effects of failed zpp calls (which change the arguments directly in the stack, as part as the conversion procedure, until it finds a failing condition) in the first case, and using casting behavior instead of zpp behavior in the second case (convert_to_int_ex() will happily and silently convert a resource into an integer).

Expose zend_parse_arg() as zend_parse_parameter()

int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...)

Exposing zend_parse_arg() as zend_parse_parameter() is the first change. It allows one to do type conversions with zpp-like semantics. These are specially useful in scenarios where a parameter may take several scalar types. For instance, one may want to accept an arbitrary number of integers (which we want to parse with zpp semantics) and expect them in an array, perhaps even also allowing a bare integer too; one may want to accept something that will be used as an array key (which can be either and int or a string); one may want to accept integer and double numbers. A search for IS_LONG reveals many situations where this need arises.

Allow ! for non pointers

long l;
zend_bool is_null;
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l!", &l, &is_null)

Allowing ! for non pointer types l/L, d and b through an extra output pointer is the second change.

For the specifiers l/L, d and b, NULL values are reported as 0, 0., or false. But sometimes one wants to distinguish NULL from those other values -- for instance, to give NULL the same effect as the argument not having been passed.

Currently, the only way to otherwise have zpp semantics (without duplicating its logic) is to fetch the raw zval from the stack and check whether it's NULL (with zpp itself or its relatives) and then run zpp again. That is not an elegant solution.

Since this came up in the discussion: this change is only marginally related to “skipped parameters proposal” and no one preempts (or even makes significantly less useful) the other. If you want to bring this up on the list, please make sure you have read carefully these two posts: post one and post two.

Proposal and Patch

The patch is available as a branch on github.

Mailing list discussion

The mailing list discussion is available here.

Voting

Voting ends not before Wednesday, January 16th 2013. The PHP language is not changed, so a 50%+1 majority is required.

Accept these improvements for PHP 5.5?
Real name Yes No
aharvey (aharvey)  
cataphract (cataphract)  
derick (derick)  
ircmaxell (ircmaxell)  
kalle (kalle)  
levim (levim)  
lstrojny (lstrojny)  
pierrick (pierrick)  
stas (stas)  
Final result: 9 0
This poll has been closed.

Changelog