diff options
-rw-r--r-- | Zend/tests/str_or_obj_of_class_zpp.phpt | 61 | ||||
-rw-r--r-- | Zend/tests/str_or_obj_zpp.phpt | 53 | ||||
-rw-r--r-- | Zend/zend_API.c | 20 | ||||
-rw-r--r-- | Zend/zend_API.h | 82 | ||||
-rw-r--r-- | ext/zend_test/test.c | 76 | ||||
-rw-r--r-- | ext/zend_test/test.stub.php | 10 | ||||
-rw-r--r-- | ext/zend_test/test_arginfo.h | 26 |
7 files changed, 320 insertions, 8 deletions
diff --git a/Zend/tests/str_or_obj_of_class_zpp.phpt b/Zend/tests/str_or_obj_of_class_zpp.phpt new file mode 100644 index 0000000000..b8c38ba060 --- /dev/null +++ b/Zend/tests/str_or_obj_of_class_zpp.phpt @@ -0,0 +1,61 @@ +--TEST-- +Test Z_PARAM_STR_OR_OBJ_OF_CLASS() and Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL +--SKIPIF-- +<?php +if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +?> +--FILE-- +<?php + +class Foo {} + +var_dump(zend_string_or_stdclass("string")); +var_dump(zend_string_or_stdclass(1)); +var_dump(zend_string_or_stdclass(null)); +var_dump(zend_string_or_stdclass(new stdClass())); + +try { + zend_string_or_stdclass([]); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +try { + zend_string_or_stdclass(new Foo()); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +var_dump(zend_string_or_stdclass_or_null("string")); +var_dump(zend_string_or_stdclass_or_null(1)); +var_dump(zend_string_or_stdclass_or_null(null)); +var_dump(zend_string_or_stdclass_or_null(new stdClass())); + +try { + zend_string_or_stdclass_or_null([]); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +try { + zend_string_or_stdclass_or_null(new Foo()); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +string(6) "string" +string(1) "1" +string(0) "" +object(stdClass)#1 (0) { +} +zend_string_or_stdclass(): Argument #1 ($param) must be of type stdClass|string, array given +zend_string_or_stdclass(): Argument #1 ($param) must be of type stdClass|string, Foo given +string(6) "string" +string(1) "1" +NULL +object(stdClass)#1 (0) { +} +zend_string_or_stdclass_or_null(): Argument #1 ($param) must be of type stdClass|string|null, array given +zend_string_or_stdclass_or_null(): Argument #1 ($param) must be of type stdClass|string|null, Foo given diff --git a/Zend/tests/str_or_obj_zpp.phpt b/Zend/tests/str_or_obj_zpp.phpt new file mode 100644 index 0000000000..386558e02a --- /dev/null +++ b/Zend/tests/str_or_obj_zpp.phpt @@ -0,0 +1,53 @@ +--TEST-- +Test Z_PARAM_STR_OR_OBJ() and Z_PARAM_STR_OR_OBJ_OR_NULL +--SKIPIF-- +<?php +if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +?> +--FILE-- +<?php + +class Foo {} + +var_dump(zend_string_or_object("string")); +var_dump(zend_string_or_object(1)); +var_dump(zend_string_or_object(null)); +var_dump(zend_string_or_object(new stdClass())); +var_dump(zend_string_or_object(new Foo())); + +try { + zend_string_or_object([]); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +var_dump(zend_string_or_object_or_null("string")); +var_dump(zend_string_or_object_or_null(1)); +var_dump(zend_string_or_object_or_null(null)); +var_dump(zend_string_or_object_or_null(new stdClass())); +var_dump(zend_string_or_object_or_null(new Foo())); + +try { + zend_string_or_object_or_null([]); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +string(6) "string" +string(1) "1" +string(0) "" +object(stdClass)#1 (0) { +} +object(Foo)#1 (0) { +} +zend_string_or_object(): Argument #1 ($param) must be of type object|string, array given +string(6) "string" +string(1) "1" +NULL +object(stdClass)#2 (0) { +} +object(Foo)#2 (0) { +} +zend_string_or_object_or_null(): Argument #1 ($param) must be of type object|string|null, array given diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 14075847ad..4aa0103ae5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -252,6 +252,26 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(i } /* }}} */ +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_error(int num, const char *name, zval *arg) /* {{{ */ +{ + if (EG(exception)) { + return; + } + + zend_argument_type_error(num, "must be of type %s|string, %s given", name, zend_zval_type_name(arg)); +} +/* }}} */ + +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_or_null_error(int num, const char *name, zval *arg) /* {{{ */ +{ + if (EG(exception)) { + return; + } + + zend_argument_type_error(num, "must be of type %s|string|null, %s given", name, zend_zval_type_name(arg)); +} +/* }}} */ + ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int num, char *error) /* {{{ */ { if (EG(exception)) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index ae0363f765..578cf629f8 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1219,6 +1219,8 @@ static zend_always_inline zval *zend_try_array_init(zval *zv) _(Z_EXPECTED_STRING_OR_LONG_OR_NULL, "of type string|int|null") \ _(Z_EXPECTED_CLASS_NAME_OR_OBJECT, "a valid class name or object") \ _(Z_EXPECTED_CLASS_NAME_OR_OBJECT_OR_NULL, "a valid class name, object, or null") \ + _(Z_EXPECTED_STRING_OR_OBJECT, "of type object|string") \ + _(Z_EXPECTED_STRING_OR_OBJECT_OR_NULL, "of type object|string|null") \ #define Z_EXPECTED_TYPE @@ -1235,18 +1237,22 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(int min_ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(int num, zend_expected_type expected_type, zval *arg); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(int num, const char *name, zval *arg); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(int num, const char *name, zval *arg); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_error(int num, const char *name, zval *arg); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_or_null_error(int num, const char *name, zval *arg); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int num, char *error); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_type_error(uint32_t arg_num, const char *format, ...); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num, const char *format, ...); -#define ZPP_ERROR_OK 0 -#define ZPP_ERROR_FAILURE 1 -#define ZPP_ERROR_WRONG_CALLBACK 2 -#define ZPP_ERROR_WRONG_CLASS 3 -#define ZPP_ERROR_WRONG_CLASS_OR_NULL 4 -#define ZPP_ERROR_WRONG_ARG 5 -#define ZPP_ERROR_WRONG_COUNT 6 +#define ZPP_ERROR_OK 0 +#define ZPP_ERROR_FAILURE 1 +#define ZPP_ERROR_WRONG_CALLBACK 2 +#define ZPP_ERROR_WRONG_CLASS 3 +#define ZPP_ERROR_WRONG_CLASS_OR_NULL 4 +#define ZPP_ERROR_WRONG_ARG 5 +#define ZPP_ERROR_WRONG_COUNT 6 +#define ZPP_ERROR_WRONG_STRING_OR_CLASS 7 +#define ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL 8 #define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \ const int _flags = (flags); \ @@ -1301,6 +1307,10 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num zend_wrong_parameter_class_or_null_error(_i, _error, _arg); \ } else if (_error_code == ZPP_ERROR_WRONG_ARG) { \ zend_wrong_parameter_type_error(_i, _expected_type, _arg); \ + } else if (_error_code == ZPP_ERROR_WRONG_STRING_OR_CLASS) { \ + zend_wrong_parameter_string_or_class_error(_i, _error, _arg); \ + } else if (_error_code == ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL) { \ + zend_wrong_parameter_string_or_class_or_null_error(_i, _error, _arg); \ } \ } \ failure; \ @@ -1411,6 +1421,40 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num #define Z_PARAM_CLASS_NAME_OR_OBJ_OR_NULL(dest) \ Z_PARAM_CLASS_NAME_OR_OBJ_EX(dest, 1); +#define Z_PARAM_STR_OR_OBJ_EX(destination_string, destination_object, allow_null) \ + Z_PARAM_PROLOGUE(0, 0); \ + if (UNEXPECTED(!zend_parse_arg_str_or_obj(_arg, &destination_string, &destination_object, NULL, allow_null))) { \ + _expected_type = allow_null ? Z_EXPECTED_STRING_OR_OBJECT_OR_NULL : Z_EXPECTED_STRING_OR_OBJECT; \ + _error_code = ZPP_ERROR_WRONG_ARG; \ + break; \ + } + +#define Z_PARAM_STR_OR_OBJ(destination_string, destination_object) \ + Z_PARAM_STR_OR_OBJ_EX(destination_string, destination_object, 0); + +#define Z_PARAM_STR_OR_OBJ_OR_NULL(destination_string, destination_object) \ + Z_PARAM_STR_OR_OBJ_EX(destination_string, destination_object, 1); + +#define Z_PARAM_STR_OR_OBJ_OF_CLASS_EX(destination_string, destination_object, base_ce, allow_null) \ + Z_PARAM_PROLOGUE(0, 0); \ + if (UNEXPECTED(!zend_parse_arg_str_or_obj(_arg, &destination_string, &destination_object, base_ce, allow_null))) { \ + if (base_ce) { \ + _error = ZSTR_VAL((base_ce)->name); \ + _error_code = allow_null ? ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL : ZPP_ERROR_WRONG_STRING_OR_CLASS; \ + break; \ + } else { \ + _expected_type = allow_null ? Z_EXPECTED_STRING_OR_OBJECT_OR_NULL : Z_EXPECTED_STRING_OR_OBJECT; \ + _error_code = ZPP_ERROR_WRONG_ARG; \ + break; \ + } \ + } + +#define Z_PARAM_STR_OR_OBJ_OF_CLASS(destination_string, destination_object, base_ce) \ + Z_PARAM_STR_OR_OBJ_OF_CLASS_EX(destination_string, destination_object, base_ce, 0); + +#define Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL(destination_string, destination_object, base_ce) \ + Z_PARAM_STR_OR_OBJ_OF_CLASS_EX(destination_string, destination_object, base_ce, 1); + /* old "d" */ #define Z_PARAM_DOUBLE_EX2(dest, is_null, check_null, deref, separate) \ Z_PARAM_PROLOGUE(deref, separate); \ @@ -1972,6 +2016,30 @@ static zend_always_inline int zend_parse_arg_class_name_or_obj( return 1; } +static zend_always_inline int zend_parse_arg_str_or_obj( + zval *arg, zend_string **destination_string, zend_object **destination_object, zend_class_entry *base_ce, int allow_null +) { + if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { + if (base_ce && UNEXPECTED(!instanceof_function(Z_OBJCE_P(arg), base_ce))) { + return 0; + } + + *destination_string = NULL; + *destination_object = Z_OBJ_P(arg); + } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { + *destination_string = Z_STR_P(arg); + *destination_object = NULL; + } else if (allow_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) { + *destination_string = NULL; + *destination_object = NULL; + } else { + *destination_object = NULL; + return zend_parse_arg_str_slow(arg, destination_string); + } + + return 1; +} + END_EXTERN_C() #endif /* ZEND_API_H */ diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index bc412305ed..92408866d6 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -132,6 +132,82 @@ ZEND_FUNCTION(zend_leak_variable) } /* }}} */ +/* Tests Z_PARAM_STR_OR_OBJ */ +ZEND_FUNCTION(zend_string_or_object) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR_OR_OBJ(str, object) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else { + RETURN_OBJ_COPY(object); + } +} +/* }}} */ + +/* Tests Z_PARAM_STR_OR_OBJ_OR_NULL */ +ZEND_FUNCTION(zend_string_or_object_or_null) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR_OR_OBJ_OR_NULL(str, object) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else if (object) { + RETURN_OBJ_COPY(object); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* Tests Z_PARAM_STR_OR_OBJ_OF_CLASS */ +ZEND_FUNCTION(zend_string_or_stdclass) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR_OR_OBJ_OF_CLASS(str, object, zend_standard_class_def) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else { + RETURN_OBJ_COPY(object); + } +} +/* }}} */ + +/* Tests Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL */ +ZEND_FUNCTION(zend_string_or_stdclass_or_null) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL(str, object, zend_standard_class_def) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else if (object) { + RETURN_OBJ_COPY(object); + } else { + RETURN_NULL(); + } +} +/* }}} */ + static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ { zend_object *obj = zend_objects_new(class_type); object_properties_init(obj, class_type); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 232c268168..0cc00b4a32 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -29,3 +29,13 @@ function zend_terminate_string(string &$str): void {} function zend_leak_variable(mixed $variable): void {} function zend_leak_bytes(int $bytes = 3): void {} + +function zend_string_or_object(object|string $param): object|string {} + +function zend_string_or_object_or_null(object|string|null $param): object|string|null {} + +/** @param stdClass|string $param */ +function zend_string_or_stdclass($param): stdClass|string {} + +/** @param stdClass|string|null $param */ +function zend_string_or_stdclass_or_null($param): stdClass|string|null {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 334d9caea3..d65ce2220c 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: a61df45ef2431d449b0ac546640101db8ce66445 */ + * Stub hash: d04baf2ffeb73d2e48f806316407ec720ea6f176 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -28,6 +28,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_leak_bytes, 0, 0, IS_VOID, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bytes, IS_LONG, 0, "3") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_string_or_object, 0, 1, MAY_BE_OBJECT|MAY_BE_STRING) + ZEND_ARG_TYPE_MASK(0, param, MAY_BE_OBJECT|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_string_or_object_or_null, 0, 1, MAY_BE_OBJECT|MAY_BE_STRING|MAY_BE_NULL) + ZEND_ARG_TYPE_MASK(0, param, MAY_BE_OBJECT|MAY_BE_STRING|MAY_BE_NULL, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_zend_string_or_stdclass, 0, 1, stdClass, MAY_BE_STRING) + ZEND_ARG_INFO(0, param) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_zend_string_or_stdclass_or_null, 0, 1, stdClass, MAY_BE_STRING|MAY_BE_NULL) + ZEND_ARG_INFO(0, param) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_is_object, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -46,6 +62,10 @@ ZEND_FUNCTION(zend_create_unterminated_string); ZEND_FUNCTION(zend_terminate_string); ZEND_FUNCTION(zend_leak_variable); ZEND_FUNCTION(zend_leak_bytes); +ZEND_FUNCTION(zend_string_or_object); +ZEND_FUNCTION(zend_string_or_object_or_null); +ZEND_FUNCTION(zend_string_or_stdclass); +ZEND_FUNCTION(zend_string_or_stdclass_or_null); ZEND_METHOD(_ZendTestClass, is_object); ZEND_METHOD(_ZendTestClass, __toString); ZEND_METHOD(_ZendTestTrait, testMethod); @@ -60,6 +80,10 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string) ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) + ZEND_FE(zend_string_or_object, arginfo_zend_string_or_object) + 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_END }; |