[ PHP 内核与扩展开发系列] 类与面向对象:访问对象实例的属性和方法

栏目: PHP · 发布时间: 7年前

内容简介:[ PHP 内核与扩展开发系列] 类与面向对象:访问对象实例的属性和方法

上一章节里,我们看了一下如何在 PHP 扩展里定义类与接口,这一章节我们将学习一下如何在 PHP 扩展中操作类的实例 ——对象。PHP语言中的面向对象其实是分为三个部分来实现的:class、object、refrence。class 就是我们所说的类,可以直观的理解为前面章节中所描述的 zend_class_entry。object 就是类的实际对象。每一个 zval 并不直接包含具体的object,而是通过一个索引(refrence)与其联系。也就是说,每个 class 都有很多个 object 实例,并把他们统一放在一个数组里,每个 zval 只要记住自己相应的 key 就行了。如此一来,我们在传递 zval 时,实际上传递的是一个索引,而不是内存中具体的对象数据。

调用对象方法

为了操作一个对象,我们需要先获取这个对象的实例,而这肯定会涉及调用对象的构造方法。有关如何在扩展中调用 PHP 的函数与对象的方法这里不展开描述了。首先我们先了解下一个 object 在 PHP内核中到底是如何实现的:

typedef struct _zend_object_value {
    zend_object_handle handle;
    zend_object_handlers *handlers;
} zend_object_value;

// 这里我们再回顾一下 zval 的值 value 的结构。
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;

如果我们有一个 zval *tmp ,那么可以通过 tmp->value.obj 来访问到最终保存对象实例的 zend_object_value 结构体,它包含两个成员:

  • zend_object_handle handle :最终实现是一个 unsigned int 值,Zend 会把每个对象放进数组里,这个 handle 就是此实例的索引。所以我们在把对象当作参数传递时,只不过传递的是 handle 罢了,这样对性能有利,同时也是对象的引用机制的原理。
  • zend_object_handlers *handlers :这个里面是一组函数指针,我们可以通过它来对对象进行一些操作,比如:添加引用、获取属性等。此结构体在 Zend/zend_object_handlers.h 里定义。

下面我给出这个类的 PHP 语言实现:

<?php
class baby
{
    public function __construct()
    {
        echo "a new baby!\n";
    }   

    public function hello()
    {
        echo "hello world!\n";
    }
}

function test_call()
{
    $obj = new baby();
    $obj->hello();
}

然后我们在扩展中实现以上 test_call 函数:

zend_class_entry *baby_ce;

int academy_call_user_function(zval** retval, zval* obj, char* function_name, char* paras, ...){        //用于接收参数  
    short int  paras_count = 0;  
    zval***    parameters  = NULL;  
    long       long_tmp;  
    char       *string_tmp;  
    zval       *zval_tmp;  
    double     dou_tmp;  
    int        i;         //仅与调用有关的变量  
    int        fun_re, retval_is_null = 0;  
    HashTable  *function_table;         //接收参数 

    paras_count = strlen(paras);  
    if(paras_count > 0) {  
        parameters = (zval***)emalloc(sizeof(zval**) * paras_count);  
        va_list ap;  
        va_start(ap,paras);  
        for(i=0; i<paras_count; i++) {  
            parameters[i] = (zval**)emalloc(sizeof(zval*));  
            switch(paras[i]) {  
                case 's':  
                    MAKE_STD_ZVAL(*parameters[i]);  
                    string_tmp = va_arg(ap, char*);  
                    long_tmp   = va_arg(ap, long);    
                    ZVAL_STRINGL(*parameters[i], string_tmp, long_tmp, 1);  
                    break;  
                case 'l':    
                    MAKE_STD_ZVAL(*parameters[i]);  
                    long_tmp = va_arg(ap, long);  
                    ZVAL_LONG(*parameters[i], long_tmp);    
                    break;                                 
                case 'd':    
                    MAKE_STD_ZVAL(*parameters[i]);  
                    dou_tmp = va_arg(ap, double);  
                    ZVAL_DOUBLE(*parameters[i], dou_tmp);  
                    break;  
                case 'n':  
                    MAKE_STD_ZVAL(*parameters[i]);  
                    ZVAL_NULL(*parameters[i]);  
                    break;  
                 case 'z':  
                    zval_tmp = va_arg(ap, zval*);  
                    *parameters[i] = zval_tmp;  
                    break;  
                 case 'b':    
                    MAKE_STD_ZVAL(*parameters[i]);    
                    ZVAL_BOOL(*parameters[i], (int)va_arg(ap, int));  
                    break;  
               default:  
                    zend_error(E_ERROR, "Unsupported type:%c in walu_call_user_function", paras[i]);                                        
                    return 0;  
            }  
        }  
        va_end(ap);  
    }   //构造参数执行call_user_function_ex    
    zval *_function_name;  
    MAKE_STD_ZVAL(_function_name);  
    ZVAL_STRINGL(_function_name, function_name, strlen(function_name), 1);  
    if (retval == NULL) {  
        retval_is_null = 1;  
        retval = (zval**)emalloc(sizeof(zval*));  
    }   //开始函数调用  
    if (obj) {  
        function_table = &Z_OBJCE_P(obj)->function_table;  
    } else {  
        function_table = (CG(function_table));  
    }  
    zend_fcall_info fci;  
    fci.size            = sizeof(fci);  
    fci.function_table  = function_table;  
    fci.object_ptr      = obj ? obj : NULL;  
    fci.function_name   = _function_name;  
    fci.retval_ptr_ptr  = retval;  
    fci.param_count     = paras_count;  
    fci.params          = parameters;  
    fci.no_separation   = 1;  
    fci.symbol_table    = NULL;  
    fun_re = zend_call_function(&fci, NULL TSRMLS_CC);  //函数调用结束
    if (retval_is_null == 1) {  
        zval_ptr_dtor(retval);  
        efree(retval);  
    }  
    //free掉parameter及其里面的每个元素zval**,及每个元素zval**对应的zval*        
    //对于传进来的zval,不进行free,由参数调用者自行free  
    zval_ptr_dtor(&_function_name); 
    if(paras_count > 0) {  
        for (i = 0; i < paras_count; i++) {  
            if (paras[i] != 'z') {  
                    zval_ptr_dtor(parameters[i]);  
            }  
            efree(parameters[i]);  
        }  
        efree(parameters);  
    }  
    return fun_re;  
} 

ZEND_FUNCTION(test_call)
{
    zval *obj;
    MAKE_STD_ZVAL(obj);
    object_init_ex(obj, baby_ce);

    //如果确认此类没有构造函数就不用调用了。
    academy_call_user_function(NULL, obj, "__construct", "");

    academy_call_user_function(NULL, obj, "hello", "");
    zval_ptr_dtor(&obj);
    return;
}

ZEND_METHOD(baby, __construct)
{
    printf("a new baby!\n");
}

ZEND_METHOD(baby, hello)
{
    printf("hello world!\n");
}

static zend_function_entry baby_method[]={
    ZEND_ME(baby, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    ZEND_ME(baby, hello, NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};

ZEND_MINIT_FUNCTION(test)
{
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, "baby", baby_method);
    baby_ce = zend_register_internal_class(&ce TSRMLS_CC);
    return SUCCESS;
}

重新编译,执行命令查看是否生效:

php -r "test_call();"

访问对象属性

我们已经看了下如何操作一个对象的方法,下面主要描述与对象属性有关的东西。有关如何对它进行定义的操作我们已经在前面章节描述过了,这里不再叙述,只讲对其的操作。

读取对象的属性

ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, char *name, int name_length, zend_bool silent TSRMLS_DC);

ZEND_API zval *zend_read_static_property(zend_class_entry *scope, char *name, int name_length, zend_bool silent TSRMLS_DC);

zend_read_property 函数用于读取对象的属性,而 zend_read_static_property 则用于读取静态属性。可以看出,静态属性是直接保存在类上的,与具体的对象无关。 silent 参数等于 0 表示如果属性不存在,则抛出一个 notice 错误;等于 1 表示如果属性不存在不报错。

如果所查的属性不存在,那么此函数将返回 IS_NULL 类型的 zval。

更新对象的属性

ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, char *name, int name_length, zval *value TSRMLS_DC); 
ZEND_API int zend_update_static_property(zend_class_entry *scope, char *name, int name_length, zval *value TSRMLS_DC);

zend_update_property 用来更新对象的属性, zend_update_static_property 用来更新类的静态属性。如果对象或者类中没有相关的属性,函数将自动的添加上。

读写对象与类属性的实例

我们先看个 PHP 实现的例子:

class baby
{
    public $age;
    public static $area;

    public function __construct($age, $area)
    {
            $this->age = $age;
            self::$area = $area;

            var_dump($this->age, self::$area);
    }
}

对应在 PHP 扩展中的实现:

ZEND_METHOD(baby, __construct)
{
    zval *age, *area;
    zend_class_entry *ce;
    ce = Z_OBJCE_P(getThis());
    if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &age, &area) == FAILURE )
    {
            printf("Error\n");
            RETURN_NULL();
    }
    zend_update_property(ce, getThis(), "age", sizeof("age")-1, age TSRMLS_CC);
    zend_update_static_property(ce, "area", sizeof("area")-1, area TSRMLS_CC);

    age = NULL;
    area = NULL;

    age = zend_read_property(ce, getThis(), "age", sizeof("age")-1, 0 TSRMLS_DC);
    php_var_dump(&age, 1 TSRMLS_CC);

    area = zend_read_static_property(ce, "area", sizeof("area")-1, 0 TSRMLS_DC);
    php_var_dump(&area, 1 TSRMLS_CC);
}

一些其它的快捷函数

更新对象与类的属性函数大全:

ZEND_API void zend_update_property_null(zend_class_entry *scope, zval *object, char *name, int name_length TSRMLS_DC); 
ZEND_API void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); 
ZEND_API void zend_update_property_long(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); 
ZEND_API void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC); 
ZEND_API void zend_update_property_string(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value TSRMLS_DC); 
ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value, int value_length TSRMLS_DC);
ZEND_API int zend_update_static_property_null(zend_class_entry *scope, char *name, int name_length TSRMLS_DC); 
ZEND_API int zend_update_static_property_bool(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); 
ZEND_API int zend_update_static_property_long(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); 
ZEND_API int zend_update_static_property_double(zend_class_entry *scope, char *name, int name_length, double value TSRMLS_DC); 
ZEND_API int zend_update_static_property_string(zend_class_entry *scope, char *name, int name_length, const char *value TSRMLS_DC); 
ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, char *name, int name_length, const char *value, int value_length TSRMLS_DC);

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Machine Learning

Machine Learning

Kevin Murphy / The MIT Press / 2012-9-18 / USD 90.00

Today's Web-enabled deluge of electronic data calls for automated methods of data analysis. Machine learning provides these, developing methods that can automatically detect patterns in data and then ......一起来看看 《Machine Learning》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器