zoukankan      html  css  js  c++  java
  • php7扩展开发[9] zend_call_method和zend_call_function


    场景:我们想要在php7扩展中调用用户自定的类中的方法,而且方法有多个参数,找到以下方
    法,没有看到可以超过两个参数的方法。所以一直向下查找,发现zend_call_method调用的
    zend_call_function,但是并非只能传两个参数。一直追下去。这样我们用zend_call_function来调用多个参数,实现我们要的目的。
    
    函数原型:在 Zend/zend_interfaces.下:
    #define zend_call_method_with_0_params(obj, obj_ce, fn_proxy, function_name, retval) 
        zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 0, NULL, NULL)
    
    #define zend_call_method_with_1_params(obj, obj_ce, fn_proxy, function_name, retval, arg1) 
        zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 1, arg1, NULL)
    
    #define zend_call_method_with_2_params(obj, obj_ce, fn_proxy, function_name, retval, arg1, arg2) 
        zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 2, arg1, arg2)
    
    
    上面三个调用下面的zend_call_methodZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval_ptr, int param_count, zval* arg1, zval* arg2)
    {
        int result;
        zend_fcall_info fci;
        zval retval;
        HashTable *function_table;
    
        zval params[2];
    
        if (param_count > 0) {
            ZVAL_COPY_VALUE(&params[0], arg1);
        }
        if (param_count > 1) {
            ZVAL_COPY_VALUE(&params[1], arg2);
        }
    
        //看这里.原来可以传多个参数,只是这里做了限制
        //看这里.原来可以传多个参数,只是这里做了限制
        //看这里.原来可以传多个参数,只是这里做了限制
    
        fci.size = sizeof(fci);
    
        fci.object = (object && Z_TYPE_P(object) == IS_OBJECT) ? Z_OBJ_P(object) : NULL;
        ZVAL_STRINGL(&fci.function_name, function_name, function_name_len);
        fci.retval = retval_ptr ? retval_ptr : &retval;
        fci.param_count = param_count;
        fci.params = params;
        fci.no_separation = 1;
        fci.symbol_table = NULL;
    
        if (!fn_proxy && !obj_ce) {
    
            fci.function_table = !object ? EG(function_table) : NULL;
    
            //看这里.原来调用的是zend_call_function
            //看这里.原来调用的是zend_call_function
            //看这里.原来调用的是zend_call_function
    
            result = zend_call_function(&fci, NULL);
            zval_ptr_dtor(&fci.function_name);
        } else {
            zend_fcall_info_cache fcic;
    
            fcic.initialized = 1;
            if (!obj_ce) {
                obj_ce = object ? Z_OBJCE_P(object) : NULL;
            }
            if (obj_ce) {
                function_table = &obj_ce->function_table;
            } else {
                function_table = EG(function_table);
            }
            if (!fn_proxy || !*fn_proxy) {
                if ((fcic.function_handler = zend_hash_find_ptr(function_table, Z_STR(fci.function_name))) == NULL) {
                    /* error at c-level */
                    zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name);
                }
                if (fn_proxy) {
                    *fn_proxy = fcic.function_handler;
                }
            } else {
                fcic.function_handler = *fn_proxy;
            }
            fcic.calling_scope = obj_ce;
            if (object) {
                fcic.called_scope = Z_OBJCE_P(object);
            } else {
                zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
    
                if (obj_ce &&
                    (!called_scope ||
                     !instanceof_function(called_scope, obj_ce))) {
                    fcic.called_scope = obj_ce;
                } else {
                    fcic.called_scope = called_scope;
                }
            }
            fcic.object = object ? Z_OBJ_P(object) : NULL;
    
            //看这里.原来调用的是zend_call_function
            //看这里.原来调用的是zend_call_function
            //看这里.原来调用的是zend_call_function
    
            result = zend_call_function(&fci, &fcic);
            zval_ptr_dtor(&fci.function_name);
        }
        if (result == FAILURE) {
            /* error at c-level */
            if (!obj_ce) {
                obj_ce = object ? Z_OBJCE_P(object) : NULL;
            }
            if (!EG(exception)) {
                zend_error_noreturn(E_CORE_ERROR, "Couldn't execute method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name);
            }
        }
        /* copy arguments back, they might be changed by references */
        if (param_count > 0 && Z_ISREF(params[0]) && !Z_ISREF_P(arg1)) {
            ZVAL_COPY_VALUE(arg1, &params[0]);
        }
        if (param_count > 1 && Z_ISREF(params[1]) && !Z_ISREF_P(arg2)) {
            ZVAL_COPY_VALUE(arg2, &params[1]);
        }
        if (!retval_ptr) {
            zval_ptr_dtor(&retval);
            return NULL;
        }
        return retval_ptr;
    }
    
    
    我们可以看到zend_call_method调用zend_call_function,函数体定义在Zend/zend_API.h下:
    ZEND_API int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache);
    我们可以看到要想使用zend_call_function 必须要了解zend_fcall_info结构体。
    
    PHP7 中
    typedef struct _zend_fcall_info {
        size_t size;
        HashTable *function_table;
        zval function_name;
        zend_array *symbol_table;
        zval *retval;
        zval *params;
        zend_object *object;
        zend_bool no_separation;
        uint32_t param_count;
    } zend_fcall_info;
    
    由于网上流传的代码都是PHP5的代码,我们也就再贴一下PHP5的代码,到改造代码时,可以有个借鉴。
    PHP5 中
    typedef struct _zend_fcall_info {
       size_t size;
       HashTable *function_table;
       zval *function_name;
       HashTable *symbol_table;
       zval **retval_ptr_ptr;
       zend_uint param_count;
       zval ***params;
       zval *object_ptr;
       zend_bool no_separation;
    } zend_fcall_info;
    
    其中有变化的参数:
    zval *function_name;   ->  zval function_name;
    zval **retval_ptr_ptr; ->  zval *retval;
    zval *object_ptr;      ->  zend_object *object;
    zval ***params;        ->  zval *params;
    
    
    在PHP7的扩展使用PHP5的代码make时,会出现如下错误:
    error: incompatible types when assigning to type ‘struct zval *’ from type ‘zval’
    error: ‘zend_fcall_info’ has no member named ‘object’
    error: ‘zend_fcall_info’ has no member named ‘retval’
    
    
    我们搜一下php源码中zend_call_function中的使用情况,我们会发现自动加载中的代码如下:
    
    static inline void spl_instantiate_arg_n(zend_class_entry *pce, zval *retval, int argc, zval *argv)
    {
        zend_function *func = pce->constructor;
        zend_fcall_info fci;
        zend_fcall_info_cache fcc;
        zval dummy;
    
        spl_instantiate(pce, retval);
    
        fci.size = sizeof(zend_fcall_info);
        fci.function_table = &pce->function_table;
        ZVAL_STR(&fci.function_name, func->common.function_name);
        fci.object = Z_OBJ_P(retval);
        fci.symbol_table = NULL;
        fci.retval = &dummy;
        fci.param_count = argc;
        fci.params = argv;
        fci.no_separation = 1;
    
        fcc.initialized = 1;
        fcc.function_handler = func;
        fcc.calling_scope = EG(scope);
        fcc.called_scope = pce;
        fcc.object = Z_OBJ_P(retval);
    
        zend_call_function(&fci, &fcc);
    }
    
    

    OK,我们写一个可以调用用户定义类的方法的函数

    int class_call_user_method(zval *retval, zend_class_entry *obj_ce, 
        zval *obj, zval func,  uint32_t params_count, zval params[]){ 
    
    
        HashTable *function_table; 
    
        if(obj) { 
                    function_table = &Z_OBJCE_P(obj)->function_table;
            }else{
                    function_table = (CG(function_table));
        }
    
        // 对象初始化内容,不能放在这里
        // if(!obj_ce){
    
        //      object_init(&obj);
        // }
        // else{
    
        //      object_init_ex(&obj, obj_ce);
        // }
    
        zend_fcall_info fci;  
        fci.size = sizeof(fci);  
        fci.function_table = function_table;  
        fci.object =  obj ? Z_OBJ_P(obj) : NULL;;
        fci.function_name = func;   
        fci.retval = retval;  
        fci.param_count = params_count;  
        fci.params = params;  
        fci.no_separation = 1;  
        fci.symbol_table = NULL;  
    
    
        /**/
        int result;
        result = zend_call_function(&fci, NULL TSRMLS_CC);         //函数调用结束。  
    
    
        if (result == FAILURE) {
    
            zend_printf("error");
        }
    
        /**/
        zend_printf("Success.
    ");
    
    
    }
    

    使用方法如下:

    ZEND_METHOD(udf,get){
    
        //call  __construct
        zval retval;
    
        zval obj;
        object_init_ex(&obj, adf_ce);
    
        zval function_name;
        ZVAL_STRING(&function_name,"__construct");
    
        //int class_call_user_method(zval *retval, zend_class_entry *obj_ce, 
        //zval *obj, zval func,  uint32_t params_count, zval params[])
        class_call_user_method(&retval, adf_ce, &obj, function_name, 0,NULL);
    
    
        //call  set
        zval retval2;
    
        zval obj2;
        object_init_ex(&obj2, adf_ce);
    
    
        zval function_name2;
        ZVAL_STRING(&function_name2,"set");
    
    
        zval params[2];
        ZVAL_STRING(&params[0],"root");
        ZVAL_STRING(&params[1],"tyyy");
    
    
        class_call_user_method(&retval2, adf_ce, &obj2, function_name2, 2, params);
    
    
        //call get
        zval retval3;
    
        zval obj3;
        object_init_ex(&obj3, adf_ce);
    
        zval function_name3;
        ZVAL_STRING(&function_name3,"get");
    
    
        class_call_user_method(&retval3, adf_ce, &obj3, function_name3, 0, NULL);
    
    }
    • 请尊重本人劳动成功,可以随意转载但保留以下信息
    • 作者:岁月经年
    • 时间:2016年03月
    • 首发:http://www.djhull.com
  • 相关阅读:
    【转】想成为为一名架构师,应该掌握哪些技术呢?
    【转】每个好架构师都是一位出色的程序员
    [LeetCode] 597. Friend Requests I: Overall Acceptance Rate 朋友请求 I: 全部的接受率
    [LeetCode]577. Employee Bonus 员工奖金
    570. Managers with at Least 5 Direct Reports 至少有5个直接汇报员工的经理
    [LeetCode] 529. Minesweeper 扫雷
    Generate Maximum revenue by selling K tickets from N windows
    [LeetCode] 311. Sparse Matrix Multiplication 稀疏矩阵相乘
    [LeetCode] 689. Maximum Sum of 3 Non-Overlapping Subarrays 三个非重叠子数组的最大和
    [LeetCode] 403. Frog Jump 青蛙跳
  • 原文地址:https://www.cnblogs.com/djhull/p/5359634.html
Copyright © 2011-2022 走看看