PHP 使用 ZEND_PARSE_PARAMETERS_START ... ZEND_PARSE_PARAMETERS_END 进行参数解析
PHP_FUNCTION(strpos)
{
zval *needle;
zend_string *haystack;
const char *found = NULL;
char needle_char[2];
zend_long offset = 0;
// 解析参数
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(haystack)
Z_PARAM_ZVAL(needle)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(offset)
ZEND_PARSE_PARAMETERS_END();
...
...
...
}
ZEND_PARSE_PARAMETERS_START ZEND_PARSE_PARAMETERS_END 为宏定义(以下代码位于Zend/zend_API.h)
#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
const int _flags = (flags); \
int _min_num_args = (min_num_args); \
int _max_num_args = (max_num_args); \
int _num_args = EX_NUM_ARGS(); \
int _i = 0; \
zval *_real_arg, *_arg = NULL; \
zend_expected_type _expected_type = Z_EXPECTED_LONG; \
char *_error = NULL; \
zend_bool _dummy; \
zend_bool _optional = 0; \
int _error_code = ZPP_ERROR_OK; \
((void)_i); \
((void)_real_arg); \
((void)_arg); \
((void)_expected_type); \
((void)_error); \
((void)_dummy); \
((void)_optional); \
\
do { \
if (UNEXPECTED(_num_args < _min_num_args) || \
(UNEXPECTED(_num_args > _max_num_args) && \
EXPECTED(_max_num_args >= 0))) { \
if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
if (_flags & ZEND_PARSE_PARAMS_THROW) { \
zend_wrong_parameters_count_exception(_min_num_args, _max_num_args); \
} else { \
zend_wrong_parameters_count_error(_min_num_args, _max_num_args); \
} \
} \
_error_code = ZPP_ERROR_FAILURE; \
break; \
} \
_real_arg = ZEND_CALL_ARG(execute_data, 0);
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args) \
ZEND_PARSE_PARAMETERS_START_EX(0, min_num_args, max_num_args)
#define ZEND_PARSE_PARAMETERS_END_EX(failure) \
} while (0); \
if (UNEXPECTED(_error_code != ZPP_ERROR_OK)) { \
if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
if (_error_code == ZPP_ERROR_WRONG_CALLBACK) { \
if (_flags & ZEND_PARSE_PARAMS_THROW) { \
zend_wrong_callback_exception(_i, _error); \
} else { \
zend_wrong_callback_error(_i, _error); \
} \
} else if (_error_code == ZPP_ERROR_WRONG_CLASS) { \
if (_flags & ZEND_PARSE_PARAMS_THROW) { \
zend_wrong_parameter_class_exception(_i, _error, _arg); \
} else { \
zend_wrong_parameter_class_error(_i, _error, _arg); \
} \
} else if (_error_code == ZPP_ERROR_WRONG_ARG) { \
if (_flags & ZEND_PARSE_PARAMS_THROW) { \
zend_wrong_parameter_type_exception(_i, _expected_type, _arg); \
} else { \
zend_wrong_parameter_type_error(_i, _expected_type, _arg); \
} \
} \
} \
failure; \
} \
} while (0)
#define ZEND_PARSE_PARAMETERS_END() \
ZEND_PARSE_PARAMETERS_END_EX(return)
替换后参数解析代码如下(以strpos中参数解析为例)
do {
const int _flag = (0); // 0 为ZEND_PARSE_PARAMETERS_START_EX(0, min_num_args, max_num_args) 中第一个参数
int _min_num_args = (2); // 2 为 ZEND_PARSE_PARAMETERS_START(2, 3)中2
int _max_num_args = (3); // 3 为 ZEND_PARSE_PARAMETERS_START(2, 3)中3
int _num_args =(execute_data)->This.u2.num_args; // 参见 struct _zval_struct 参数数量
int _i = 0;
zval *_real_arg, *_arg = NULL;
zend_expected_type _expected_type = Z_EXPECTED_LONG;
char *_error = NULL;
zend_bool _dummy;
zend_bool _optional = 0;
int _error_code = ZPP_ERROR_OK;
((void)_i);
((void)_real_arg);
((void)_arg);
((void)_expected_type);
((void)_error);
((void)_dummy);
((void)_optional);
// 校验参数数量
do {
if (UNEXPECTED(_num_args < _min_num_args) ||
(UNEXPECTED(_num_args > _max_num_args) &&
EXPECTED(_max_num_args >= 0))) {
if (!(_0 & ZEND_PARSE_PARAMS_QUIET)) {
if (_0 & ZEND_PARSE_PARAMS_THROW) {
zend_wrong_parameters_count_exception(_min_num_args, _max_num_args);
} else {
zend_wrong_parameters_count_error(_min_num_args, _max_num_args);
}
}
_error_code = ZPP_ERROR_FAILURE;
break;
}
_real_arg = ZEND_CALL_ARG(execute_data, 0);
Z_PARAM_STR(haystack) // 解析参数
Z_PARAM_ZVAL(needle) // 解析参数
_optional = 1;
Z_PARAM_LONG(offset) // 解析参数
} while (0);
if (UNEXPECTED(_error_code != ZPP_ERROR_OK)) {
if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) {
if (_error_code == ZPP_ERROR_WRONG_CALLBACK) {
if (_flags & ZEND_PARSE_PARAMS_THROW) {
zend_wrong_callback_exception(_i, _error);
} else {
zend_wrong_callback_error(_i, _error);
}
} else if (_error_code == ZPP_ERROR_WRONG_CLASS) {
if (_flags & ZEND_PARSE_PARAMS_THROW) {
zend_wrong_parameter_class_exception(_i, _error, _arg);
} else {
zend_wrong_parameter_class_error(_i, _error, _arg);
}
} else if (_error_code == ZPP_ERROR_WRONG_ARG) {
if (_flags & ZEND_PARSE_PARAMS_THROW) {
zend_wrong_parameter_type_exception(_i, _expected_type, _arg);
} else {
zend_wrong_parameter_type_error(_i, _expected_type, _arg);
}
}
}
return;
}
} while (0)
Z_PARAM_STR(haystack) Z_PARAM_ZVAL(needle) Z_PARAM_LONG(offset) 将用户传入参数值解析至指定参数
#define Z_PARAM_STR_EX2(dest, check_null, deref, separate) \
Z_PARAM_PROLOGUE(deref, separate); \
if (UNEXPECTED(!zend_parse_arg_str(_arg, &dest, check_null))) { \
_expected_type = Z_EXPECTED_STRING; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
#define Z_PARAM_STR_EX(dest, check_null, separate) \
Z_PARAM_STR_EX2(dest, check_null, separate, separate)
#define Z_PARAM_STR(dest) \
Z_PARAM_STR_EX(dest, 0, 0)
#define Z_PARAM_ZVAL_EX2(dest, check_null, deref, separate) \
Z_PARAM_PROLOGUE(deref, separate); \
zend_parse_arg_zval_deref(_arg, &dest, check_null);
#define Z_PARAM_ZVAL_EX(dest, check_null, separate) \
Z_PARAM_ZVAL_EX2(dest, check_null, separate, separate)
#define Z_PARAM_ZVAL(dest) \
Z_PARAM_ZVAL_EX(dest, 0, 0)
#define Z_PARAM_LONG_EX2(dest, is_null, check_null, deref, separate) \
Z_PARAM_PROLOGUE(deref, separate); \
if (UNEXPECTED(!zend_parse_arg_long(_arg, &dest, &is_null, check_null, 0))) { \
_expected_type = Z_EXPECTED_LONG; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
#define Z_PARAM_LONG_EX(dest, is_null, check_null, separate) \
Z_PARAM_LONG_EX2(dest, is_null, check_null, separate, separate)
#define Z_PARAM_LONG(dest) \
Z_PARAM_LONG_EX(dest, _dummy, 0, 0)
先调用 Z_PARAM_PROLOGUE(deref, separate) 进行参数校验。_i表示第几个参数;_arg为第_i个参数。
#define Z_PARAM_PROLOGUE(deref, separate) \
++_i; \
ZEND_ASSERT(_i <= _min_num_args || _optional==1); \
ZEND_ASSERT(_i > _min_num_args || _optional==0); \
if (_optional) { \
if (UNEXPECTED(_i >_num_args)) break; \
} \
_real_arg++; \
_arg = _real_arg; \
if (deref) { \
if (EXPECTED(Z_ISREF_P(_arg))) { \
_arg = Z_REFVAL_P(_arg); \
} \
} \
if (separate) { \
SEPARATE_ZVAL_NOREF(_arg); \
}
然后分别调用 zend_parse_arg_str zend_parse_arg_zval_deref zend_parse_arg_long 将
_arg赋值dest完成参数解析