PHP7/5扩展开发函数手册(5) - 对象

本文详细介绍了PHP扩展开发中对象的相关操作,包括类的初始化、实体注册、成员函数参数声明、成员变量和常量的声明、对象句柄获取、修改对象属性、调用对象方法及获取对象成员变量值等关键步骤,附带了多个示例代码。

目录

  • String 的两个宏定义
  • 类的初始化 INIT_CLASS_ENTRY
  • 类实体的注册 zend_register_internal_class
  • 成员函数参数 ZEND_BEGIN_ARG_INFO_EX
  • 声明类的成员变量 zend_declare_property
  • 声明类常量 zend_declare_class_constant
  • 类的初始化示例 
  • 获取对象句柄 Z_OBJ_HANDLE
  • 修改类对象/静态成员变量的值 zend_update_property
  • 获取对象自身this 指针 getThis
  • 在扩展内创建对象 object_init
  • 在扩展中调用对象的方法 zend_call_method_with_0_params
  • 获取对象成员变量值 zend_read_property

String 的两个宏定义

在Zend 内核中,针对字符串,定义了两个宏, 这个在后续的字符串处理中经常需要用到,大家留意

----zend_portability.h----
#define ZEND_STRL(str)		(str), (sizeof(str)-1)
#define ZEND_STRS(str)		(str), (sizeof(str))

类的初始化

//初始化类实体
void INIT_CLASS_ENTRY(zend_class_entry ce, char *classname, zend_function_entry *functions);
参数用途
ce类实体 zend_class_entry ,存储类的信息,通常用该指针代表一个类
classname类名称
functions类的成员方法, 定义在 zend_function_entry 结构体数组中。

类实体的注册

//注册一般类实体
zend_class_entry *zend_register_internal_class(zend_class_entry *ce TSRMLS_DC);

//注册带父类的实体
zend_class_entry *zend_register_internal_class_ex(zend_class_entry *ce, 
				zend_class_entry *parent_ce, char *parent_name TSRMLS_DC);
//注册内部类
zend_class_entry *zend_register_internal_interface(zend_class_entry *ce TSRMLS_DC);
 参数用途
ce之前被INIT_CLASS_ENTRY初始化过的类实体
parent_ce已经注册过的该类的父类实体
parent_name父类的类名

一个空类,连构造函数也没有的类实体的注册示例:

----php_swoole.h----
extern zend_class_entry *swoole_server_class_entry_ptr; //类实体的声明

----swoole.c----
//类成员函数的实体
static zend_function_entry swoole_server_methods[] = {
    {NULL, NULL, NULL}
};

//类实体
zend_class_entry swoole_server_ce;
zend_class_entry *swoole_server_class_entry_ptr;

...

PHP_MINIT_FUNCTION(swoole)
{
    //类的初始化
    INIT_CLASS_ENTRY(swoole_server_ce, "swoole_server", swoole_server_methods);
    swoole_server_class_entry_ptr = zend_register_internal_class(&swoole_server_ce TSRMLS_CC);
    ...
}

成员函数参数

//类成员函数的参数宏定义
ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)
    ZEND_ARG_PASS_INFO(by_ref)
    ZEND_ARG_INFO(by_ref, name)
    ZEND_ARG_ARRAY_INFO(by_ref, name, allow_null)
    ZEND_ARG_OBJ_INFO(by_ref, name, classname, allow_null)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX 宏用于声明参数的属性
参数用途
name参数信息名
pass_rest_by_reference是否引用传参,如果这个参数设置为1, 所有没有在结构中显式描述的参数都被认为是编译期引用传值的参数.
return_reference引用返回,告诉Zend你的函数需要用自己的zval覆写return_value_ptr.
required_num_args必输的参数个数
其他的宏用于描述,参数的具体类型
用途
ZEND_ARG_PASS_INFO 标识是否对应的参数应该被强制为引用传值
ZEND_ARG_INFO普通参数, name 为参数名
ZEND_ARG_ARRAY_INFO传入参数必须为数组, name 为参数名, allow_null 是否可以为空
ZEND_ARG_OBJ_INFO传入参数必须为对象,name 为参数名, classname 为对象名, allow_null 是否可空

 

声明类的成员变量

int zend_declare_property(zend_class_entry *ce, char *name, int name_length, 
		zval *value, int access_type TSRMLS_DC);
int zend_declare_property_ex(zend_class_entry *ce, char *name, int name_length, 
		zval *value, int access_type, char *doc_comment, int doc_comment_len TSRMLS_DC);
int zend_declare_property_null(zend_class_entry *ce, char *name, int name_length, 
		int access_type TSRMLS_DC);
int zend_declare_property_bool(zend_class_entry *ce, char *name, int name_length, 
		long value,int access_type TSRMLS_DC);
int zend_declare_property_long(zend_class_entry *ce, char *name, int name_length, 
		long value, int access_type TSRMLS_DC);
int zend_declare_property_double(zend_class_entry *ce, char *name, int name_length, 
		double value, int access_type TSRMLS_DC);
int zend_declare_property_string(zend_class_entry *ce, char *name, int name_length, 
		char *value, int access_type TSRMLS_DC);
int zend_declare_property_stringl(zend_class_entry *ce, char *name, int name_length,
		char *value, int value_len, int access_type TSRMLS_DC);
参数用途
ce类实体
name成员变量名
name_length不包含结尾符NULL的变量名长度
value变量值, 每个函数指定的类型不一样,注意,如果是zval作为成员变量,需要注意zval的内存必须始终分配着
value_len该值指定不包含结尾符NULL的value的长度,只在 zend_declare_property_stringl 函数中需要指定
access_type成员变量的访问类型,包含ZEND_ACC_PUBLIC, ZEND_ACC_PROTECTED, 或者 ZEND_ACC_PRIVATE,也可以用二进制或 " | " 结合 ZEND_ACC_STATIC 参数使用,将参数定义为类的静态变量

 

声明类常量

int zend_declare_class_constant(zend_class_entry *ce, char *name, 
		size_t name_length, zval *value TSRMLS_DC);
int zend_declare_class_constant_long(zend_class_entry *ce, char *name, 
		size_t name_length, long value TSRMLS_DC);
int zend_declare_class_constant_bool(zend_class_entry *ce, char *name, 
		size_t name_length, zend_bool value TSRMLS_DC);
int zend_declare_class_constant_double(zend_class_entry *ce, char *name, 
		size_t name_length, double value TSRMLS_DC);
int zend_declare_class_constant_string(zend_class_entry *ce, char *name, 
		size_t name_length, char *value TSRMLS_DC);
int zend_declare_class_constant_stringl(zend_class_entry *ce, char *name, 
		size_t name_length, char *value, size_t value_len TSRMLS_DC);
参数用途
ce类实体
name常量名
name_length不包含结尾符NULL的常量名长度
value常量值, 每个函数指定的类型不一样,注意,如果是zval作为常量,需要注意zval的内存必须始终分配着
value_len该值指定不包含结尾符NULL的value的长度,只在 zend_declare_class_constant_stringl 函数中需要指定

 

类的初始化示例

----php7_wrapper.h----

#if PHP_MAJOR_VERSION < 7
    #define sw_zend_register_internal_class_ex    zend_register_internal_class_ex

#else /* PHP Version 7 */
    #define sw_zend_register_internal_class_ex(entry,parent_ptr,str)    zend_register_internal_class_ex(entry,parent_ptr)

#endif

----php_swoole.h----

extern zend_class_entry *swoole_server_class_entry_ptr;

//类成员方法的声明
PHP_METHOD(swoole_server, __construct);
PHP_METHOD(swoole_server, __destruct);
PHP_METHOD(swoole_server, set);

#define SW_STRL(s)             s, sizeof(s)

----swoole.c----

//参数的定义
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) //函数无参数
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server__construct, 0, 0, 1) //4个参数,1个必输
    ZEND_ARG_INFO(0, host)
    ZEND_ARG_INFO(0, port)
    ZEND_ARG_INFO(0, mode)
    ZEND_ARG_INFO(0, sock_type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server_set_oo, 0, 0, 1) //一个数组参数
    ZEND_ARG_ARRAY_INFO(0, settings, 0)
ZEND_END_ARG_INFO()

//类成员函数的实体
static zend_function_entry swoole_server_methods[] = {
    PHP_ME(swoole_server, __construct, arginfo_swoole_server__construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(swoole_server, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR)
    PHP_ME(swoole_server, set, arginfo_swoole_server_set_oo, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};

//类实体
zend_class_entry swoole_server_ce;
zend_class_entry *swoole_server_class_entry_ptr;

...

PHP_MINIT_FUNCTION(swoole)
{
    //类的初始化
    INIT_CLASS_ENTRY(swoole_server_ce, "swoole_server", swoole_server_methods);
    swoole_server_class_entry_ptr = zend_register_internal_class(&swoole_server_ce TSRMLS_CC);
    swoole_server_class_entry_ptr->serialize = zend_class_serialize_deny;
    swoole_server_class_entry_ptr->unserialize = zend_class_unserialize_deny;
    
    //声明类的成员变量
    zend_declare_property_null(swoole_server_class_entry_ptr, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC);
    zend_declare_property_long(swoole_server_class_entry_ptr, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
    zend_declare_property_long(swoole_server_class_entry_ptr, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
    zend_declare_property_long(swoole_server_class_entry_ptr, ZEND_STRL("mode"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
    zend_declare_property_bool(swoole_server_class_entry_ptr, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
    zend_declare_property_long(swoole_server_class_entry_ptr, SW_STRL("errCode")-1, 0, ZEND_ACC_PUBLIC TSRMLS_CC);
    zend_declare_property_string(swoole_server_class_entry_ptr, SW_STRL("errMsg")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);

    //声明类常量
    zend_declare_class_constant_long(swoole_server_class_entry_ptr, SW_STRL("PIPE_MASTER")-1, 1 TSRMLS_CC);
    zend_declare_class_constant_long(swoole_server_class_entry_ptr, SW_STRL("PIPE_WORKER")-1, 2 TSRMLS_CC);
    zend_declare_class_constant_long(swoole_server_class_entry_ptr, SW_STRL("PIPE_READ")-1, 3 TSRMLS_CC);
    zend_declare_class_constant_long(swoole_server_class_entry_ptr, SW_STRL("PIPE_WRITE")-1, 4 TSRMLS_CC);
}

...

----swoole_server.c----

//函数定义
static PHP_METHOD(swoole_server, __construct)
{
    ...
    return;
}

static PHP_METHOD(swoole_server, __destruct)
{
	...
}

static PHP_METHOD(swoole_server, set)
{
	...
}

获取对象句柄

zend_object_handle Z_OBJ_HANDLE_P(zval *object) 函数用于获取对象 object 的句柄,相当于对象的一个编号,我们通常用该编号将一些我们自己应用程序的内核信息与对象绑定到一起,例如下面示例,通过 swoole_get_object 方法获取对象句柄,然后获取句柄绑定的服务器配置信息 swServer 指针,注意php5 和 php7 获取句柄的不同。

示例:

----php_swoole.h----

#define SWOOLE_PROPERTY_MAX     32

typedef struct
{
    void **array;
    uint32_t size;
    void **property[SWOOLE_PROPERTY_MAX];
    uint32_t property_size[SWOOLE_PROPERTY_MAX];
} swoole_object_array;

swoole_object_array swoole_objects;

static sw_inline void* swoole_get_object(zval *object)
{
#if PHP_MAJOR_VERSION < 7
    zend_object_handle handle = Z_OBJ_HANDLE_P(object);
#else
    int handle = (int)Z_OBJ_HANDLE(*object);
#endif
    return swoole_objects.array[handle];
}


----swoole_server.c----

PHP_METHOD(swoole_server, set)
{
    zval *zset = NULL;
    zval *zobject = getThis();
    HashTable *vht;
    zval *v;
    
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zset) == FAILURE)
    {
        return;
    }
	
    //获取对象挂载的服务器配置信息 swServer
    swServer *serv = swoole_get_object(zobject);

    vht = Z_ARRVAL_P(zset);
    
    if (php_swoole_array_get_value(vht, "reactor_num", v))
    {
        convert_to_long(v);
        serv->reactor_num = (int) Z_LVAL_P(v);
        if (serv->reactor_num <= 0)
        {
            serv->reactor_num = SwooleG.cpu_num;
        }
    }
    
    ...
}

 

修改类对象/静态成员变量的值

//成员变量修改
void zend_update_property(zend_class_entry *scope, zval *object, char *name, int name_length, zval *value TSRMLS_DC);
void zend_update_property_null(zend_class_entry *scope, zval *object, char *name, int name_length TSRMLS_DC);
void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC);
void zend_update_property_long(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC);
void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC);
void zend_update_property_string(zend_class_entry *scope, zval *object, char *name, int name_length, char *value TSRMLS_DC);
void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, char *value, int value_len TSRMLS_DC);

//静态成员变量修改
int zend_update_static_property(zend_class_entry *scope, char *name, int name_length, zval *value TSRMLS_DC);
int zend_update_static_property_null(zend_class_entry *scope,char *name, int name_length TSRMLS_DC);
int zend_update_static_property_bool(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC);
int zend_update_static_property_long(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC);
int zend_update_static_property_double(zend_class_entry *scope, char *name, int name_length, double value TSRMLS_DC);
int zend_update_static_property_string(zend_class_entry *scope, char *name, int name_length, char *value TSRMLS_DC);
int zend_update_static_property_stringl(zend_class_entry *scope, char *name, int name_length, char *value, int value_len TSRMLS_DC);
参数属性
scope类实体,指明修改哪个类的成员变量
object类对象, 静态成员变量是属于整个类共有变量,不属于单个对象, 所以没有 object 参数
name变量名
name_length不包含最后的 NULL 结束符的变量长度
value变量值, 每个函数指定的类型不一样,注意,如果是zval作为成员变量,需要注意zval的内存必须始终分配着
value_len该值指定不包含结尾符NULL的value的长度,只在 zend_update_property_stringl / zend_update_static_property_stringl 函数中需要指定

示例:

static zval* create_object(char * host, int port)
{
    zval * server_object;
    SW_ALLOC_INIT_ZVAL(server_object);  //php5 php7 适配的内存分配
    object_init_ex(server_object, swoole_server_class_entry_ptr);
    
    ...
    
    zend_update_property_string(swoole_server_class_entry_ptr, server_object, ZEND_STRL("host"), host TSRMLS_CC);
    zend_update_property_long(swoole_server_class_entry_ptr, server_object, ZEND_STRL("port"), port TSRMLS_CC);
    zend_update_static_property_long(swoole_server_class_entry_ptr, ZEND_STRL("uniqueId"), 0 TSRMLS_CC);
    zend_update_static_property_bool(swoole_server_class_entry_ptr, ZEND_STRL("checked"), 1 TSRMLS_CC);

    return server_object;
}

 

获取对象自身 this 指针 

zval * getThis();

示例:

static PHP_METHOD(swoole_buffer, __construct)
{
    long size = SW_STRING_BUFFER_DEFAULT; //默认大小

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &size) == FAILURE)
    {
        RETURN_FALSE;
    }

    ...

    zend_update_property_long(swoole_buffer_class_entry_ptr, getThis(), ZEND_STRL("capacity"), size TSRMLS_CC);
    zend_update_property_long(swoole_buffer_class_entry_ptr, getThis(), ZEND_STRL("length"), 0 TSRMLS_CC);
}

 

在扩展内创建对象

int object_init(zval *arg);
int object_init_ex(zval *arg, zend_class_entry *ce);
int object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties TSRMLS_DC);
参数用途
arg需要被初始化的对象的 zval 指针
ce将要被初始化的类的类实体,指明哪个类会被初始化
properties用初始属性代替类的默认属性,

在扩展内创建对象,因为在php中,对象也是一个zval, 所以我们需要首先创建zval, 然后为 zval 分配内存 

最后调用 object_init_ex 函数初始化为对象类型,至于是哪个,由 zend_class_entry 指定,如下, swoole_server_port 对象被初始化了,然后用 zend_update_property_string 和 zend_update_property_long 更新 swoole_server_port对象的,host以及port属性。

示例:

static zval* create_object(char * host, int port)
{
    zval *port_object;
    SW_ALLOC_INIT_ZVAL(port_object);  //php5 php7 适配的内存分配
    object_init_ex(port_object, swoole_server_port_class_entry_ptr);
    
    ...
    
    zend_update_property_string(swoole_server_port_class_entry_ptr, port_object, ZEND_STRL("host"), host TSRMLS_CC);
    zend_update_property_long(swoole_server_port_class_entry_ptr, port_object, ZEND_STRL("port"), port TSRMLS_CC);

    return port_object;
}

 

在扩展中调用对象的方法

void zend_call_method_with_0_params(zval** obj, zend_class_entry *ce, zend_function **fn_proxy, char* function_name, zval ** retval)

void zend_call_method_with_1_params (zval** obj, zend_class_entry *ce, zend_function **fn_proxy, char* function_name, zval ** retval, zval * v1)

void zend_call_method_with_2_params (zval** obj, zend_class_entry *ce, zend_function **fn_proxy, char* function_name, zval ** retval, zval * v1, zval *v2)
参数用途
obj被调用的对象的 zval 指针
ce被调用的类的类实体,指明哪个类被调用了
fn_proxy代理函数
function_name成员方法名,指定该类的哪个成员方法将会被调用
retval返回值,也是一个 zval指针
v1/v2传入参数

在下面示例,服务器端口类 swoole_server_port 的set 方法被调用了。

----php7_wrapper.h----
#if PHP_MAJOR_VERSION < 7 /* PHP Version 5*/
	#define sw_zend_call_method_with_0_params     zend_call_method_with_0_params
	#define sw_zend_call_method_with_1_params     zend_call_method_with_1_params
	#define sw_zend_call_method_with_2_params     zend_call_method_with_2_params
#else /* PHP Version 7 */
	#define sw_zend_call_method_with_0_params(obj, ptr, what, method, retval) \
	    zval __retval;\
	    zend_call_method_with_0_params(*obj, ptr, what, method, &__retval);\
	    if (ZVAL_IS_NULL(&__retval)) *(retval) = NULL;\
	    else *(retval) = &__retval;
	
	#define sw_zend_call_method_with_1_params(obj, ptr, what, method, retval, v1)           \
	    zval __retval;\
	    zend_call_method_with_1_params(*obj, ptr, what, method, &__retval, v1);\
	    if (ZVAL_IS_NULL(&__retval)) *(retval) = NULL;\
	    else *(retval) = &__retval;
	
	#define sw_zend_call_method_with_2_params(obj, ptr, what, method, retval, v1, v2)    \
	    zval __retval;\
	    zend_call_method_with_2_params(*obj, ptr, what, method, &__retval, v1, v2);\
	    if (ZVAL_IS_NULL(&__retval)) *(retval) = NULL;\
	    else *(retval) = &__retval;

#endif /* PHP Version */

----test.c----

PHP_METHOD(call_swoole_server_port_function_set)
{
    zval *port_object = getServerPortObject(); //获取 swoole_server_port 类的对象
	zval *zset; //传入参数
	zval *retval = NULL; //返回值
	....
	
    sw_zend_call_method_with_1_params(&port_object, swoole_server_port_class_entry_ptr, NULL, "set", &retval, zset);

    ...
}

获取对象成员变量值

//更新对象属性
zval *zend_read_property(zend_class_entry *scope, zval *object, char *name, 
                    int name_length, zend_bool silent TSRMLS_DC);
//更新类的静态属性
zval *zend_read_static_property(zend_class_entry *scope, char *name, 
                    int name_length, zend_bool silent TSRMLS_DC);
参数属性
scope类实体,指明修改哪个类的成员变量
object类对象, 静态成员变量是属于整个类共有变量,不属于单个对象, 所以没有 object 参数
name成员变量名
name_length变量名的长度,不包含最后的 NULL 字符
silent设置为非零值时,不会报告“undefined property”错误。 注意:未定义 read_property 处理程序的实例将报告错误,而不管silent参数如何。

示例读取 swoole_server_port 对象的参数设置 setting 的值:

#if PHP_MAJOR_VERSION < 7 /* PHP Version 5*/
	#define sw_zend_read_property                  zend_read_property
#else /* PHP Version 7 */
	static inline zval* sw_zend_read_property(zend_class_entry *class_ptr, zval *obj, char *s, int len, int silent)
	{
	    zval rv;
	    return zend_read_property(class_ptr, obj, s, len, silent, &rv);
	}

#endif /* PHP Version */

//获取对象属性值,如果为空,则初始化为空数组
static inline zval* php_swoole_read_init_property(zend_class_entry *scope, zval *object, const char *p, size_t pl TSRMLS_DC)
{
    zval *property = sw_zend_read_property(scope, object, p, pl, 1 TSRMLS_CC);
    if (property == NULL || ZVAL_IS_NULL(property))
    {
        SW_MAKE_STD_ZVAL(property);
        array_init(property);
        zend_update_property(scope, object, p, pl, property TSRMLS_CC);
        sw_zval_ptr_dtor(&property);
        return sw_zend_read_property(scope, object, p, pl, 1 TSRMLS_CC);
    }
    else
    {
        return property;
    }
}

PHP_METHOD(swoole_server_port, set)
{
    ...
	zval *zsetting = php_swoole_read_init_property(swoole_server_port_class_entry_ptr, getThis(), ZEND_STRL("setting") TSRMLS_CC);
	...
}

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值