開發PHP擴展-提供一個可以被PHP使用的類

· 作者:laruence(http://www.laruence.com/)
· 本文地址: http://www.laruence.com/2008/04/16/19.html
· 轉載請註明出處


   還是那個關於開發安全簽名的PHP模塊, 今天將它包裝成一個PHP的CLASS,也同樣,網上的資料少之甚少,於是我想將經驗寫一篇,關於如何在Extension Module中創建一個可以被PHP訪問的對象的文章。 和大家分享。

    首先,讓我們達成幾個共識:

        1.我的模塊叫做getCookie. 使用C++編寫,源文件是getCookie.cc,現在我要給它添加一個可以爲PHP使用的對象;

        2.DOC_ROOT 是你的擴展開發目錄,比如我的擴展名字叫getCookie,那麼目錄就在/home/share/php/getCookie下;

        3.我要添加的類叫做XCSecure,爲什麼要叫XC,因爲是我名字的縮寫:)

        4.我的XCSecure完成一個功能,構造這個對象的時候,傳入一個字符串, 返回一個以這個字符串爲key,和cookie經過MD5後的字符串;

        5.我假設你已經對基本的如何在linux下開發一個PHP Module很熟悉了,如果不熟悉 ,那麼你可以去在網上查閱《深入PHP內核》,翻譯的很不錯;

    現在,我們已經有一定的共識了,那麼開始吧:

    在PHP中,模塊和PHP腳本交換數據,都是通過Zval的,我們要使用自己的C++類,就必須把我們的C++類註冊爲一個資源,然後每次NEW的時候,就註冊一個資源實例,然後記錄它的句柄到Zval中。

   接下來,我們一步一步來做把。

    1. 首先你要在Module中定義你自己的C++ Class。 如下:

     

  XCSecure.h

        #ifndef _XCSECURE_H_

        
#define _XCSECURE_H_

        #include 
<string>

        
using namespace std;

        
class XCSecure{

                
private:

                        
string _sec;

                
public:

                        XCSecure(
char * s);

                        
~XCSecure();

                
char * genSec();

        }
;

        
#endif

 

    然後在XCSecure.cc中定義類函數,並在getCookie.cc中也包含XCSecure.h;

   

  2. 修改config.m4,

   PHP_NEW_EXTENSION(my_module, 'my_module.cc' 'XCSecure.cc', $ext_shared);

    此處要注意的是,'my_module.cc'和'XCSecure.cc'之間使用空格分割;

    然後在DOC_ROOT下運行phpize,使得爲我們生成configure;

    然後在DOC_ROOT下運行./configure 使得爲我們生成Makefile;

    到這裏,我們的前期工作就做好了, 你現在也可以運行make ; make install了,但只會生成一個空的module,接下來就可以填充完善它了。

    3. 修改getCookie.cc,爲容納XCSecure做一些工作:

        首先,我們要定義一個全局的zend_class_entry * ;

        static zend_class_entry * php_xcsecure_ptr;
        然後,我們要定義一個全局句柄(INT型), 我們要把我們的C++類註冊成一個PHP中的資源(Resource), 在PHP中,資源是個很寬泛的概念,比如,鏈接Mysql的句柄,打開一個文件的句柄等等。

        static int de_xcsecure;

    4. 定義我們XCSecure的成員函數:

      function_entry php5_secure_method[] = {

                 PHP_FALIAS(XCSecure, XCSecure_new, NULL)

                 PHP_FALIAS(genSec, XCSecure_genSec, NULL)

                 
{NULL, NULL, NULL}

           }
;

        當然,你還需要自己定義這些函數;       

    5. 我選擇在PHP_MINIT階段來初始化我的類;

       PHP_MINIT_FUNCTION(getCookie)...{


              zend_class_entry php5_secure_entry;

              de_php5_secure 
= zend_register_list_destructors_ex(_de_php5_secure, NULL, "Signature Generate Type", module_number); //爲我們的C++類的對象創建析構函數,並獲得它的資源類型句柄

              INIT_CLASS_ENTRY(php5_secure_entry, 
"XCSecure", php5_secure_method);

              php5_secure_entry_ptr 
= zend_register_internal_class(&php5_secure_entry);//註冊我們的類,這樣在PHP腳本中就可以使用了。

              REGISTER_LONG_CONSTANT(
"XCSECURE_LOAD"1, CONST_CS|CONST_PERSISTENT);

              
return SUCCESS;

            }

        首先我們創建了一個zend_class_entry, 根據變量所表達出來的意思 ,這是一個zend中的對於一個對象的操作句柄類型

 

        然後,我們註冊了我們這個對象的清理函數, 這個是因爲,當在PHP腳本中unset我們的變量的時候,zend內核必須要知道如何清理我們的對象,釋放我們佔用的內存;

        然後,我們初始化了我們的類的申明,

            INIT_CLASS_ENTRY(php5_secure_entry, "XCSecure", php5_secure_method);

        參數2是我們的類型名字,在PHP中var_dump的時候 ,就會顯示出來,如:

            object(XCSecure)#1 (0) { }

        參數3是我們已經定義過的我們的類的成員函數;

        然後我們向zend內核註冊了我們的類,

            並將它返回的指針付給了我們的全局變量(這個我自己另有用處)

                zend_register_internal_class(&php5_secure_entry);

        這個時候,我們的對象就算註冊完成了,現在已經可以在腳本中使用了;

              $xcSecure = new XCSecure('laruence');

 6. 當在PHP腳本中new一個我們的對象的時候,Zend就會自動調用我們的構造函數,接下來,完成我們的構造函數:

PHP_FUNCTION(XCSecure_new)......{

        XCSecure 
* instance;
        
char* s;
        
int len = 0;
        
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s"&s, &len) == FAILURE) ......{
                RETURN_FALSE;
        }


        instance 
= new XCSecure(s);
        zval 
* res_id;
        MAKE_STD_ZVAL(res_id);
        
int id = ZEND_REGISTER_RESOURCE(NULL, instance, le_xc_secure);
        ZVAL_LONG(res_id, id);
        zend_hash_update(Z_OBJPROP_P(getThis()), Hash_Key, 
sizeof(Hash_Key), &res_id, sizeof(res_id), NULL);
    }

 

首先我們取得用戶 new XCSecure($para)的參數,它是個字符串,然後,new了一個C++對象, 並註冊它爲一個資源實例,注意ZEND_REGISTER_RESOURCE的最後一個參數,是我們當時定義我們的資源析構函數的時候 ,返回的資源類型句柄。

然後我們把註冊資源實例以後返回的資源句柄,id 保存在一個Zval中。

當用戶在PHP中創建一個我們的類的實例的時候,Zend會調用一個zend_objects_new來創建一個標準的Zend對象,並把它存入zend_objects_store, 它是一個Bucket的數組,然後把我們的對象在數組中的索引(Zend中稱爲handler),存入一個zend_object_value結構,然後把這個zend_object_value存入一個zval結構,之後,zend會再調用我們的構造函數,並且this指針指向這個zval, 所以,在我們的構造函數中,就可以通過getThis(),來獲得這個zval,當然,也可以直接使用this_ptr;

註冊完資源後,構造函數就把得到的資源句柄(其實也是一個list的索引),存入this指向的zval的object的properties屬性中(這是一個哈希表),以後當用戶通過我們的對象調用類函數的時候,我們就可以通過this獲得這個對象,然後再通過對象的屬性中的資源句柄,獲得我們的C++對象。比如:

 

PHP_FUNCTION(XCSecure_genSec){
        zval 
** rsc;
        XCSecure 
* secure;
        
if(zend_hash_find(Z_OBJPROP_P(getThis()), Hash_Key, sizeof(Hash_Key), (void **)&rsc) == SUCCESS){
                secure 
= (XCSecure *)zend_fetch_resource(NULL, Z_LVAL_PP(rsc), NULL, NULL, 1, le_xc_secure);
        }

        ZVAL_STRING(return_value, secure
->genSec(), 1);
      }

 

首先我們通過getThis(),取得this指針, 然後通過Z_OBJPROP_P宏,來取得this指向的zval(一個對象object)的對象的propertis屬性,然後再通過zend_hash_find取得構造函數的時候保存的資源句柄。

之後,通過zend_fetch_resource取得構造函數創建的C++對象,之後你就可以象在C++中一樣,隨便使用這個C++對象了

            7.    最後,我定義了個整型常量,這個也是另有它用;現在也可以在腳本中,訪問這個常量了 ,其中的CONST_SC表明我們的這個常量是大小寫敏感的,CONST_PERSISTENT顧名思義了。。

    恩,到現在,我們的對象就已經加入到我們的模塊中了,你現在就可以簡單的make ; make install;然後來測試了 :)

    最後:歡迎來信交流

    附錄,關於本文涉及的Zend宏的定義:

   

zend.h 
typedef _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 */
        zend_object_value obj;
}
 zvalue_value;


struct _zval_struct {
        
/* Variable information */
        zvalue_value value;             
/* value */
        zend_uint refcount;
        zend_uchar type;        
/* active type */
        zend_uchar is_ref;
}
;

zend_object 
typedef 
struct _zend_object {
        zend_class_entry 
*ce;
        HashTable 
*properties;
        HashTable 
*guards; /* protects from __get/__set ... recursion */
}
 zend_object;

zend_class_entry
struct _zend_class_entry {
        
char type;
        
char *name;
        zend_uint name_length;
        
struct _zend_class_entry *parent;
        
int refcount;
        zend_bool constants_updated;
        zend_uint ce_flags;

        HashTable function_table;
        HashTable default_properties;
        HashTable properties_info;
        HashTable default_static_members;
        HashTable 
*static_members;
        HashTable constants_table;
        
struct _zend_function_entry *builtin_functions;

        union _zend_function 
*constructor;
        union _zend_function 
*destructor;
        union _zend_function 
*clone;
        union _zend_function 
*__get;
        union _zend_function 
*__set;
        union _zend_function 
*__unset;
        union _zend_function 
*__isset;
        union _zend_function 
*__call;
        union _zend_function 
*__tostring;
        union _zend_function 
*serialize_func;
        union _zend_function 
*unserialize_func;

        zend_class_iterator_funcs iterator_funcs;

        
/* handlers */
        zend_object_value (
*create_object)(zend_class_entry *class_type TSRMLS_DC);
        zend_object_iterator 
*(*get_iterator)(zend_class_entry *ce, zval *objectint by_ref TSRMLS_DC);
        
int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* a class implements this interface */

        
/* serializer callbacks */
        
int (*serialize)(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
        
int (*unserialize)(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);

        zend_class_entry 
**interfaces;
        zend_uint num_interfaces;

        
char *filename;
        zend_uint line_start;
        zend_uint line_end;
        
char *doc_comment;
        zend_uint doc_comment_len;

        
struct _zend_module_entry *module;
}
;


zend_types.h
typedef 
struct _zend_object_value {
        zend_object_handle handle;
        zend_object_handlers 
*handlers;
}
 zend_object_value;
typedef unsigned 
int zend_object_handle;

zend_object_handlers.h
struct _zend_object_handlers {
        
/* general object functions */
        zend_object_add_ref_t                           add_ref;
        zend_object_del_ref_t                           del_ref;
        zend_object_clone_obj_t                         clone_obj;
        
/* individual object functions */
        zend_object_read_property_t                     read_property;
        zend_object_write_property_t                    write_property;
        zend_object_read_dimension_t                    read_dimension;
        zend_object_write_dimension_t                   write_dimension;
        zend_object_get_property_ptr_ptr_t              get_property_ptr_ptr;
        zend_object_get_t                               
get;
        zend_object_set_t                               
set;
        zend_object_has_property_t                      has_property;
        zend_object_unset_property_t                    unset_property;
        zend_object_has_dimension_t                     has_dimension;
        zend_object_unset_dimension_t                   unset_dimension;
        zend_object_get_properties_t                    get_properties;
        zend_object_get_method_t                        get_method;
        zend_object_call_method_t                       call_method;
        zend_object_get_constructor_t                   get_constructor;
        zend_object_get_class_entry_t                   get_class_entry;
        zend_object_get_class_name_t                    get_class_name;
        zend_object_compare_t                           compare_objects;
        zend_object_cast_t                              cast_object;
        zend_object_count_elements_t                    count_elements;
}
;



#define INIT_CLASS_ENTRY(class_container, class_name, functions) INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, func
tions, NULL, NULL, NULL)

#define INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset) 
    INIT_OVERLOADED_CLASS_ENTRY_EX(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset, NULL,
 NULL)

#define INIT_OVERLOADED_CLASS_ENTRY_EX(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset, h
andle_propunset, handle_propisset) 
    
{                                                           
        class_container.name 
= strdup(class_name);              
        class_container.name_length 
= sizeof(class_name) - 1;   
        class_container.builtin_functions 
= functions;          
        class_container.constructor 
= NULL;                     
        class_container.destructor 
= NULL;                      
        class_container.clone 
= NULL;                           
        class_container.serialize 
= NULL;                       
        class_container.unserialize 
= NULL;                     
        class_container.create_object 
= NULL;                   
        class_container.interface_gets_implemented 
= NULL;      
        class_container.__call 
= handle_fcall;                  
        class_container.__tostring 
= NULL;                      
        class_container.__get 
= handle_propget;                 
        class_container.__set 
= handle_propset;                 
        class_container.__unset 
= handle_propunset;             
        class_container.__isset 
= handle_propisset;             
        class_container.serialize_func 
= NULL;                  
        class_container.unserialize_func 
= NULL;                
        class_container.serialize 
= NULL;                       
        class_container.unserialize 
= NULL;                     
        class_container.parent 
= NULL;                          
        class_container.num_interfaces 
= 0;                     
        class_container.interfaces 
= NULL;                      
        class_container.get_iterator 
= NULL;                    
        class_container.iterator_funcs.funcs 
= NULL;            
        class_container.module 
= NULL;                          
    }


ZEND_API zend_class_entry 
*zend_register_internal_class(zend_class_entry *orig_class_entry TSRMLS_DC)
{
        
return do_register_internal_class(orig_class_entry, 0 TSRMLS_CC);
}


static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, zend_uint ce_flags TSRMLS_DC)
{
        zend_class_entry 
*class_entry = malloc(sizeof(zend_class_entry));
        
char *lowercase_name = malloc(orig_class_entry->name_length + 1);
        
*class_entry = *orig_class_entry;

        class_entry
->type = ZEND_INTERNAL_CLASS;
        zend_initialize_class_data(class_entry, 
0 TSRMLS_CC);
        class_entry
->ce_flags = ce_flags;
        class_entry
->module = EG(current_module);

        
if (class_entry->builtin_functions) {
                zend_register_functions(class_entry, class_entry
->builtin_functions, &class_entry->function_table, MODULE_PERS
ISTENT TSRMLS_CC);
        }


        zend_str_tolower_copy(lowercase_name, orig_class_entry
->name, class_entry->name_length);
        zend_hash_update(CG(class_table), lowercase_name, class_entry
->name_length+1&class_entry, sizeof(zend_class_entry *)
, NULL);
        free(lowercase_name);
        
return class_entry;
}



#define ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type)  
    zend_register_resource(rsrc_result, rsrc_pointer, rsrc_type);


ZEND_API 
int zend_register_resource(zval *rsrc_result, void *rsrc_pointer, int rsrc_type)
{
        
int rsrc_id;

        rsrc_id 
= zend_list_insert(rsrc_pointer, rsrc_type);

        
if (rsrc_result) {
                rsrc_result
->value.lval = rsrc_id;
                rsrc_result
->type = IS_RESOURCE;
        }


        
return rsrc_id;
}



ZEND_API 
int zend_list_insert(void *ptr, int type)
{
        
int index;
        zend_rsrc_list_entry le;
        TSRMLS_FETCH();

        le.ptr
=ptr;
        le.type
=type;
        le.refcount
=1;

        index 
= zend_hash_next_free_element(&EG(regular_list));

        zend_hash_index_update(
&EG(regular_list), index, (void *&le, sizeof(zend_rsrc_list_entry), NULL);
        
return index;
}



ZEND_API 
void *zend_object_store_get_object(zval *zobject TSRMLS_DC)
{
        zend_object_handle handle 
= Z_OBJ_HANDLE_P(zobject);

        
return EG(objects_store).object_buckets[handle].bucket.obj.object;
}


zend_operators.h:
#define Z_OBJ_HANDLE_P(zval_p)  Z_OBJ_HANDLE(*zval_p)
zend_operators.h:
#define Z_OBJVAL(zval)                       (zval).value.obj
zend_operators.h:
#define Z_OBJ_HANDLE(zval)           Z_OBJVAL(zval).handle
zend_globals_macros.h:# define EG(v) TSRMG(executor_globals_id, zend_executor_globals 
*, v)
TSRM.h:
#define TSRMG(id, type, element)       (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define Z_OBJPROP(zval)                      Z_OBJ_HT((zval))->get_properties(&(zval) TSRMLS_CC)




 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章