php object 对象系统
php object 对象系统
概述
本节内容仅谈论对象系统内容, 对于相关内容并不做更深一步的扩展, 相关扩展的内容会在后续补充
object 对象属于 zval 结构的一种形式
php 将所有执行过程中的 object 放在一个对象池中进行管理 EG(objects_store)
结构
zval 结构 (Zend/zend.h)
struct _zval_struct { // PHP存储变量的结构
zvalue_value value; // 值
zend_uint refcount__gc; // 引用计数
zend_uchar type; // 类型标识, IS_NULL, IS_BOOL, IS_STRING ...
zend_uchar is_ref__gc; //是否为引用
};
value 结构 (Zend/zend.h)
typedef union _zvalue_value { // 变量存储的值结构
// 省略 ...
zend_object_value obj; // 存储对象,参见zend_types.h
} zvalue_value;
zend_object_value 结构 (Zend/zend_types.h)
typedef struct _zend_object_value {
/*
* php 内核会将所有对象存放在一个对象容器中: EG(objects_store).object_buckets
* handle 参数是对象在这个容器中的索引, 无符号整数
*/
zend_object_handle handle;
// 对象属性, 方法的操作函数: Zend/zend_object_handlers.h
const zend_object_handlers *handlers;
} zend_object_value;
执行过程中的全局结构体 zend_executor_globals (Zend/zend_globals.h)
// 运行时的全局数据
struct _zend_executor_globals {
// 省略 ... // 当前符号表
HashTable *active_symbol_table;
// 符号表
HashTable symbol_table;
// 函数表
HashTable *function_table;
// 类表
HashTable *class_table;
// 常量表
HashTable *zend_constants;
zend_class_entry *scope;
zend_class_entry *called_scope; /* Scope of the calling class */
// 对象池
zend_objects_store objects_store; // 省略 ...
};
对象池 zend_objects_store (Zend/zend_objects_API.h)
// 对象池, 存放 php 中间代码运行过程中生成的所有对象
typedef struct _zend_objects_store {
// 对象容器
zend_object_store_bucket *object_buckets;
zend_uint top;
zend_uint size;
int free_list_head;
} zend_objects_store;
对象池的初始化在 request RINIT
// 初始化执行器
void init_executor(TSRMLS_D)
{
// 省略 ... zend_objects_store_init(&EG(objects_store), 1024); // 省略 ...
}
// 对象池的初始化
ZEND_API void zend_objects_store_init(zend_objects_store *objects, zend_uint init_size)
{
objects->object_buckets = (zend_object_store_bucket *) emalloc(init_size * sizeof(zend_object_store_bucket));
objects->top = 1; /* Skip 0 so that handles are true */
objects->size = init_size;
objects->free_list_head = -1;
memset(&objects->object_buckets[0], 0, sizeof(zend_object_store_bucket));
}
对象池的释放在 request RSHUTDOWN
// 释放各变量的析构方法
void shutdown_destructors(TSRMLS_D)
{
// 省略 ... zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); // 省略 ...
}
// 关闭对象池
void shutdown_executor(TSRMLS_D)
{
// 省略 ... zend_objects_store_destroy(&EG(objects_store)); // 省略 ...
}
对象容器 zend_object_store_bucket (Zend/zend_objects_API.h)
/*
* 对象哈希表桶结构
* 解决冲突的哈希算法为链接法
* 哈希冲突解决办法有两种:
* 1. 链接法
* 2. 开放寻址法
*/
typedef struct _zend_object_store_bucket {
zend_bool destructor_called;
zend_bool valid;
zend_uchar apply_count;
union _store_bucket {
struct _store_object {
// 对象数据, zend_object 结构
void *object;
zend_objects_store_dtor_t dtor;
zend_objects_free_object_storage_t free_storage;
zend_objects_store_clone_t clone;
const zend_object_handlers *handlers;
zend_uint refcount;
gc_root_buffer *buffered;
} obj;
struct {
int next;
} free_list;
} bucket;
} zend_object_store_bucket;
向对象池中添加对象, 本处仅从调用点开始分析, 对于怎么走到调用点的部分放到之后的语法分析和词法分析再来补充
各调用点调用 object_init, object_init_ex 等函数最终调用_object_and_properties_init
// 初始化对象
ZEND_API int _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties ZEND_FILE_LINE_DC TSRMLS_DC)
{
zend_object *object;
// 通过类信息初始化对象
if (class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
char *what = (class_type->ce_flags & ZEND_ACC_INTERFACE) ? "interface" :((class_type->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) ? "trait" : "abstract class";
zend_error(E_ERROR, "Cannot instantiate %s %s", what, class_type->name);
} zend_update_class_constants(class_type TSRMLS_CC); Z_TYPE_P(arg) = IS_OBJECT;
if (class_type->create_object == NULL) {
// 调用 zend_objects_new 实例化对象
Z_OBJVAL_P(arg) = zend_objects_new(&object, class_type TSRMLS_CC);
if (properties) {
object->properties = properties;
object->properties_table = NULL;
} else {
object_properties_init(object, class_type);
}
} else {
Z_OBJVAL_P(arg) = class_type->create_object(class_type TSRMLS_CC);
}
return SUCCESS;
}
调用 zend_objects_new 创建对象
// 创建对象, 放入对象池中
// 注意 zend_object 结构
ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_entry *class_type TSRMLS_DC)
{
zend_object_value retval; *object = emalloc(sizeof(zend_object));
(*object)->ce = class_type;
(*object)->properties = NULL;
(*object)->properties_table = NULL;
(*object)->guards = NULL;
// 将 zend_object 结构对象存储到对象池中
retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC);
retval.handlers = &std_object_handlers;
return retval;
}
zend_object 结构, 是从调用点经过对 zval 经过一些处理得到的结构, 然后再将其进一步封装成 zend_object_value, 存入对象池
zend_object 结构
// 最终存储在对象哈希表中的对象结构
typedef struct _zend_object {
// 对象的类信息
zend_class_entry *ce;
// 属性信息
HashTable *properties;
zval **properties_table;
HashTable *guards; /* protects from __get/__set ... recursion */
} zend_object;
调用 zend_objects_store_put 函数, 设置对象的值, 存入对象池中
// 将对象存入对象池
ZEND_API zend_object_handle zend_objects_store_put(void *object, zend_objects_store_dtor_t dtor, zend_objects_free_object_storage_t free_storage, zend_objects_store_clone_t clone TSRMLS_DC)
{
zend_object_handle handle;
struct _store_object *obj; if (EG(objects_store).free_list_head != -1) {
handle = EG(objects_store).free_list_head;
EG(objects_store).free_list_head = EG(objects_store).object_buckets[handle].bucket.free_list.next;
} else {
if (EG(objects_store).top == EG(objects_store).size) {
EG(objects_store).size <<= 1;
EG(objects_store).object_buckets = (zend_object_store_bucket *) erealloc(EG(objects_store).object_buckets, EG(objects_store).size * sizeof(zend_object_store_bucket));
}
handle = EG(objects_store).top++;
}
// 设置对象池中新节点的值
obj = &EG(objects_store).object_buckets[handle].bucket.obj;
EG(objects_store).object_buckets[handle].destructor_called = 0;
EG(objects_store).object_buckets[handle].valid = 1;
EG(objects_store).object_buckets[handle].apply_count = 0; obj->refcount = 1;
GC_OBJ_INIT(obj);
// 将 zend_object 结构的对象存入 object 属性
obj->object = object;
obj->dtor = dtor?dtor:(zend_objects_store_dtor_t)zend_objects_destroy_object;
obj->free_storage = free_storage;
obj->clone = clone;
obj->handlers = NULL; #if ZEND_DEBUG_OBJECTS
fprintf(stderr, "Allocated object id #%d\n", handle);
#endif
return handle;
}
对象方法
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;
zend_object_get_debug_info_t get_debug_info;
zend_object_get_closure_t get_closure;
zend_object_get_gc_t get_gc;
};
还是拿之前强制类型转换为例
// 打印 zval 值
ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_copy)
{
if (Z_TYPE_P(expr)==IS_STRING) {
*use_copy = 0;
return;
}
switch (Z_TYPE_P(expr)) {
case IS_NULL:
Z_STRLEN_P(expr_copy) = 0;
Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC();
break; // 省略 ... case IS_OBJECT:
{
TSRMLS_FETCH();
// 直接转换成字符串, zend_std_cast_object_tostring, 调用 tostring
if (zend_std_cast_object_tostring(expr, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) {
break;
}
// 是否定义了 cast_object 函数
if (Z_OBJ_HANDLER_P(expr, cast_object)) {
zval *val; ALLOC_ZVAL(val);
INIT_PZVAL_COPY(val, expr);
zval_copy_ctor(val);
// 调用转换函数
if (Z_OBJ_HANDLER_P(expr, cast_object)(val, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) {
zval_ptr_dtor(&val);
break;
}
zval_ptr_dtor(&val);
}
// 是否定义了 get 函数
if (!Z_OBJ_HANDLER_P(expr, cast_object) && Z_OBJ_HANDLER_P(expr, get)) {
zval *z = Z_OBJ_HANDLER_P(expr, get)(expr TSRMLS_CC); Z_ADDREF_P(z);
if (Z_TYPE_P(z) != IS_OBJECT) {
zend_make_printable_zval(z, expr_copy, use_copy);
if (*use_copy) {
zval_ptr_dtor(&z);
} else {
ZVAL_ZVAL(expr_copy, z, 0, 1);
*use_copy = 1;
}
return;
}
zval_ptr_dtor(&z);
} // 省略 ...
}
总结
php 内核对于对象的管理是通过对象池 object_store 这么个东西
对象池中的节点并没有直接存 zval 结构, 而是将 zval 包装成 zend_object 结构, 再存入 object_store.object_buckets[handle].bucket.obj.object 中
object_store api (Zend/zend_objects_API.c)
函数 | 作用 | 备注 |
---|---|---|
zend_objects_store_init | 对象池的初始化 | ZEND_API void zend_objects_store_init(zend_objects_store *objects, zend_uint init_size) |
zend_objects_store_destroy | 对象池的销毁 | ZEND_API void zend_objects_store_destroy(zend_objects_store *objects) |
zend_objects_store_call_destructors | 对象池中的对象释放 | ZEND_API void zend_objects_store_call_destructors(zend_objects_store *objects TSRMLS_DC) |
zend_objects_store_mark_destructed | 标记对象池中待销毁的对象 | ZEND_API void zend_objects_store_mark_destructed(zend_objects_store *objects TSRMLS_DC) |
zend_objects_store_free_object_storage | 释放对象池中的所有对象 | ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects TSRMLS_DC) |
zend_objects_store_put | 将对象存储到对象池中 | ZEND_API zend_object_handle zend_objects_store_put(void *object, zend_objects_store_dtor_t dtor, zend_objects_free_object_storage_t free_storage, zend_objects_store_clone_t clone TSRMLS_DC) |
zend_objects_store_get_refcount | 获取对象池中指定 zval 对象的 refcount 值 | ZEND_API zend_uint zend_objects_store_get_refcount(zval *object TSRMLS_DC) |
zend_objects_store_add_ref | 增加对象池中指定 zval 对象的 refcount 值, +1 | ZEND_API void zend_objects_store_add_ref(zval *object TSRMLS_DC) |
zend_objects_store_add_ref_by_handle | 通过索引 handle 增加对象的 refcount, +1 | ZEND_API void zend_objects_store_add_ref_by_handle(zend_object_handle handle TSRMLS_DC) |
zend_objects_store_del_ref | 从对象池中删除指定 zval 的对象的 refcount, -1 | ZEND_API void zend_objects_store_del_ref_by_handle_ex(zend_object_handle handle, const zend_object_handlers *handlers TSRMLS_DC) |
zend_objects_store_clone_obj | 克隆对象 | ZEND_API zend_object_value zend_objects_store_clone_obj(zval *zobject TSRMLS_DC) |
zend_object_store_get_object | 从对象池中获取对象的值 | ZEND_API void *zend_object_store_get_object(const zval *zobject TSRMLS_DC) |
zend_object_store_get_object_by_handle | 通过索引 handle 获取对象 | ZEND_API void *zend_object_store_get_object_by_handle(zend_object_handle handle TSRMLS_DC) |
zend_object_store_ctor_failed | 当创建对象发生异常时调用的函数 | ZEND_API void zend_object_store_ctor_failed(zval *zobject TSRMLS_DC) |
php object 对象系统的更多相关文章
- redis object 对象系统
redis object对象系统 概述 redis 当中, sds字符串, adlist双向链表, dict字典, ziplist压缩链表, intset整数集合等均为底层数据结构 redis 并没有 ...
- Qt Meta Object System-元对象系统
研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 元对象系统 ...
- 从零构建JavaScript的对象系统
一.正统的类与继承 类是对象的定义,而对象是类的实例(Instance).类不可直接使用,要想使用就必须在内存上生成该类的副本,这个副本就是对象. 以Java为例: public class Grou ...
- 深入了解Qt(二)之元对象系统(Meta-Object System)
深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象 ...
- javascript ES5 Object对象
原文:http://javascript.ruanyifeng.com/stdlib/object.html 目录 概述 Object对象的方法 Object() Object.keys(),Obje ...
- javascript中的null,对象系统还是非对象系统?
1.一直以来的认知 在我学习js的过程中,爱民老师的绿皮书里将js的类型系统分成了两类: 其一是元类型系统:由typeof运算来检测 其二是对象类型系统:是元类型的object的一个分支 而null这 ...
- JS基础:基于原型的对象系统
简介: 仅从设计模式的角度讲,如果我们想要创建一个对象,一种方法是先指定它的类型,然后通过这个类来创建对象,例如传统的面向对象编程语言 "C++"."Java" ...
- [Redis] redis的设计与实现-对象系统
1.redis并没有直接使用前面的数据结构实现键值对数据库,而是基于数据结构创建了一个对象系统,字符串对象/列表对象/哈希对象/集合对象/有序集合对象都用到了至少一种前面的数据结构2.针对不同的使用场 ...
- QtCore是Qt的精髓(包括五大模块:元对象系统,属性系统,对象模型,对象树,信号槽)
作者:小豆君的干货铺链接:https://www.zhihu.com/question/27040542/answer/218384474来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
随机推荐
- .Net3月份开发札记
筛选数据 需求:如果数据库中存在OrderNum相同,且IsDefault不同的记录,那么IsDefault值为0的记录将替换值为1的记录(IsDefault值为1的记录不展示). 由于查出来的数据不 ...
- vue组件最佳实践
看了老外的一篇关于组件开发的建议(强烈建议阅读英文原版),感觉不错翻译一下加深理解. 这篇文章制定一个统一的规则来开发你的vue程序,以至于达到一下目的. 1.让开发者和开发团队更容易发现一些事情. ...
- 技术分享,学术报告presentation 常用的承接句
前言 现在即使是搞技术,做科研的,也需要在不同的场合,用ppt来做分享,做汇报,做总结. 如果国际会议,研讨会,或者在外企,国外工作,英文的presentation就更加必不可少.英语的提升需要大家从 ...
- Struts2框架(5)---result结果集
result结果集 上一篇文章主要讲Struts2框架(4)---Action类访问servlet这篇主要讲result结果集 在Struts.xml中的result元素指的是:指定动作类的动作方法执 ...
- intent,实现两个活动之间数据的传递
一.Intent 可以启动一个活动,也可以在启动活动的时候传递数据.intent中提供了putExtra()方法,它可以把我们想要传递的数据暂存在intent中,启动了另一个活动后,通过getInte ...
- HTML5—canvas绘制图形(1)
1.canvas基础知识 canvas元素是HTML5中新增的一个重要的元素,专门用来绘制图形,不过canvas本身不具备画图的能力,在页面中放置了canvas元素,就相当于在页面中放置了一块矩形的“ ...
- aiohttp之添加静态资源路径
所谓静态资源,是指图片.js.css等文件.官方的说明在这里. 以一个小项目来说明,下面是项目的目录结构: . ├── static │ ├── css │ │ ├── base.css │ │ ├─ ...
- linux版powershell安装教程(.net core版)
powershell 传教士 原创文章 始于2016-12-20,2017-03-15改.文章版本目前博客园为最新版. 允许转载,但必须保留名字和出处,否则追究法律责任 问:powershell二进制 ...
- php文件基本操作与文件管理功能
文件的基本操作 先来看一下PHP文件基础操作,请看强大注释 <body> <?php var_dump(filetype("./img/11.png")); // ...
- oracle 11g centos6 安装
选型:32位的内存是个瓶颈,已经是64位的时代了.使用64位的CentOS6 和 64位的Oracle 11g R2在虚拟机器安装,采用hostonly方式设置网络注意:能上网的网卡要设置一下ICS( ...