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.\n"); } 

使用方法如下:

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM