开发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)




 

你可能感兴趣的:(开发PHP扩展-提供一个可以被PHP使用的类)