zoukankan      html  css  js  c++  java
  • php zend api【第一次】

    我们来看一下以下代码片段:

     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).

     

    上述代码片段定义了一个函数名为: lychee_cli_usr_get_balance()的函数, 它需要4个参数: 第一个参数的类型是字符串型, 第二个参数是长整型, 第三个参数是长整型, 第四个参数是字符串型.函数 zend_parse_parameters() 的作用是获取在PHP文件调用函数 lychee_cli_usr_get_balance()时传入的参数. 但是它是怎么获得这四个参数的呢? 请看下面:
    在zend_API.c的1132行中可以找到如下定义:
     

     

    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  }  
    必须同时满足(type_spec)[0] == 0 && __num_args != 0 && !(quiet), 然后才返回FAILURE, 就是说获取参数失败, 而失败的信息会经过zend_error()函数打印出来. 后面两个条件很容易满足, 最重要的是第一个条件. 第一个条件才符合该宏的名字RETURN_IF_ZERO_ARGS. 这里还有两个函数: get_active_class_name()以及get_active_function_name(). 这两个函数会捕捉到PHP页面调用的当前的类名和函数名.
    1139 va_start(va, type_spec);         // 初始化变量va, 然后给下面的函数调用
    1140 retval = zend_parse_va_args(num_args, type_spec, &va, 0 TSRMLS_CC);   // 这行代码根据参数的个数, 以及 类型指定符来匹配参数是否正确. 如果正确, 便为参数分配存储空间;如果错误, 会打印出错误信息. zend_parse_va_args()函数在zend_API.c中定义如下:
    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 }  
    我们先看923行的for循环: 它扫描type_spec, 遇到正确的类型指定符或类型修饰符"|", 统计出实际传入的参数个数, 以及最小的需要传入的参数个数; 如果遇到不正确的类型指定符, 便打印出类型不匹配的错误信息 
    982行代码: 如果没有遇到"|", 那么便设置最小需要传入的参数个数为扫描到的实际参数个数.
    992--1005行代码: 如果扫描到的实际参数个数和传入的参数个数不相等, 那么便打印出参数个数不对的错误信息.
    1051--1100行代码: 如果代码执行到这里, 那么表示, 传入的参数正确, 并且参数的类型也正确, 那么就要为这些参数分配存储空间了. 而1068--1081行代码正确做了这些事情.
     
     
    该函数的第二个参数: TSRMLS_DC, 是一个宏, 在线程安全的时候用到的.
     
     
    该函数的第三个参数: type_spec 是一个字符串类型, 表示类型指定符. 其具体信息如下:
    Type Specifier(类型指定符)      Userspace Datatype(用户空间数据类型)
    b                              Boolean
    l                              Integer
    d                              Floating point
    s                              String
    r                              Resource
    a                              Array
    o                              Object instance
    O                              Object instance of a specified type
    z                              Non-specific zval
    Z                              Dereferenced non-specific zval
    特殊的Type Sepcifier还包括: "|", "!", "/"这三个东东. 这三个东东是干嘛使的呢? 请看下面:
    Type Modifier    Meaning
    |                Optional parameters follow. When this is specified, all previous parameters are considered required and all subsequent parameters are considered optional.
    例如: "ss|s", 表示前面两个"ss"是必须要传入的, 而后面的那个"s"是可选的.
    !                If a NULL is passed for the parameter corresponding to the preceding argument specifier, the internal variable provided will be set to an actual NULL pointer as opposed to an IS_NULL zval.
    例如: "z!", 表示该修饰符之前的参数可以被设置为空. 如果传入的参数为空, 则把该参数设置成一个空的指针, 而不是设置成一个空的zval值. 网上有文章说此修饰符的类型必须为zval*类型的变量. 也许是以前的版本, 但现在新的版本可以不为zval*. 
    /                If the parameter corresponding to the preceding argument specifier is in a copy-on-write reference set, it will be automatically separated into a new zval with is_ref==0, and refcount==1.
     
     
    该函数的最后一些参数: (...), 它表示该函数接受变参. 这些参数就是你实际调用lychee_cli_usr_get_balance()时的形参. 必须在前面加上"&", 否则会报错. 而在传递字符串参数时, 该参数后面必须还得加上一个整形参数, 表示该字符串的长度, 否则也会报错.
  • 相关阅读:
    二叉树的构造与遍历
    最长公共子序列
    Python爬虫与数据图表的实现
    降维实例之主成分分析
    数据集之转换器以及估计器
    机器学习算法分类以及开发流程
    数据的降维之特征选择及主成分分析
    特征工程之归一化及标准化
    文本tfidf
    文本特征抽取
  • 原文地址:https://www.cnblogs.com/tohilary/p/2721971.html
Copyright © 2011-2022 走看看