• c扩展调用php的函数(调用实现php函数的c函数)


    上一次是写的c扩展调用c的标准函数,但是只能调用头文件中申明的函数,今天来说下c扩展调用实现php函数的c函数,比方说,c扩展要用到php中ip2long这个函数,但是c不可能去php中调用,肯定是去调用实现php函数的c函数。那么c扩展如何调用c内核对php的API呢?

    这里要用到一个函数:ZEND_API int call_user_function_ex(HashTable *function_table, zval **object_pp, zval *function_name, zval **retval_ptr_ptr, zend_uint param_count, zval **params[], int no_separation, HashTable *symbol_table TSRMLS_DC);

    第一个参数是HashTable,Zend使用HashTable来存储PHP函数,function_table用于指定从哪个HashTable中获取函数。通常应该用CG(function_table),展开就是compiler_globals.function_table,compiler_globals是一个用来存储编译器数据的全局数据结构(与其对应的还有个EG宏,即executor_globals,它用来存储执行器数据)。compiler_globals.function_table里面存储了所有我们可以在PHP页面里面调用的函数,包括Zend内建函数、PHP标准库函数、模块导出的函数以及用户使用PHP代码定义的函数。

     object_pp是一个对象,当指定该值时,Zend会从对象的函数表中获取函数,这里不予讨论,总是设为NULL。

    function_name必须是string型的zval,存储我们希望调用的函数的名称。为什么使用zval而不是直接用char*,是因为Zend考虑到大部分情况下,我们都是从用户那获得参数,然后再调用call_user_function_ex的,这样就可以不作处理直接把用户参数传给该函数。当然,我们也可以手动创建一个string型zval传给它。

    retval_ptr_ptr用于获取函数的返回值,Zend执行完指定的函数后,它就将返回值的指针填充到这里。这个容器的空间函数会自动帮你申请,所以我们无需手动申请,但在事后这个容器空间的销毁释放工作得由我们自己(使用 zval_dtor())来做。

    param_count和params用于指定函数的参数,param_count是一个标识参数个数的整数,params[] 是一个包含具体参数的数组。

    no_separation用于指定是否在必要时执行zval分离,这在写入非引用zval时发生。应该总是将其设为0,表示执行zval分离,否则可能破坏数据。

    symbol_table用于指定目标函数的active_symbol_table,通常应该使用NULL,这样Zend会为目标函数生成一个空的符号表。下面来看一个具体例子:

    我在c扩展中要使用php函数中的ip2long函数,那么调用方法

    unsigned long ip2longs(const char* ip){  

      zval *funname,*ret_ptr = NULL,*args,**params[1],*args_2;   //如果有多个参数,比方说两个参数,就继续定义变量,看红色部分,如果继续加参数,按红色的步骤继续,定义变量,赋值,放入数组,参数个数也要变
      MAKE_STD_ZVAL(funname);     //创建变量
      ZVAL_STRING(funname, "ip2long", 1);  //设置好zval的类型和值,第二个参数就是我们要调用的ip2long函数 ,这两个方法不了解的可以查看http://www.cunmou.com/phpbook/2.3.md

      MAKE_STD_ZVAL(args);
      ZVAL_STRING(args,ip,1);

      MAKE_STD_ZVAL(args_2);
      ZVAL_LONG(args_2,123); //这是第二个参数,创建变量并赋值,整形只有两个参数,别的类型可以上网查

      params[0] = &args;  //把参数放入数组中

      params[1] = &args_2; // 放入数组

      call_user_function_ex(EG(function_table), NULL, funname, &ret_ptr, 2, params, 0, EG(active_symbol_table));  //调用函数,第5个参数代表参数个数我们这是2

      zval_ptr_dtor(&ret_ptr); //销毁手动创建的空间

      return ret_ptr->value.lval;  //获取返回的值,因为函数返回是一个long型的,所以取lval
    }

    //该函数是php可以直接调用的函数。功能是用来判断某个ip是否在内网中

    PHP_FUNCTION(is_intranet) {
      char *ip;
      int ip_length=0;
      if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&ip,&ip_length) == FAILURE){
        RETURN_NULL();
      }
      unsigned long ip_long = ip2longs(ip);   //调用声明的函数
      if(ip_long == ip2longs("127.0.0.1") || (ip_long > ip2longs("10.0.0.0") && ip_long <= ip2longs("10.255.255.255")) ||
      (ip_long >= ip2longs("172.16.0.0") && ip_long <= ip2longs("172.31.255.255")) ||
      (ip_long >= ip2longs("192.168.0.0") && ip_long <= ip2longs("192.168.255.255"))
      ) {
        RETURN_BOOL(1);
      } else {
        RETURN_BOOL(0);
      }
    }

    这里在多说下zval的结构,这些是自己平时学习总结的,在此拿出来和大家分享:

    php中的存储的基本单元是zval(Zend Value),zval结构,其实所有用户定义的变量在PHP中都是用zval类型来表示的,当我门 使用zend_parse_parameters函数解析参数时,Zend引擎会根据相应的数据类型进行类型转换,而由于PHP中的数组、对象和资源类 型,在C语言中没有对应的类型,所以无法进行类型转换,它们都使用zval表示。
     
    struct _zval_struct {
        zvalue_value value; 
        unsigned char type; 
        unsigned char is_ref;
        short refcount;
    };
    typedef struct _zval_struct zval;
     
    结构体字段解释:
         (1)zval_value value   

    变量的实际值,具体来说是一个zvalue_value的联合体(union):

    typedef union _zvalue_value {
        long lval;                  /* long value */
        double dval;                /* double value */
        struct {                    /* string */
            char *val;
            int len;
        } str;
        HashTable *ht;              /* hash table value,used for array */
        zend_object_value obj;      /* object */
    } zvalue_value;
         

    zvalue_value结构的说明如下:

    lval    如果变量类型为 IS_LONG、IS_BOOLEAN 或 IS_RESOURCE 就用这个属性值 
    dval    如果变量类型为 IS_DOUBLE 就用这个属性值 
    str    如果变量类型为 IS_STRING 就访问这个属性值。它的字段 len 表示这个字符串的长度,字段 val 则指向该字符串。由于 Zend 使用的是 C 风格的字符串,因此字符串的长度就必须把字符串末尾的结束符 0×00 也计算在内 
    ht    如果变量类型为数组,那这个 ht 就指向数组的哈希表入口 
    obj    如果变量类型为 IS_OBJECT 就用这个属性值
     
     
    (2)type
         zval *uservar;
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",
         &uservar) == FAILURE) {
            RETURN_NULL();
        }
     
    结构体的type的值如下:    获取类型通过 Z_TYPE_P(uservar);
    IS_NULL    表示是一个空值 NULL 
    IS_LONG    是一个(长)整数 
    IS_DOUBLE    是一个双精度的浮点数 
    IS_STRING    是一个字符串 
    IS_ARRAY    是一个数组 
    IS_OBJECT    是一个对象 
    IS_BOOL    是一个布尔值 
    IS_RESOURCE    是一个资源(关于资源的讨论,我们以后会在适当的时候讨论到它) 
    IS_STRING    是一个常量
     
    (3)is_ref
          0 表示这个变量还不是一个引用。1 表示这个变量还有被别的变量所引用
     
    (4) refcount    表示这个变量是否仍然有效。每增加一个对这个变量的引用,这个数值就增加 1。反之,每失去一个对这个变量的引用,该值就会减1。当引用计数减为0的时候,就说明已经不存在对这个变量的引用了,于是这个变量就会自动释放
  • 相关阅读:
    题解 [APIO2014]连珠线
    题解 [ZJOI2019]语言
    题解 Hero meet devil
    题解 [BJOI2019]奥术神杖
    题解「2017 山东一轮集训 Day1 / SDWC2018 Day1」Set
    题解 Sue的小球/名次排序问题/方块消除/奥运物流
    题解 「一本通 5.4 练习 1」涂抹果酱
    Mybatis的类型转换
    面试题:微服务熔断降级限流
    面试题:微服务网关
  • 原文地址:https://www.cnblogs.com/zhb-php/p/5741169.html
Copyright © 2020-2023  润新知