zoukankan      html  css  js  c++  java
  • 深入PHP内核之参数

    1、看一下一个扩展中的简单代码

    ZEND_BEGIN_ARG_INFO(params_add_arginfo, 0)
        ZEND_ARG_INFO(0, a)
        ZEND_ARG_INFO(0, b)
    ZEND_END_ARG_INFO()
    
    PHP_FUNCTION(params_add) {
        long a,b;
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &a, &b) == FALSE) {
            return;
        }
        RETURN_LONG(a+b);
    }
    

     2、参数相关宏的定义 (Zend/zend_API.h)

    #define ZEND_ARG_INFO(pass_by_ref, name)
    { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 },
    //声明普通参数,可以用来表示PHP中的int, float, double, string等基本数组类型
    
    #define ZEND_ARG_PASS_INFO(pass_by_ref)
    { NULL, 0, NULL, 0, 0, pass_by_ref, 0, 0 },
    //pass_by_ref为1时,强制设置后续的参数为引用类型 
    
    #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null)
    { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, IS_OBJECT, pass_by_ref, allow_null, 0 },
    //声明对象类型的参数
    
    #define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null)
     { #name, sizeof(#name)-1, NULL, 0, IS_ARRAY, pass_by_ref, allow_null, 0 },
    //声明数组类型的参数
    
    #define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
     { #name, sizeof(#name)-1, NULL, 0, type_hint, pass_by_ref, allow_null, 0 },
    
    #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name)
    { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 1 },
    
    #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)  
        static const zend_arg_info name[] = {                                                                       
            { NULL, 0, NULL, required_num_args, 0, return_reference, 0, 0 },
    #define ZEND_BEGIN_ARG_INFO(name, _unused)  
        ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
    #define ZEND_END_ARG_INFO()     };
    

     3、展开ZEND_BEGIN_ARG_INFO语句

    static const zend_arg_info params_add_arginfo[] = {
        {NULL, 0, NULL, -1, 0, ZEND_RETURN_VALUE, 0},
        {a, sizeof(a)-1, NULL, 0, 0, 0, 0},
        {b, sizeof(b)-1, NULL, 0, 0, 0, 0},
    };
    

     4、参数在zend中的定义(Zend/zend_compile.h)

    typedef struct _zend_arg_info {
        const char *name; //参数的名称
        zend_uint name_len; //参数名称的长度
        char *class_name; //当参数类型为类时,指定类的名称
        zend_uint class_name_len; //类名称的长度
        zend_uchar type_hint; 
        zend_bool allow_null; //是否允许设置为null
        zend_bool pass_by_reference; //是否设置为引用,即使用&操作符
    }
    
    
    #define PHP_FUNCTION            ZEND_FUNCTION //函数的实现
    #define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
    #define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
    #define INTERNAL_FUNCTION_PARAMETERS 
    int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
    

     5、zend_parse_parameters的参数

    Boolean         b zend_bool      //布尔值
    Long            l long           //长整数
    Double          d double         //双精度浮点数
    String          s char *, int    //符串 (也可能是空字节)和其长度
    Resource        r zval*          // 资源, 保存在 zval*
    Array           a zval*          //数组, 保存在 zval*
    Object          o zval*          //任何类的)对象, 保存在 zval*
    zval            z zval*          //实际的 zval*
    zval            O zval*         //由class entry 指定的类的)对象, 保存在 zval*
    HashTable       h HashTable*     //数组的哈希表
    Function       f char *, int     //函数,方法名 (版本 > php5.1)
    
    
    |  -表明剩下的参数都是可选参数。如果用户没有传进来这些参数值,那么这些值就会被初始化成默认值。
    /  -表明参数解析函数将会对剩下的参数以 SEPARATE_ZVAL_IF_NOT_REF() 的方式来提供这个参数的一份拷贝,除非这些参数是一个引用。
    !  -表明剩下的参数允许被设定为 NULL(仅用在 a、o、O、r和z身上)。如果用户传进来了一个 NULL 值,则存储该参数的变量将会设置为 NULL。 
    

    看看官方文档中提供的几个例子:

    /* 取得一个长整数,一个字符串和它的长度,再取得一个 zval 值。 */  
    long l;  
    char *s;  
    int s_len;  
    zval *param;  
    
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsz", &l, &s, &s_len, &param) == FAILURE) {  
    return;  
    }  
    
    /* 取得一个由 my_ce 所指定的类的一个对象,另外再取得一个可选的双精度的浮点数。 */  
    zval *obj;  
    double d = 0.5;  
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|d", &obj, my_ce, &d) == FAILURE) {  
    return;  
    }  
    
    /* 取得一个对象或空值,再取得一个数组。如果传递进来一个空对象,则 obj 将被设置为 NULL。*/  
    zval *obj;  
    zval *arr;  
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) {  
    return;  
    }  
    
    /* 取得一个分离过的数组。*/  
    zval *arr;  
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) {  
    return;  
    }  
    
    /* 仅取得前 3 个参数(这对可变参数的函数很有用)。*/  
    zval *z;  
    zend_bool b;  
    zval *r;  
    if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) {  
    return;  
    }  
    

     在接收参数时还有一个可用的函数zend_parse_parameters_ex,允许我们传入一些flags来控制解析参数的动作,使用方式如下所示:

    int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...); 
    

     目前flags仅能传入ZEND_PARSE_PARAMS_QUIET这个值,表示函数不输出任何错误信息,如下面的示例:

    long l1, l2, l3;  
    char *s;  
    
    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,  
                            ZEND_NUM_ARGS() TSRMLS_CC,  
                            "lll", &l1, &l2, &l3) == SUCCESS) {  
    /* manipulate longs */  
    } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,  
                                   ZEND_NUM_ARGS(), "s", &s, &s_len) == SUCCESS) {  
    /* manipulate string */  
    } else {  
    php_error(E_WARNING, "%s() takes either three long values or a string as argument",  
             get_active_function_name(TSRMLS_C));  
    return;  
    }  
    

     可变参数

    由于PHP支持可变参数,所以在接收可变参数时,使用前面介绍的两个方法就不太合适,我们可以用zend_get_parameters_array_ex()来代替,如下面的示例:

    zval **parameter_array[4];  
    
    /* 取得参数个数 */  
    argument_count = ZEND_NUM_ARGS();  
    
    /* 看一下参数个数是否满足我们的要求:最少 2 个,最多 4个。 */  
    if(argument_count < 2 || argument_count > 4)  
    {  
    WRONG_PARAM_COUNT;  
    }  
    
    /* 参数个数正确,开始接收。 */  
    if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS)  
    {  
    WRONG_PARAM_COUNT;  
    }  
    

     6、获取参数数量

    PHP无法根据函数的显式声明来对调用进行语法检查,而且它还支持可变参数,所以我们就不得不在所调用函数的内部来获取参数个数。我们可以使用宏ZEND_NUM_ARGS来获取参数个数,如下面的代码:

    if(ZEND_NUM_ARGS() != 2)
    {
    	WRONG_PARAM_COUNT
    }
    

     这段代码使用宏WRONG_PARAM_COUNT抛出一个参数个数错误

    7、更多的参数类型

    #define Z_TYPE_P //获取参数类型 
    //IS_NULL|IS_BOOL|IS_LONG|IS_DOUBLE|IS_STRING|IS_ARRAY|IS_RESOURCE|IS_OBJECT
    #define Z_BVAL_P //获取bool类型参数的值
    #define Z_LVAL_P //获取long类型参数的值
    #define Z_DVAL_P //获取float类型参数的值
    #define Z_STRVAL_P //获取string类型参数的值
    #define Z_STRLEN_P //获取string类型参数的长度
    #define Z_ARRVAL_P //获取array类型参数的值
    #define Z_RESVAL_P //获取resource类型参数的值
    #define Z_OBJCE_P //获取object类型参数的值
    
  • 相关阅读:
    【整理】Dword、LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR
    C/C++中printf和C++中cout的输出格式
    左值的理解(给渴望学习的新手)
    c++ 指针精髓
    c++中的函数前面加个LRESULT是什么意思啊?
    pb调用vc写的动态链接库文件
    C++问题 & *用法
    vs2008下MFC内存泄露问题一点经验
    mysql5.6.41winx64安装
    开发是一件需要非常小心的工作
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/4833917.html
Copyright © 2011-2022 走看看