場景:咱們想要在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,咱們寫一個能夠調用用戶定義類的方法的函數php
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"); }
使用方法以下:php7
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); }