标签:
场景:我们想要在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_method:
ZEND_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(¶ms[0], arg1);
}
if (param_count > 1) {
ZVAL_COPY_VALUE(¶ms[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, ¶ms[0]);
}
if (param_count > 1 && Z_ISREF(params[1]) && !Z_ISREF_P(arg2)) {
ZVAL_COPY_VALUE(arg2, ¶ms[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(¶ms[0],"root");
ZVAL_STRING(¶ms[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);
}
php7扩展开发[9] zend_call_method和zend_call_function
标签:
原文地址:http://www.cnblogs.com/djhull/p/5359634.html