Skip to content

Commit

Permalink
Add string or object ZPP macros
Browse files Browse the repository at this point in the history
Closes GH-5788
  • Loading branch information
kocsismate committed Jul 6, 2020
1 parent a4b253c commit b18b2c8
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 8 deletions.
61 changes: 61 additions & 0 deletions Zend/tests/str_or_obj_of_class_zpp.phpt
Original file line number Diff line number Diff line change
@@ -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
53 changes: 53 additions & 0 deletions Zend/tests/str_or_obj_zpp.phpt
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
82 changes: 75 additions & 7 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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); \
Expand Down Expand Up @@ -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; \
Expand Down Expand Up @@ -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); \
Expand Down Expand Up @@ -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 */
76 changes: 76 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}

0 comments on commit b18b2c8

Please sign in to comment.