对象的实现

对象是类的实例,上面讲了类的源码实现,接下来讲对象的源码实现,并介绍和类相关的普通属性。

实现

先来看看对象的存储结构:

struct _zend_object {
    zend_refcounted_h gc;
    uint32_t          handle;
    zend_class_entry *ce;
    const zend_object_handlers *handlers;
    HashTable        *properties;
    zval               properties_table[1];
};

struct _zend_object_handlers {
    /* offset of real object header (usually zero) */
    int                                       offset;
    /* general object functions */
    zend_object_free_obj_t                   free_obj;
    zend_object_dtor_obj_t                   dtor_obj;
    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_name_t             get_class_name;
    zend_object_compare_t                    compare_objects;
    zend_object_cast_t                       cast_object;
    zend_object_count_elements_t             count_elements;
    zend_object_get_debug_info_t             get_debug_info;
    zend_object_get_closure_t                get_closure;
    zend_object_get_gc_t                     get_gc;
    zend_object_do_operation_t               do_operation;
    zend_object_compare_zvals_t              compare;
};

结构体 struct _zend_object 各个字段的说明如下。

  • gcgc 头部(详见第 3 章)。

  • handle:每生成一个结构体 zend_object,会将其首地址存储在全局变量 executor_globals.objects_store.object_buckets 中,而 handle 即为此结构体在此全局变量中的索引。

  • ce:所属的类结构体指针。

  • handlers:初始化时,默认指向全局变量 std_object_handlers,存储着包括操作对象属性等的多个指针函数。

  • properties:HashTable 结构,存储对象的动态普通属性值。

  • properties_table:柔性数组,存储对象的普通属性值。在初始化时创建,数组大小为对象所属类的默认普通属性个数 default_properties_count+1

接下来,创建一个对象 Php7,其生成的数据结构示意图如图6-4所示。

$obj = new Php7();
image 2024 06 09 16 05 24 102
Figure 1. 图6-4 对象的结构

普通属性

普通属性也存储在对象中。当查找对象的普通属性时,在其所属的类的变量 properties_info 中,根据属性名 key,找到 value(类型为 zend_property_info 结构体),判断属性后,结构体的字段 offset 即为要查找到的属性值在对象的 properties_table 中的地址偏移量,如图6-5所示。

看上去顺利地解决了普通属性的存储问题,其实不然。请看下段代码:

$php = new Php7();
$php->filename = "beatles.php";

基于 PHP 的特性,对象中还存在一种动态普通属性。当然了,由于动态普通属性没有权限问题,也不需要在对象创建时初始化,所以比较简单,直接存储到对象的 properties 字段就好了。