PHP 类和对象

PHP 中的对象由 zend_object 结构表示,紧接着是纯数组属性(zvals)。对象的第一个字由 zend_refcounted 结构定义并用于引用计数,然后是指向类入口结构的指针,接着是指向对象处理程序表(类似于虚拟方法表)和未声明属性的哈希表的指针。所有已声明的属性都在后面,可以通过偏移量访问。

image 2023 11 14 13 53 33 904

有两个重要的依赖结构—​zend_class_entry 和 zend_object_handlers。第一个结构保存类的所有静态信息,包括类名、父类、方法、常量、属性及其默认值、静态属性值等。在这里,你还可以看到一个回调函数 "create_object",可以通过重载该函数来创建自定义的对象。

typedef struct zend_class_entry {
    char type;
    zend_string *name;
    zend_class_entry *parent;
    int refcount;
    uint32_t ce_flags;
    int default_properties_count;
    int default_static_members_count;
    zval *default_properties_table;
    zval *default_static_members_table;
    zval *static_members_table;
    HashTable function_table;
    HashTable properties_info;
    HashTable constants_table;
    zend_property_info **properties_info_table;
    ...
    zend_object* (*create_object)(zend_class_entry *class_type);
    ...
} zend_class_entry;

第二个表格包含决定对象行为的回调函数。我们可以创建具有自定义行为的对象,覆盖此表,然后更改某些特定函数。这是 PHP 高级内部知识。现在,你只需阅读处理程序名称列表,然后猜测它们要做什么。

typedef struct _zend_object_handlers {
    /* offset of real object header (usually zero) */
    int offset; /* object handlers */
    zend_object_free_obj_t free_obj; /* required */
    zend_object_dtor_obj_t dtor_obj; /* required */
    zend_object_clone_obj_t clone_obj; /* optional */
    zend_object_read_property_t read_property; /* required */
    zend_object_write_property_t write_property; /* required */
    zend_object_read_dimension_t read_dimension; /* required */
    zend_object_write_dimension_t write_dimension; /* required */
    zend_object_get_property_ptr_ptr_t get_property_ptr_ptr; /* required */
    zend_object_get_t get; /* optional */
    zend_object_set_t set; /* optional */
    zend_object_has_property_t has_property; /* required */
    zend_object_unset_property_t unset_property; /* required */
    zend_object_has_dimension_t has_dimension; /* required */
    zend_object_unset_dimension_t unset_dimension; /* required */
    zend_object_get_properties_t get_properties; /* required */
    zend_object_get_method_t get_method; /* required */
    zend_object_call_method_t call_method; /* optional */
    zend_object_get_constructor_t get_constructor; /* required */
    zend_object_get_class_name_t get_class_name; /* required */
    zend_object_compare_t compare_objects; /* optional */
    zend_object_cast_t cast_object; /* optional */
    zend_object_count_elements_t count_elements; /* optional */
    zend_object_get_debug_info_t get_debug_info; /* optional */
    zend_object_get_closure_t get_closure; /* optional */
    zend_object_get_gc_t get_gc; /* required */
    zend_object_do_operation_t do_operation; /* optional */
    zend_object_compare_zvals_t compare; /* optional */
    zend_object_get_properties_for_t get_properties_for; /* optional */
} zend_object_handlers;

处理 PHP 对象和类的 API 很少。它们并不紧凑,不总是一致的,也没有很好的组织。我试图收集最重要的几组。第一组是用于基本内部对象字段的简单获取宏:

  • Z_OBJ(zv) - 返回来自 zval 的 zend_object 结构指针(类型必须是 IS_OBJECT)。

  • Z_OBJCE(zv) - 返回指向给定 PHP 对象 zval 的类入口的指针。

  • Z_OBJ_HANDLE(zv) - 返回给定 PHP 对象 zval 的对象句柄(唯一编号)。

  • Z_OBJ_HT(zv) - 返回给定 PHP 对象 zval 的对象句柄表。

  • Z_OBJ_HANDLER(zv, name) - 从给定的 PHP 对象 zval 的表中返回命名的对象句柄。

  • Z_OBJPROP(zv) - 返回一个包含所有属性(已声明和未声明的)的 HashTable。已声明的属性存储为 IS_INDIRECT 类型的 zvals,可通过 Z_INDIRECT(zv) 宏获取指向真实值的指针。

一些有用的宏和函数,用于处理对象或静态类成员:

  • ZVAL_OBJ(zv, obj) - 使用 IS_OBJECT 类型和给定的 zen_object 初始化 zval。

  • object_init(zv) - 创建一个新的空 stdClass 对象并将其存储在 zv 中。

  • object_init_ex(zv, ce) - 创建一个新的空 stdClass 对象并将其存放在 zv 中。

  • zend_objects_new(ce) - 创建并返回指定类的对象。

  • zend_object_alloc(hdr_size, ce) - 为对象分配一个内存块,该内存块包含给定类中声明的所有属性。要创建普通的 PHP 对象,hdr_size 必须等于 sizeof(zend_object)。不过,也可以申请更多内存,用于保存额外的 C 数据(这将在后面讨论)。

  • zend_object_std_init(obj, ce) - 将给定的 zend_object 字段初始化为给定类的对象。

  • zend_object_release(obj) - 释放对象(递减引用计数器并在其为零时销毁对象)。

  • add_property_…​(zv, name, value) - 一系列函数,用于为给定对象添加不同值的命名属性。

  • zend_read_property(ce, zv, name, len, silent, ret) - 读取静态属性的值,并返回指向 zval 的指针或 NULL。"ret "是存放属性值的 zval 地址(但不是必须的)。

  • zend_read_static_property(ce, name, len, silent) - 读取静态属性的值,并返回指向 zval 的指针或 NULL。

  • zend_update_property…​(ce, zv, name, len, value) - 一系列为对象属性赋值的函数。

  • zend_update_static_property…​(ce, name, len, value) - 一系列为静态属性赋值的函数。

下面是一些用于声明内部类及其元素的函数:

  • INIT_CLASS_ENTRYce, name, functions) - 用给定的名称和方法列表初始化类入口结构。

  • zend_register_internal_class(ce) - 在全局类表中注册给定的类条目,并返回实际类条目地址。

  • zend_register_internal_class_ex(ce, parent_ce) - 类似于 zend_register_internal_class(),但也从给定的 "parent_ce "执行继承。

  • zend_register_internal_interface(ce) - 类似于 zend_register_internal_class(ce),但会注册接口。

  • zend_class_implements(ce, num_interfaces, …​)-创建实现指定接口的类。

  • zend_declare_class_constant_…​(ce, name, len, value) - 一系列用于声明不同值类型的类常量的函数。

  • zend_declare_property_…​(ce, name, name, len, flags, value) - 一系列用于声明具有不同默认值类型的属性的函数。flags" 参数可用于声明 public、protected、private 和 static 属性。