Skip to content

Commit b18b2c8

Browse files
committed
Add string or object ZPP macros
Closes GH-5788
1 parent a4b253c commit b18b2c8

File tree

7 files changed

+320
-8
lines changed

7 files changed

+320
-8
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Test Z_PARAM_STR_OR_OBJ_OF_CLASS() and Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
class Foo {}
11+
12+
var_dump(zend_string_or_stdclass("string"));
13+
var_dump(zend_string_or_stdclass(1));
14+
var_dump(zend_string_or_stdclass(null));
15+
var_dump(zend_string_or_stdclass(new stdClass()));
16+
17+
try {
18+
zend_string_or_stdclass([]);
19+
} catch (TypeError $exception) {
20+
echo $exception->getMessage() . "\n";
21+
}
22+
23+
try {
24+
zend_string_or_stdclass(new Foo());
25+
} catch (TypeError $exception) {
26+
echo $exception->getMessage() . "\n";
27+
}
28+
29+
var_dump(zend_string_or_stdclass_or_null("string"));
30+
var_dump(zend_string_or_stdclass_or_null(1));
31+
var_dump(zend_string_or_stdclass_or_null(null));
32+
var_dump(zend_string_or_stdclass_or_null(new stdClass()));
33+
34+
try {
35+
zend_string_or_stdclass_or_null([]);
36+
} catch (TypeError $exception) {
37+
echo $exception->getMessage() . "\n";
38+
}
39+
40+
try {
41+
zend_string_or_stdclass_or_null(new Foo());
42+
} catch (TypeError $exception) {
43+
echo $exception->getMessage() . "\n";
44+
}
45+
46+
?>
47+
--EXPECT--
48+
string(6) "string"
49+
string(1) "1"
50+
string(0) ""
51+
object(stdClass)#1 (0) {
52+
}
53+
zend_string_or_stdclass(): Argument #1 ($param) must be of type stdClass|string, array given
54+
zend_string_or_stdclass(): Argument #1 ($param) must be of type stdClass|string, Foo given
55+
string(6) "string"
56+
string(1) "1"
57+
NULL
58+
object(stdClass)#1 (0) {
59+
}
60+
zend_string_or_stdclass_or_null(): Argument #1 ($param) must be of type stdClass|string|null, array given
61+
zend_string_or_stdclass_or_null(): Argument #1 ($param) must be of type stdClass|string|null, Foo given

Zend/tests/str_or_obj_zpp.phpt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--TEST--
2+
Test Z_PARAM_STR_OR_OBJ() and Z_PARAM_STR_OR_OBJ_OR_NULL
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
class Foo {}
11+
12+
var_dump(zend_string_or_object("string"));
13+
var_dump(zend_string_or_object(1));
14+
var_dump(zend_string_or_object(null));
15+
var_dump(zend_string_or_object(new stdClass()));
16+
var_dump(zend_string_or_object(new Foo()));
17+
18+
try {
19+
zend_string_or_object([]);
20+
} catch (TypeError $exception) {
21+
echo $exception->getMessage() . "\n";
22+
}
23+
24+
var_dump(zend_string_or_object_or_null("string"));
25+
var_dump(zend_string_or_object_or_null(1));
26+
var_dump(zend_string_or_object_or_null(null));
27+
var_dump(zend_string_or_object_or_null(new stdClass()));
28+
var_dump(zend_string_or_object_or_null(new Foo()));
29+
30+
try {
31+
zend_string_or_object_or_null([]);
32+
} catch (TypeError $exception) {
33+
echo $exception->getMessage() . "\n";
34+
}
35+
36+
?>
37+
--EXPECT--
38+
string(6) "string"
39+
string(1) "1"
40+
string(0) ""
41+
object(stdClass)#1 (0) {
42+
}
43+
object(Foo)#1 (0) {
44+
}
45+
zend_string_or_object(): Argument #1 ($param) must be of type object|string, array given
46+
string(6) "string"
47+
string(1) "1"
48+
NULL
49+
object(stdClass)#2 (0) {
50+
}
51+
object(Foo)#2 (0) {
52+
}
53+
zend_string_or_object_or_null(): Argument #1 ($param) must be of type object|string|null, array given

Zend/zend_API.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,26 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(i
252252
}
253253
/* }}} */
254254

255+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_error(int num, const char *name, zval *arg) /* {{{ */
256+
{
257+
if (EG(exception)) {
258+
return;
259+
}
260+
261+
zend_argument_type_error(num, "must be of type %s|string, %s given", name, zend_zval_type_name(arg));
262+
}
263+
/* }}} */
264+
265+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_or_null_error(int num, const char *name, zval *arg) /* {{{ */
266+
{
267+
if (EG(exception)) {
268+
return;
269+
}
270+
271+
zend_argument_type_error(num, "must be of type %s|string|null, %s given", name, zend_zval_type_name(arg));
272+
}
273+
/* }}} */
274+
255275
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int num, char *error) /* {{{ */
256276
{
257277
if (EG(exception)) {

Zend/zend_API.h

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,8 @@ static zend_always_inline zval *zend_try_array_init(zval *zv)
12191219
_(Z_EXPECTED_STRING_OR_LONG_OR_NULL, "of type string|int|null") \
12201220
_(Z_EXPECTED_CLASS_NAME_OR_OBJECT, "a valid class name or object") \
12211221
_(Z_EXPECTED_CLASS_NAME_OR_OBJECT_OR_NULL, "a valid class name, object, or null") \
1222+
_(Z_EXPECTED_STRING_OR_OBJECT, "of type object|string") \
1223+
_(Z_EXPECTED_STRING_OR_OBJECT_OR_NULL, "of type object|string|null") \
12221224

12231225
#define Z_EXPECTED_TYPE
12241226

@@ -1235,18 +1237,22 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(int min_
12351237
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(int num, zend_expected_type expected_type, zval *arg);
12361238
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(int num, const char *name, zval *arg);
12371239
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(int num, const char *name, zval *arg);
1240+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_error(int num, const char *name, zval *arg);
1241+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_or_null_error(int num, const char *name, zval *arg);
12381242
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int num, char *error);
12391243
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...);
12401244
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_type_error(uint32_t arg_num, const char *format, ...);
12411245
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num, const char *format, ...);
12421246

1243-
#define ZPP_ERROR_OK 0
1244-
#define ZPP_ERROR_FAILURE 1
1245-
#define ZPP_ERROR_WRONG_CALLBACK 2
1246-
#define ZPP_ERROR_WRONG_CLASS 3
1247-
#define ZPP_ERROR_WRONG_CLASS_OR_NULL 4
1248-
#define ZPP_ERROR_WRONG_ARG 5
1249-
#define ZPP_ERROR_WRONG_COUNT 6
1247+
#define ZPP_ERROR_OK 0
1248+
#define ZPP_ERROR_FAILURE 1
1249+
#define ZPP_ERROR_WRONG_CALLBACK 2
1250+
#define ZPP_ERROR_WRONG_CLASS 3
1251+
#define ZPP_ERROR_WRONG_CLASS_OR_NULL 4
1252+
#define ZPP_ERROR_WRONG_ARG 5
1253+
#define ZPP_ERROR_WRONG_COUNT 6
1254+
#define ZPP_ERROR_WRONG_STRING_OR_CLASS 7
1255+
#define ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL 8
12501256

12511257
#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
12521258
const int _flags = (flags); \
@@ -1301,6 +1307,10 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num
13011307
zend_wrong_parameter_class_or_null_error(_i, _error, _arg); \
13021308
} else if (_error_code == ZPP_ERROR_WRONG_ARG) { \
13031309
zend_wrong_parameter_type_error(_i, _expected_type, _arg); \
1310+
} else if (_error_code == ZPP_ERROR_WRONG_STRING_OR_CLASS) { \
1311+
zend_wrong_parameter_string_or_class_error(_i, _error, _arg); \
1312+
} else if (_error_code == ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL) { \
1313+
zend_wrong_parameter_string_or_class_or_null_error(_i, _error, _arg); \
13041314
} \
13051315
} \
13061316
failure; \
@@ -1411,6 +1421,40 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num
14111421
#define Z_PARAM_CLASS_NAME_OR_OBJ_OR_NULL(dest) \
14121422
Z_PARAM_CLASS_NAME_OR_OBJ_EX(dest, 1);
14131423

1424+
#define Z_PARAM_STR_OR_OBJ_EX(destination_string, destination_object, allow_null) \
1425+
Z_PARAM_PROLOGUE(0, 0); \
1426+
if (UNEXPECTED(!zend_parse_arg_str_or_obj(_arg, &destination_string, &destination_object, NULL, allow_null))) { \
1427+
_expected_type = allow_null ? Z_EXPECTED_STRING_OR_OBJECT_OR_NULL : Z_EXPECTED_STRING_OR_OBJECT; \
1428+
_error_code = ZPP_ERROR_WRONG_ARG; \
1429+
break; \
1430+
}
1431+
1432+
#define Z_PARAM_STR_OR_OBJ(destination_string, destination_object) \
1433+
Z_PARAM_STR_OR_OBJ_EX(destination_string, destination_object, 0);
1434+
1435+
#define Z_PARAM_STR_OR_OBJ_OR_NULL(destination_string, destination_object) \
1436+
Z_PARAM_STR_OR_OBJ_EX(destination_string, destination_object, 1);
1437+
1438+
#define Z_PARAM_STR_OR_OBJ_OF_CLASS_EX(destination_string, destination_object, base_ce, allow_null) \
1439+
Z_PARAM_PROLOGUE(0, 0); \
1440+
if (UNEXPECTED(!zend_parse_arg_str_or_obj(_arg, &destination_string, &destination_object, base_ce, allow_null))) { \
1441+
if (base_ce) { \
1442+
_error = ZSTR_VAL((base_ce)->name); \
1443+
_error_code = allow_null ? ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL : ZPP_ERROR_WRONG_STRING_OR_CLASS; \
1444+
break; \
1445+
} else { \
1446+
_expected_type = allow_null ? Z_EXPECTED_STRING_OR_OBJECT_OR_NULL : Z_EXPECTED_STRING_OR_OBJECT; \
1447+
_error_code = ZPP_ERROR_WRONG_ARG; \
1448+
break; \
1449+
} \
1450+
}
1451+
1452+
#define Z_PARAM_STR_OR_OBJ_OF_CLASS(destination_string, destination_object, base_ce) \
1453+
Z_PARAM_STR_OR_OBJ_OF_CLASS_EX(destination_string, destination_object, base_ce, 0);
1454+
1455+
#define Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL(destination_string, destination_object, base_ce) \
1456+
Z_PARAM_STR_OR_OBJ_OF_CLASS_EX(destination_string, destination_object, base_ce, 1);
1457+
14141458
/* old "d" */
14151459
#define Z_PARAM_DOUBLE_EX2(dest, is_null, check_null, deref, separate) \
14161460
Z_PARAM_PROLOGUE(deref, separate); \
@@ -1972,6 +2016,30 @@ static zend_always_inline int zend_parse_arg_class_name_or_obj(
19722016
return 1;
19732017
}
19742018

2019+
static zend_always_inline int zend_parse_arg_str_or_obj(
2020+
zval *arg, zend_string **destination_string, zend_object **destination_object, zend_class_entry *base_ce, int allow_null
2021+
) {
2022+
if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
2023+
if (base_ce && UNEXPECTED(!instanceof_function(Z_OBJCE_P(arg), base_ce))) {
2024+
return 0;
2025+
}
2026+
2027+
*destination_string = NULL;
2028+
*destination_object = Z_OBJ_P(arg);
2029+
} else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) {
2030+
*destination_string = Z_STR_P(arg);
2031+
*destination_object = NULL;
2032+
} else if (allow_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
2033+
*destination_string = NULL;
2034+
*destination_object = NULL;
2035+
} else {
2036+
*destination_object = NULL;
2037+
return zend_parse_arg_str_slow(arg, destination_string);
2038+
}
2039+
2040+
return 1;
2041+
}
2042+
19752043
END_EXTERN_C()
19762044

19772045
#endif /* ZEND_API_H */

ext/zend_test/test.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,82 @@ ZEND_FUNCTION(zend_leak_variable)
132132
}
133133
/* }}} */
134134

135+
/* Tests Z_PARAM_STR_OR_OBJ */
136+
ZEND_FUNCTION(zend_string_or_object)
137+
{
138+
zend_string *str;
139+
zend_object *object;
140+
141+
ZEND_PARSE_PARAMETERS_START(1, 1)
142+
Z_PARAM_STR_OR_OBJ(str, object)
143+
ZEND_PARSE_PARAMETERS_END();
144+
145+
if (str) {
146+
RETURN_STR_COPY(str);
147+
} else {
148+
RETURN_OBJ_COPY(object);
149+
}
150+
}
151+
/* }}} */
152+
153+
/* Tests Z_PARAM_STR_OR_OBJ_OR_NULL */
154+
ZEND_FUNCTION(zend_string_or_object_or_null)
155+
{
156+
zend_string *str;
157+
zend_object *object;
158+
159+
ZEND_PARSE_PARAMETERS_START(1, 1)
160+
Z_PARAM_STR_OR_OBJ_OR_NULL(str, object)
161+
ZEND_PARSE_PARAMETERS_END();
162+
163+
if (str) {
164+
RETURN_STR_COPY(str);
165+
} else if (object) {
166+
RETURN_OBJ_COPY(object);
167+
} else {
168+
RETURN_NULL();
169+
}
170+
}
171+
/* }}} */
172+
173+
/* Tests Z_PARAM_STR_OR_OBJ_OF_CLASS */
174+
ZEND_FUNCTION(zend_string_or_stdclass)
175+
{
176+
zend_string *str;
177+
zend_object *object;
178+
179+
ZEND_PARSE_PARAMETERS_START(1, 1)
180+
Z_PARAM_STR_OR_OBJ_OF_CLASS(str, object, zend_standard_class_def)
181+
ZEND_PARSE_PARAMETERS_END();
182+
183+
if (str) {
184+
RETURN_STR_COPY(str);
185+
} else {
186+
RETURN_OBJ_COPY(object);
187+
}
188+
}
189+
/* }}} */
190+
191+
/* Tests Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL */
192+
ZEND_FUNCTION(zend_string_or_stdclass_or_null)
193+
{
194+
zend_string *str;
195+
zend_object *object;
196+
197+
ZEND_PARSE_PARAMETERS_START(1, 1)
198+
Z_PARAM_STR_OR_OBJ_OF_CLASS_OR_NULL(str, object, zend_standard_class_def)
199+
ZEND_PARSE_PARAMETERS_END();
200+
201+
if (str) {
202+
RETURN_STR_COPY(str);
203+
} else if (object) {
204+
RETURN_OBJ_COPY(object);
205+
} else {
206+
RETURN_NULL();
207+
}
208+
}
209+
/* }}} */
210+
135211
static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ {
136212
zend_object *obj = zend_objects_new(class_type);
137213
object_properties_init(obj, class_type);

ext/zend_test/test.stub.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,13 @@ function zend_terminate_string(string &$str): void {}
2929
function zend_leak_variable(mixed $variable): void {}
3030

3131
function zend_leak_bytes(int $bytes = 3): void {}
32+
33+
function zend_string_or_object(object|string $param): object|string {}
34+
35+
function zend_string_or_object_or_null(object|string|null $param): object|string|null {}
36+
37+
/** @param stdClass|string $param */
38+
function zend_string_or_stdclass($param): stdClass|string {}
39+
40+
/** @param stdClass|string|null $param */
41+
function zend_string_or_stdclass_or_null($param): stdClass|string|null {}

0 commit comments

Comments
 (0)