From 80e90ad7bac9ea2643eb8ca9cf32b1829fbf4d6d Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Tue, 7 Mar 2023 17:15:53 +0000 Subject: [PATCH] Add number or str ZPP macros --- Zend/tests/number_or_str_zpp.phpt | 74 +++++++++++++++++++++++++++++++ Zend/zend_API.c | 30 +++++++++++++ Zend/zend_API.h | 29 ++++++++++++ ext/zend_test/test.c | 44 ++++++++++++++++++ ext/zend_test/test.stub.php | 4 ++ ext/zend_test/test_arginfo.h | 14 +++++- 6 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/number_or_str_zpp.phpt diff --git a/Zend/tests/number_or_str_zpp.phpt b/Zend/tests/number_or_str_zpp.phpt new file mode 100644 index 00000000000..6ee47ec6f63 --- /dev/null +++ b/Zend/tests/number_or_str_zpp.phpt @@ -0,0 +1,74 @@ +--TEST-- +Test Z_PARAM_NUMBER_OR_STR() and Z_PARAM_NUMBER_OR_STR_OR_NULL +--EXTENSIONS-- +zend_test +--FILE-- +getMessage() . "\n"; +} +try { + zend_number_or_string(new Foo()); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +var_dump(zend_number_or_string_or_null("string")); +var_dump(zend_number_or_string_or_null(1)); +var_dump(zend_number_or_string_or_null(5.5)); +var_dump(zend_number_or_string_or_null(null)); +var_dump(zend_number_or_string_or_null(false)); +var_dump(zend_number_or_string_or_null(true)); +var_dump(zend_number_or_string_or_null(new ToString())); + +try { + zend_number_or_string_or_null([]); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} +try { + zend_number_or_string_or_null(new Foo()); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECTF-- +string(6) "string" +int(1) +float(5.5) + +Deprecated: zend_number_or_string(): Passing null to parameter #1 ($param) of type string|int|float is deprecated in %s on line %d +int(0) +int(0) +int(1) +string(8) "ToString" +zend_string_or_object(): Argument #1 ($param) must be of type object|string, array given +zend_number_or_string(): Argument #1 ($param) must be of type string|int|float, Foo given +string(6) "string" +int(1) +float(5.5) +NULL +int(0) +int(1) +string(8) "ToString" +zend_number_or_string_or_null(): Argument #1 ($param) must be of type string|int|float|null, array given +zend_number_or_string_or_null(): Argument #1 ($param) must be of type string|int|float|null, Foo given diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 897201c7dbd..34ab803321a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -674,6 +674,36 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, u } /* }}} */ + +ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **dest, uint32_t arg_num) /* {{{ */ +{ + if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { + return false; + } + if (Z_TYPE_P(arg) < IS_TRUE) { + if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("string|int|float", arg_num)) { + return false; + } + ZVAL_LONG(arg, 0); + } else if (Z_TYPE_P(arg) == IS_TRUE) { + ZVAL_LONG(arg, 1); + } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { + zend_object *zobj = Z_OBJ_P(arg); + zval obj; + if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) { + OBJ_RELEASE(zobj); + ZVAL_COPY_VALUE(arg, &obj); + *dest = arg; + return true; + } + return false; + } else { + return false; + } + *dest = arg; + return true; +} + ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 7c13c5e860b..acdc5b06b3d 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1520,6 +1520,8 @@ static zend_always_inline zval *zend_try_array_init(zval *zv) _(Z_EXPECTED_DOUBLE_OR_NULL, "of type ?float") \ _(Z_EXPECTED_NUMBER, "of type int|float") \ _(Z_EXPECTED_NUMBER_OR_NULL, "of type int|float|null") \ + _(Z_EXPECTED_NUMBER_OR_STRING, "of type string|int|float") \ + _(Z_EXPECTED_NUMBER_OR_STRING_OR_NULL, "of type string|int|float|null") \ _(Z_EXPECTED_ARRAY_OR_STRING, "of type array|string") \ _(Z_EXPECTED_ARRAY_OR_STRING_OR_NULL, "of type array|string|null") \ _(Z_EXPECTED_STRING_OR_LONG, "of type string|int") \ @@ -1891,6 +1893,20 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * #define Z_PARAM_NUMBER(dest) \ Z_PARAM_NUMBER_EX(dest, 0) +#define Z_PARAM_NUMBER_OR_STR_EX(dest, check_null) \ + Z_PARAM_PROLOGUE(0, 0); \ + if (UNEXPECTED(!zend_parse_arg_number_or_str(_arg, &dest, check_null, _i))) { \ + _expected_type = check_null ? Z_EXPECTED_NUMBER_OR_STRING_OR_NULL : Z_EXPECTED_NUMBER_OR_STRING; \ + _error_code = ZPP_ERROR_WRONG_ARG; \ + break; \ + } + +#define Z_PARAM_NUMBER_OR_STR(dest) \ + Z_PARAM_NUMBER_OR_STR_EX(dest, false) + +#define Z_PARAM_NUMBER_OR_STR_OR_NULL(dest) \ + Z_PARAM_NUMBER_OR_STR_EX(dest, true) + /* old "o" */ #define Z_PARAM_OBJECT_EX(dest, check_null, deref) \ Z_PARAM_PROLOGUE(deref, 0); \ @@ -2145,6 +2161,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double * ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num); +ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num); static zend_always_inline bool zend_parse_arg_bool(const zval *arg, bool *dest, bool *is_null, bool check_null, uint32_t arg_num) @@ -2209,6 +2226,18 @@ static zend_always_inline bool zend_parse_arg_number(zval *arg, zval **dest, boo return 1; } +static zend_always_inline bool zend_parse_arg_number_or_str(zval *arg, zval **dest, bool check_null, uint32_t arg_num) +{ + if (EXPECTED(Z_TYPE_P(arg) == IS_LONG || Z_TYPE_P(arg) == IS_DOUBLE || Z_TYPE_P(arg) == IS_STRING)) { + *dest = arg; + } else if (check_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) { + *dest = NULL; + } else { + return zend_parse_arg_number_or_str_slow(arg, dest, arg_num); + } + return true; +} + static zend_always_inline bool zend_parse_arg_str(zval *arg, zend_string **dest, bool check_null, uint32_t arg_num) { if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 809031ca33a..aade7c5674f 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -266,6 +266,50 @@ static ZEND_FUNCTION(zend_string_or_stdclass_or_null) } } +/* Tests Z_PARAM_NUMBER_OR_STR */ +static ZEND_FUNCTION(zend_number_or_string) +{ + zval *input; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_NUMBER_OR_STR(input) + ZEND_PARSE_PARAMETERS_END(); + + switch (Z_TYPE_P(input)) { + case IS_LONG: + RETURN_LONG(Z_LVAL_P(input)); + case IS_DOUBLE: + RETURN_DOUBLE(Z_DVAL_P(input)); + case IS_STRING: + RETURN_STR_COPY(Z_STR_P(input)); + EMPTY_SWITCH_DEFAULT_CASE(); + } +} + +/* Tests Z_PARAM_NUMBER_OR_STR_OR_NULL */ +static ZEND_FUNCTION(zend_number_or_string_or_null) +{ + zval *input; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_NUMBER_OR_STR_OR_NULL(input) + ZEND_PARSE_PARAMETERS_END(); + + if (!input) { + RETURN_NULL(); + } + + switch (Z_TYPE_P(input)) { + case IS_LONG: + RETURN_LONG(Z_LVAL_P(input)); + case IS_DOUBLE: + RETURN_DOUBLE(Z_DVAL_P(input)); + case IS_STRING: + RETURN_STR_COPY(Z_STR_P(input)); + EMPTY_SWITCH_DEFAULT_CASE(); + } +} + static ZEND_FUNCTION(zend_weakmap_attach) { zval *value; diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 7228021b314..a7913a8971a 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -162,6 +162,10 @@ namespace { /** @param stdClass|string|null $param */ function zend_string_or_stdclass_or_null($param): stdClass|string|null {} + function zend_number_or_string(string|int|float $param): string|int|float {} + + function zend_number_or_string_or_null(string|int|float|null $param): string|int|float|null {} + function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {} function zend_weakmap_attach(object $object, mixed $value): bool {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index d3b26b652d0..83997368a34 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 58ae12118458386ab204a2400966c25c833baeae */ + * Stub hash: c526a3e961501a511b210eab38bb961ca61bb258 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -56,6 +56,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_zend_string_or_stdclass_or_n ZEND_ARG_INFO(0, param) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_number_or_string, 0, 1, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE) + ZEND_ARG_TYPE_MASK(0, param, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_number_or_string_or_null, 0, 1, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_NULL) + ZEND_ARG_TYPE_MASK(0, param, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_NULL, NULL) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_iterable, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_TYPE_MASK(0, arg1, Traversable, MAY_BE_ARRAY, NULL) ZEND_ARG_OBJ_TYPE_MASK(0, arg2, Traversable, MAY_BE_ARRAY|MAY_BE_NULL, "null") @@ -209,6 +217,8 @@ static ZEND_FUNCTION(zend_string_or_object); static ZEND_FUNCTION(zend_string_or_object_or_null); static ZEND_FUNCTION(zend_string_or_stdclass); static ZEND_FUNCTION(zend_string_or_stdclass_or_null); +static ZEND_FUNCTION(zend_number_or_string); +static ZEND_FUNCTION(zend_number_or_string_or_null); static ZEND_FUNCTION(zend_iterable); static ZEND_FUNCTION(zend_weakmap_attach); static ZEND_FUNCTION(zend_weakmap_remove); @@ -272,6 +282,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_string_or_object_or_null, arginfo_zend_string_or_object_or_null) ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass) ZEND_FE(zend_string_or_stdclass_or_null, arginfo_zend_string_or_stdclass_or_null) + ZEND_FE(zend_number_or_string, arginfo_zend_number_or_string) + ZEND_FE(zend_number_or_string_or_null, arginfo_zend_number_or_string_or_null) ZEND_FE(zend_iterable, arginfo_zend_iterable) ZEND_FE(zend_weakmap_attach, arginfo_zend_weakmap_attach) ZEND_FE(zend_weakmap_remove, arginfo_zend_weakmap_remove)