c擴展調用php的函數(調用實現php函數的c函數)


上一次是寫的c擴展調用c的標准函數,但是只能調用頭文件中申明的函數,今天來說下c擴展調用實現php函數的c函數,比方說,c擴展要用到php中ip2long這個函數,但是c不可能去php中調用,肯定是去調用實現php函數的c函數。那么c擴展如何調用c內核對php的API呢?

這里要用到一個函數: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())來做。

param_count和params用於指定函數的參數,param_count是一個標識參數個數的整數,params[] 是一個包含具體參數的數組。

no_separation用於指定是否在必要時執行zval分離,這在寫入非引用zval時發生。應該總是將其設為0,表示執行zval分離,否則可能破壞數據。

symbol_table用於指定目標函數的active_symbol_table,通常應該使用NULL,這樣Zend會為目標函數生成一個空的符號表。下面來看一個具體例子:

 

我在c擴展中要使用php函數中的ip2long函數,那么調用方法

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的結構,這些是自己平時學習總結的,在此拿出來和大家分享:

php中的存儲的基本單元是zval(Zend Value),zval結構,其實所有用戶定義的變量在PHP中都是用zval類型來表示的,當我門 使用zend_parse_parameters函數解析參數時,Zend引擎會根據相應的數據類型進行類型轉換,而由於PHP中的數組、對象和資源類 型,在C語言中沒有對應的類型,所以無法進行類型轉換,它們都使用zval表示。
 
struct _zval_struct {
    zvalue_value value; 
    unsigned char type; 
    unsigned char is_ref;
    short refcount;
};
typedef struct _zval_struct zval;
 
結構體字段解釋:
     (1) zval_value value   

變量的實際值,具體來說是一個zvalue_value的聯合體(union):

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {                    /* string */
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value,used for array */
    zend_object_value obj;      /* object */
} zvalue_value;
     

zvalue_value結構的說明如下:

lval    如果變量類型為 IS_LONG、IS_BOOLEAN 或 IS_RESOURCE 就用這個屬性值 
dval    如果變量類型為 IS_DOUBLE 就用這個屬性值 
str    如果變量類型為 IS_STRING 就訪問這個屬性值。它的字段 len 表示這個字符串的長度,字段 val 則指向該字符串。由於 Zend 使用的是 C 風格的字符串,因此字符串的長度就必須把字符串末尾的結束符 0×00 也計算在內 
ht    如果變量類型為數組,那這個 ht 就指向數組的哈希表入口 
obj    如果變量類型為 IS_OBJECT 就用這個屬性值
 
 
(2)type
     zval *uservar;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",
     &uservar) == FAILURE) {
        RETURN_NULL();
    }
 
結構體的type的值如下:    獲取類型通過 Z_TYPE_P(uservar);
IS_NULL    表示是一個空值 NULL 
IS_LONG    是一個(長)整數 
IS_DOUBLE    是一個雙精度的浮點數 
IS_STRING    是一個字符串 
IS_ARRAY    是一個數組 
IS_OBJECT    是一個對象 
IS_BOOL    是一個布爾值 
IS_RESOURCE    是一個資源(關於資源的討論,我們以后會在適當的時候討論到它) 
IS_STRING    是一個常量
 
(3)is_ref
      0 表示這個變量還不是一個引用。1 表示這個變量還有被別的變量所引用
 
(4) refcount    表示這個變量是否仍然有效。每增加一個對這個變量的引用,這個數值就增加 1。反之,每失去一個對這個變量的引用,該值就會減1。當引用計數減為0的時候,就說明已經不存在對這個變量的引用了,於是這個變量就會自動釋放

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM