mirror of
https://github.com/php/php-src.git
synced 2024-12-17 22:09:12 +08:00
8aa9727037
* Out-of-range numbers overflow/preserve least significant bits (no LONG_MAX/MIN limit) * See bug #42868 (presumably-rare platform with different results in 5.2) * On 32-bit platforms with 64-bit long type, a zend_long64 cast has been added, otherwise it's the same as 5.2 * Use this conversion method everywhere instead of some plain (long) casts Added 'L' parameter parsing specifier to ensure a LONG_MAX/MIN limit: * Essentially what 5.3's new conversion was doing in most cases * Functions with "limit" or "length" type params could be updated to use this, and prevent confusing overflow behavior with huge numbers (*also* in 5.2) - See bug #47854, for example; or even #42868 again # Test updates coming
282 lines
8.5 KiB
Plaintext
282 lines
8.5 KiB
Plaintext
New parameter parsing functions
|
|
===============================
|
|
|
|
It should be easier to parse input parameters to an extension function.
|
|
Hence, borrowing from Python's example, there are now a set of functions
|
|
that given the string of type specifiers, can parse the input parameters
|
|
and store the results in the user specified variables. This avoids most
|
|
of the IS_* checks and convert_to_* conversions. The functions also
|
|
check for the appropriate number of parameters, and try to output
|
|
meaningful error messages.
|
|
|
|
|
|
Prototypes
|
|
----------
|
|
/* Implemented. */
|
|
int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...);
|
|
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
|
|
|
|
The zend_parse_parameters() function takes the number of parameters
|
|
passed to the extension function, the type specifier string, and the
|
|
list of pointers to variables to store the results in. The _ex() version
|
|
also takes 'flags' argument -- current only ZEND_PARSE_PARAMS_QUIET can
|
|
be used as 'flags' to specify that the function should operate quietly
|
|
and not output any error messages.
|
|
|
|
Both functions return SUCCESS or FAILURE depending on the result.
|
|
|
|
The auto-conversions are performed as necessary. Arrays, objects, and
|
|
resources cannot be auto-converted.
|
|
|
|
|
|
Type specifiers
|
|
---------------
|
|
The following list shows the type specifier, its meaning and the parameter
|
|
types that need to be passed by address. All passed paramaters are set
|
|
if the PHP parameter is non optional and untouched if optional and the
|
|
parameter is not present. The only exception is O where the zend_class_entry*
|
|
has to be provided on input and is used to verify the PHP parameter is an
|
|
instance of that class.
|
|
|
|
a - array (zval*)
|
|
A - array or object (zval *)
|
|
b - boolean (zend_bool)
|
|
C - class (zend_class_entry*)
|
|
d - double (double)
|
|
f - function or array containing php method call info (returned as
|
|
zend_fcall_info and zend_fcall_info_cache)
|
|
h - array (returned as HashTable*)
|
|
H - array or HASH_OF(object) (returned as HashTable*)
|
|
l - long (long)
|
|
L - long, limits out-of-range numbers to LONG_MAX/LONG_MIN (long)
|
|
o - object of any type (zval*)
|
|
O - object of specific type given by class entry (zval*, zend_class_entry)
|
|
r - resource (zval*)
|
|
s - string (with possible null bytes) and its length (char*, int)
|
|
S - binary string, does not allow conversion from Unicode strings
|
|
t - text (zstr (string union), int (length), zend_uchar (IS_STRING/..))
|
|
accepts either Unicode or binary string
|
|
T - text (zstr (string union), int (length), zend_uchar (IS_STRING/..))
|
|
coalesces all T parameters to common type (Unicode or binary)
|
|
u - unicode (UChar*, int)
|
|
U - Unicode string, does not allow conversion from binary strings
|
|
x - Unicode or binary string depending on UG(unicode). In unicode this
|
|
behaves like 'u' and in nonunicode mode it behaves like 's'.
|
|
z - the actual zval (zval*)
|
|
Z - the actual zval (zval**)
|
|
* - variable arguments list (0 or more)
|
|
+ - variable arguments list (1 or more)
|
|
|
|
The following characters also have a meaning in the specifier string:
|
|
| - indicates that the remaining parameters are optional, they
|
|
should be initialized to default values by the extension since they
|
|
will not be touched by the parsing function if they are not
|
|
passed to it.
|
|
/ - use SEPARATE_ZVAL_IF_NOT_REF() on the parameter it follows
|
|
! - the parameter it follows can be of specified type or NULL (applies
|
|
to all specifiers except for 'b', 'l', and 'd'). If NULL is passed, the
|
|
results pointer is set to NULL as well.
|
|
& - alternate format (currently used for 's' only to specify a converter to
|
|
use when converting from Unicode strings)
|
|
^ - returns original string type before conversion (only for 's' and 'u'
|
|
specifiers)
|
|
|
|
|
|
Note on 64bit compatibility
|
|
---------------------------
|
|
Please do not forget that int and long are two different things on 64bit
|
|
OSes (int is 4 bytes and long is 8 bytes), so make sure you pass longs to "l"
|
|
and ints to strings length (i.e. for "s" you need to pass char * and int),
|
|
not the other way round!
|
|
Remember: "l" is the only case when you need to pass long (and that's why
|
|
it's "l", not "i" btw).
|
|
|
|
Both mistakes cause memory corruptions and segfaults on 64bit OSes:
|
|
1)
|
|
char *str;
|
|
long str_len; /* XXX THIS IS WRONG!! Use int instead. */
|
|
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len)
|
|
|
|
2)
|
|
int num; /* XXX THIS IS WRONG!! Use long instead. */
|
|
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &num)
|
|
|
|
If you're in doubt, use check_parameters.php script to the parameters
|
|
and their types (it can be found in ./scripts/dev/ directory of PHP sources):
|
|
|
|
# php ./scripts/dev/check_parameters.php /path/to/your/sources/
|
|
|
|
|
|
Examples
|
|
--------
|
|
/* 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",
|
|
&l, &s, &s_len, ¶m) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
|
|
/* Gets an object of class specified by my_ce, and an optional double. */
|
|
zval *obj;
|
|
double d = 0.5;
|
|
zend_class_entry *my_ce;
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|d",
|
|
&obj, my_ce, &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",
|
|
&obj, &arr) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
|
|
/* Gets a separated array which can also be null. */
|
|
zval *arr;
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!",
|
|
&arr) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
|
|
/* Gets a binary string in UTF-8 */
|
|
char *str;
|
|
int str_len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&", &str, &str_len, UG(utf8_conv)) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
|
|
/* Gets a Unicode string */
|
|
UChar *str;
|
|
int len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "u", &str, &len) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
|
|
/* Gets a Unicode or binary string */
|
|
zstr str;
|
|
int len;
|
|
zend_uchar type;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &len, &type) == FAILURE) {
|
|
return;
|
|
}
|
|
if (type == IS_UNICODE) {
|
|
/* process str.u as Unicode string */
|
|
} else {
|
|
/* process str.s binary string */
|
|
}
|
|
|
|
|
|
/* Gets two string parameters, both of which will be guaranteed to be of the same type */
|
|
zstr str1, str2;
|
|
int len1, len2;
|
|
zend_uchar type1, type2;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "TT", &str1, &len1,
|
|
&type1, &str2, &len2, &type2) == FAILURE) {
|
|
return;
|
|
}
|
|
if (type1 == IS_UNICODE) {
|
|
/* process as Unicode, str2 is guaranteed to be Unicode as well */
|
|
} else {
|
|
/* process as binary string, str2 is guaranteed to be the same */
|
|
}
|
|
|
|
|
|
/* Get either a set of 3 longs or a string. */
|
|
long l1, l2, l3;
|
|
char *s;
|
|
/*
|
|
* The function expects a pointer to a integer in this case, not a long
|
|
* or any other type. If you specify a type which is larger
|
|
* than a 'int', the upper bits might not be initialized
|
|
* properly, leading to random crashes on platforms like
|
|
* Tru64 or Linux/Alpha.
|
|
*/
|
|
int length;
|
|
|
|
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
|
|
"lll", &l1, &l2, &l3) == SUCCESS) {
|
|
/* manipulate longs */
|
|
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
|
|
"s", &s, &length) == SUCCESS) {
|
|
/* manipulate string */
|
|
} else {
|
|
/* output error */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Function that accepts only varargs (0 or more) */
|
|
|
|
int i, num_varargs;
|
|
zval ***varargs = NULL;
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*", &varargs, &num_varargs) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < num_varargs; i++) {
|
|
/* do something with varargs[i] */
|
|
}
|
|
|
|
if (varargs) {
|
|
efree(varargs);
|
|
}
|
|
|
|
|
|
/* Function that accepts a string, followed by varargs (1 or more) */
|
|
|
|
char *str;
|
|
int str_len;
|
|
int i, num_varargs;
|
|
zval ***varargs = NULL;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s+", &str, &str_len, &varargs, &num_varargs) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < num_varargs; i++) {
|
|
/* do something with varargs[i] */
|
|
}
|
|
|
|
if (varargs) {
|
|
efree(varargs);
|
|
}
|
|
|
|
|
|
/* Function that takes an array, followed by varargs, and ending with a long */
|
|
long num;
|
|
zval *array;
|
|
int i, num_varargs;
|
|
zval ***varargs = NULL;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a*l", &array, &varargs, &num_varargs, &num) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < num_varargs; i++) {
|
|
/* do something with varargs[i] */
|
|
}
|
|
|
|
if (varargs) {
|
|
efree(varargs);
|
|
}
|
|
|