用C++擴展PHP

     前端時間寫過一個Repl的PHP Extension, 記得當時在國內的中文網站上,相關的資料真的很少, 今天就拋磚引玉寫一些,希望對後來者有所幫助, 那些基本的東西我就不贅述了, 主要談談, PHP Exitension和 PHP 之間的參數傳遞問題;
     先說說我寫的那個PHP Extension, 因爲Repl(Yahoo的一個用於數據同步的工具)提供了C++ API,所以我的實現方式是, 先用C++對原有的API進行包裝,以適合擴展使用, 生成一個C++的動態庫.SO, 然後在PHP Extension中,簡單的獲取PHP傳來的參數,調用有前面提及的動態庫提供的API, 而問題就在於, PHP 和 PHP Extension之間的參數傳遞方法.
     (生成動態庫,你只要使用 g++ -fpic -shared 編譯即可.)
     1, 取得PHP傳遞來的參數的數量
        這個問題,zend API定義了一個宏ZEND_NUM_ARGS(),
                if(ZEND_NUM_ARGS() != 2) WRONG_PARAM_COUNT;
         WRONG_PARAM_COUNT也是一個宏.主要負責拋出一個默認的錯誤信息,並返回調用者.
         在zend_API.h中的定義如下,很直觀了
                 ZEND_API void wrong_param_count(void);
                 #define WRONG_PARAM_COUNT { wrong_param_count(); return; }
  2, 取得參數
         這個用到了zend API中的一個函數:
               int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, …);
         int num_args : 要接受的參數個數;並總是應該後面跟着 TSRMLS_DC;
         char * type_spec : 類似與printf的格式控制字符,表明了要接受的參數的類型;
         後面就緊跟着要獲得參數的變量的指針;
         控制字符主要的類型有:
            
      • l - 長整形

      • d - 雙精度浮點類型

      • s - 字符串 (也可能是空字節)和其長度

      • b - 布爾型

      • r - 資源, 保存在 zval*

      • a - 數組, 保存在zval*

      • o - (任何類的)對象, 保存在 zval *

      • O - (由class entry 指定的類的)對象, 保存在 zval *

      • z - 實際的 zval*

下面的一些字符在類型說明字符串(就是那個char *type_spec)中具有特別的含義:

  • | - 表明後面的參數都是可選參數。如果用戶沒有傳進來這些參數值,那麼這些值就會被初始化成默認值。

  • / - 表明前面的參數會被參數解析函數應用 SEPARATE_ZVAL_IF_NOT_REF() 方式來提供這個參數的一份拷貝,除非這些參數是一個引用。

  • ! - 表明前面的參數允許被設定爲 NULL(僅用在 a、o、O、r和z身上)。如果傳進來了一個 NULL 值,則存儲該參數的變量將會設置爲 NULL。

  • 來看個例子 :
    /* 取得一個長整型,一個字符串和它的長度,再取得一個 zval 值。 */
    long l;
    char *s;
    int s_len;
    zval *param;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
                              "lsz", &l, &s, &s_len, &param) == FAILURE) {
        return;
    }

        在我的擴展中,我使用了zend_get_parameters_ex() , 它將所有取回的參數都放在一個zval**(後面會講)的結構中;
                  zval ** z_array;
                  if(FAILURE = zend_get_parameters_ex(1, &z_array){
                          RETURN_BOOL(0);}
        而後,你就可以很控制函數對參數進行解析進行控制; 這也是我爲什麼選用這個的原因之一  :)
        3,核心的核心,zval; 

        先來看看 zval的定義:
           typedef pval zval;
       typedef struct _zval_struct zval;
       typedef union _zvalue_value {
       long lval; /* long value */
       double dval; /* double value */
       struct {
        char *val;
        int len;
       } str;
       HashTable *ht; /* hash table value */
       struct {
        zend_class_entry *ce;
        HashTable *properties;
      } obj;
      } zvalue_value;

    struct _zval_struct {
    /* Variable information */
    zvalue_value value; /* value */
    unsigned char type; /* active type */
    unsigned char is_ref;
    short refcount;
};

     可以看出, zval中的 value是個聯合體,顯然它是用來容納各種類型的值的, 相應的有個type來指明值的類型,is_ref 和 refcount是用來指明是否是引用和,引用計數的;

      好到這一步,我們已經將參數的"值"取來了,現在的問題就是讓它變成C++中的值(有類型);
      我寫的一段數組解析代碼:

    std::map<string, string> photo;
    convert_to_array_ex(z_array);
    count = zend_hash_num_elements(Z_ARRVAL_PP(z_array));
    zend_hash_internal_pointer_reset(Z_ARRVAL_PP(z_array));
    for (i = 0; i < count; i ++)
    {
        char* key;
        unsigned long idx;
        if (zend_hash_get_current_key(Z_ARRVAL_PP(z_array), &key, &idx, 0) == HASH_KEY_IS_STRING)
        {
            zend_hash_get_current_data(Z_ARRVAL_PP(z_array), (void**) &z_item);
            convert_to_string_ex(z_item);
            photo.insert(pair<string, string>(string(key), string(Z_STRVAL_PP(z_item))));
        }
        zend_hash_move_forward(Z_ARRVAL_PP(z_array));
    }

     今天就寫這麼多,改天有時間,再補。

        更多內容, 可以參看  yAnbiN 的 深入 PHP 內核

        做個廣告,呵呵,也可以參看由我們牛X的戰友Ranix翻譯的Programming PHP的Extending PHP一章

                     //By:惠新宸 (xinchen.hui(at)alibaba-inc.com)轉載請註明出處;

 
發佈了31 篇原創文章 · 獲贊 9 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章