上一次是寫的c擴展調用c的標準函數,可是隻能調用頭文件中申明的函數,今天來講下c擴展調用實現php函數的c函數,比方說,c擴展要用到php中ip2long這個函數,可是c不可能去php中調用,確定是去調用實現php函數的c函數。那麼c擴展如何調用c內核對php的API呢?php
這裏要用到一個函數: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())來作。ui
param_count和params用於指定函數的參數,param_count是一個標識參數個數的整數,params[] 是一個包含具體參數的數組。spa
no_separation用於指定是否在必要時執行zval分離,這在寫入非引用zval時發生。應該老是將其設爲0,表示執行zval分離,不然可能破壞數據。指針
symbol_table用於指定目標函數的active_symbol_table,一般應該使用NULL,這樣Zend會爲目標函數生成一個空的符號表。下面來看一個具體例子:對象
我在c擴展中要使用php函數中的ip2long函數,那麼調用方法ip
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的結構,這些是本身平時學習總結的,在此拿出來和你們分享:
變量的實際值,具體來講是一個zvalue_value的聯合體(union):
zvalue_value結構的說明以下: