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完成参数解析