我们来看一下以下代码片段:
1 PHP_FUNCTION(lychee_cli_usr_get_balance) 2 { 3 char* srv_addr = NULL; 4 long srv_addr_len; 5 long srv_port; 6 long time_out; 7 char* logined_cookie = NULL; 8 long logined_cookie_len; 9 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"slls", 10 &srv_addr, &srv_addr_len, 11 &srv_port, 12 &time_out, 13 &logined_cookie, &logined_cookie_len) == FAILURE) 14 RETURN_NULL(); 15 ... 16 ... 17 }
PHP_FUNCTION是一个宏, 这是PHP扩展C++函数的一种写法(另一种写法是PHP_METHOD(funcname), 在定义类的成员函数的时候用到). 它在php.h中定义如下:
#define PHP_FUNCTION ZEND_FUNCTION
而ZEND_FUNCTION又是一个宏, 它在zend_API.h中定义如下:
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
这个宏里面又有两个宏: ZEND_NAMED_FUNCTION和ZEND_FN.ZEND_NAMED_FUNCTION在zend_API.h中定义如下:
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
ZEND_FE在zend_API.h中定义如下
#define ZEND_FN(name) zif_##name
经过上述四个宏展开以后, PHP_FUNCTION(lychee_cli_usr_get_balance)变成以下形式:
void zif_lychee_cli_get_balance(INTERNAL_FUNCTION_PARAMETERS)
当然这还不是最终的形式, 因为INTERNAL_FUNCTION_PARAMETERS还是一个宏, 它在zend.h中定义如下:
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
所以PHP_FUNCTION(lychee_cli_usr_get_balance)最终的被展开的形式是:
void zif_lychee_cli_get_balance(int ht, zval* return_value, zval** return_value_ptr, zval* this_ptr, int return_value_used TSRMLS_DC)
这里涉及到一个zend engine的一个重要的数据类型: zval, 这里先不解释它是什么了. 以后慢慢再讲.
我们来看看上面那几个参数什么意思:
Parameter | Description |
ht | The number of arguments passed to the Zend function. You should not touch this directly, but instead use ZEND_NUM_ARGS() to obtain the value. |
return_value | This variable is used to pass any return values of your function back to PHP. Access to this variable is best done using the predefined macros. For a description of these see below. |
this_ptr | Using this variable, you can gain access to the object in which your function is contained, if it's used within an object. Use the functiongetThis() to obtain this pointer. |
return_value_used |
This flag indicates whether an eventual return value from this function will actually be used by the calling script. 0 indicates that the return value is not used; 1 indicates that the caller expects a return value. Evaluation of this flag can be done to verify correct usage of the function as well as speed optimizations in case returning a value requires expensive operations (for an example, see how array.cmakes use of this). |
1132 ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...) 1133 { 1134 va_list va; 1135 int retval; 1136 1137 RETURN_IF_ZERO_ARGS(num_args, type_spec, 0); 1138 1139 va_start(va, type_spec); 1140 retval = zend_parse_va_args(num_args, type_spec, &va, 0 TSRMLS_CC); 1141 va_end(va); 1142 1143 return retval; 1144 }
该函数的第一个参数: num_args, 是一个int类型, 表示参数的个数. 在实际调用的时候,你看到它是一个宏: ZEND_NUM_ARGS(), 这个宏在zend_API.h中定义如下:
353 #define ZEND_NUM_ARGS() (ht)
事实上你也可以传入一个数值, 但这个数必须和你实际传入的参数个数一致, 不然, 在你调用lychee_cli_usr_get_balance()的时候会报错, 而且报错信息还不是正确的信息. 以上面的为例: 你传入的第一个参数必须为4, 如果不是为4, 而是为5, 当你在调用lychee_cli_usr_get_balance()时会报这样的错: "lychee_cli_usr_get_balance()需要4个参数, 但是传入了5个", 而无论实际上你传入了多少个参数, 它都会报个这错误. 所以这里推荐使用ZEND_NUM_ARGS() 这个宏, 在不正确调用的时候, 会报正确的错误. 为什么呢? 这时候RETURN_IF_ZERO_ARGS()函数出场啦. 见名思义, 当参数个数为0的时候返回. RETURN_IF_ZERO_ARGS是一个宏, 它在zend_API.c中定义如下:
1104 #define RETURN_IF_ZERO_ARGS(num_args, type_spec, quiet) { / 1105 int __num_args = (num_args); / 1106 / 1107 if (0 == (type_spec)[0] && 0 != __num_args && !(quiet)) { / 1108 char *__space; / 1109 zstr __class_name = get_active_class_name(&__space TSRMLS_CC); / 1110 zend_error(E_WARNING, "%v%s%v() expects exactly 0 parameters, %d given", / 1111 __class_name, __space, / 1112 get_active_function_name(TSRMLS_C), __num_args); / 1113 return FAILURE; / 1114 }/ 1115 }
907 static int zend_parse_va_args(int num_args, char *type_spec, va_list *va, int flags TSRMLS_DC) 908 { 909 char *spec_walk; 910 int c, i; 911 int min_num_args = -1; 912 int max_num_args = 0; 913 int post_varargs = 0; 914 zval **arg; 915 int arg_count; 916 int quiet = flags & ZEND_PARSE_PARAMS_QUIET; 917 zend_bool have_varargs = 0; 918 zend_bool T_present = 0; 919 signed char T_arg_type = -1; 920 zval ****varargs = NULL; 921 int *n_varargs = NULL; 922 923 for (spec_walk = type_spec; *spec_walk; spec_walk++) { 924 c = *spec_walk; 925 switch (c) { 926 case 'T': 927 T_present++; 928 /* break omitted intentionally */ 929 case 'l': case 'd': 930 case 's': case 'b': 931 case 'r': case 'a': 932 case 'o': case 'O': 933 case 'z': case 'Z': 934 case 't': case 'u': 935 case 'C': case 'h': 936 case 'U': case 'S': 937 case 'f': case 'x': 938 case 'A': case 'H': 939 max_num_args++; 940 break; 941 942 case '|': 943 min_num_args = max_num_args; 944 break; 945 946 case '/': case '!': 947 case '&': case '^': 948 /* Pass */ 949 break; 950 951 case '*': 952 case '+': 953 if (have_varargs) { 954 if (!quiet) { 955 char *space; 956 zstr class_name = get_active_class_name(&space TSRMLS_CC); 957 zend_error(E_WARNING, "%v%s%v(): only one varargs specifier (* or +) is permitted", 958 class_name, space, get_active_function_name(TSRMLS_C)); 959 } 960 return FAILURE; 961 } 962 have_varargs = 1; 963 /* we expect at least one parameter in varargs */ 964 if (c == '+') { 965 max_num_args++; 966 } 967 /* mark the beginning of varargs */ 968 post_varargs = max_num_args; 969 break; 970 971 default: 972 if (!quiet) { 973 char *space; 974 zstr class_name = get_active_class_name(&space TSRMLS_CC); 975 zend_error(E_WARNING, "%v%s%v(): bad type specifier while parsing parameters", 976 class_name, space, get_active_function_name(TSRMLS_C)); 977 } 978 return FAILURE; 979 } 980 } 981 982 if (min_num_args < 0) { 983 min_num_args = max_num_args; 984 } 985 986 if (have_varargs) { 987 /* calculate how many required args are at the end of the specifier list */ 988 post_varargs = max_num_args - post_varargs; 989 max_num_args = -1; 990 } 991 992 if (num_args < min_num_args || (num_args > max_num_args && max_num_args > 0)) { 993 if (!quiet) { 994 char *space; 995 zstr class_name = get_active_class_name(&space TSRMLS_CC); 996 zend_error(E_WARNING, "%v%s%v() expects %s %d parameter%s, %d given", 997 class_name, space, 998 get_active_function_name(TSRMLS_C), 999 min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most", 1000 num_args < min_num_args ? min_num_args : max_num_args, 1001 (num_args < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s", 1002 num_args); 1003 } 1004 return FAILURE; 1005 } 1006 1007 arg_count = (int)(zend_uintptr_t) *(zend_vm_stack_top(TSRMLS_C) - 1); 1008 1009 if (num_args > arg_count) { 1010 zend_error(E_WARNING, "%v(): could not obtain parameters for parsing", 1011 get_active_function_name(TSRMLS_C)); 1012 return FAILURE; 1013 } 1014 1015 if (T_present > 1) { 1016 /* determine 'T' target argument type */ 1017 for (spec_walk = type_spec, i = 0; *spec_walk && i < num_args; spec_walk++) { 1018 switch (*spec_walk) { 1019 case 'T': 1020 arg = (zval**)zend_vm_stack_top(TSRMLS_C) - 1 - (arg_count-i); 1021 if (Z_TYPE_PP(arg) == IS_UNICODE && (T_arg_type == -1 || T_arg_type == IS_STRING)) { 1022 /* we can upgrade from strings to Unicode */ 1023 T_arg_type = IS_UNICODE; 1024 } else if (Z_TYPE_PP(arg) == IS_STRING && T_arg_type == -1) { 1025 T_arg_type = IS_STRING; 1026 } 1027 i++; 1028 break; 1029 1030 case '|': case '!': 1031 case '/': case '&': 1032 /* pass */ 1033 break; 1034 1035 case '*': 1036 case '+': 1037 i = arg_count - post_varargs; 1038 break; 1039 1040 default: 1041 i++; 1042 break; 1043 } 1044 } 1045 1046 if (T_arg_type == -1) { 1047 T_arg_type = IS_UNICODE; 1048 } 1049 } 1050 1051 i = 0; 1052 while (num_args-- > 0) { 1053 if (*type_spec == '|') { 1054 type_spec++; 1055 } 1056 1057 if (*type_spec == '*' || *type_spec == '+') { 1058 int num_varargs = num_args + 1 - post_varargs; 1059 1060 /* eat up the passed in storage even if it won't be filled in with varargs */ 1061 varargs = va_arg(*va, zval ****); 1062 n_varargs = va_arg(*va, int *); 1063 type_spec++; 1064 1065 if (num_varargs > 0) { 1066 int iv = 0; 1067 zval **p = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (arg_count - i)); 1068 1069 *n_varargs = num_varargs; 1070 1071 /* allocate space for array and store args */ 1072 *varargs = safe_emalloc(num_varargs, sizeof(zval **), 0); 1073 while (num_varargs-- > 0) { 1074 (*varargs)[iv++] = p++; 1075 } 1076 1077 /* adjust how many args we have left and restart loop */ 1078 num_args = num_args + 1 - iv; 1079 i += iv; 1080 continue; 1081 } else { 1082 *varargs = NULL; 1083 *n_varargs = 0; 1084 } 1085 } 1086 1087 arg = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (arg_count-i)); 1088 1089 if (zend_parse_arg(i+1, arg, va, &type_spec, quiet, T_arg_type TSRMLS_CC) == FAILURE) { 1090 /* clean up varargs array if it was used */ 1091 if (varargs && *varargs) { 1092 efree(*varargs); 1093 *varargs = NULL; 1094 } 1095 return FAILURE; 1096 } 1097 i++; 1098 } 1099 1100 return SUCCESS; 1101 }