Proposes two improvements to zend_parse_parameters()
: allow !
for non pointer types and expose zend_parse_arg()
as zend_parse_parameter()
.
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).
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.
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.
The patch is available as a branch on github.
The mailing list discussion is available here.
Voting ends not before Wednesday, January 16th 2013. The PHP language is not changed, so a 50%+1 majority is required.