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来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
随机推荐
- c++文件读取(一)---输入类使用和查找当前程序路径
鉴于公司人员流动大,设计一个小的日志定位器,首先要读取日志文件 用输入输出流类操作文件比c的stdio更安全和容易,当然任何此类观点都是对大牛除外 首先是查找当前文件路径,如果用GetCurrentD ...
- Maven与Eclipse使用中遇到的问题解决之道
在使用Maven以及Eclipse的Maven插件时,我和同事遇到了一下几个问题,本着知其然知其所以然的学习精神,总结如下: Unrecognised tag 问题 由于我使用本地代理仓库,所以set ...
- XMemcached简单使用示例
Memcached的Java客户端目前有三个: Memcached Client for Java 比 SpyMemcached更稳定.更早.更广泛: SpyMemcached 比 Memcached ...
- jQuery测验题
1.在div元素中,包含了一个<span>元素,通过has选择器获取<div>元素中的<span>元素的语法是? 提示使用has() $("div:has ...
- Java面试10|数据库相关
1.ID分配单点问题 系统使用一张表的自增来得到订单号,所有的订单生成必须先在这里insert一条数据,得到订单号.分库后,库的数量变多,相应的故障次数变多,但由于单点的存在,故障影响范围并未相应的减 ...
- Windows定时计划执行Python
先看下定时执行的py文件 #coding:gbk from selenium import webdriver import time,os driver = webdriver.PhantomJS( ...
- css重要属性之辩
一.relative 1)与兄弟 relative对absolute 1.限制left/top/right/bottom定位 2.限制z-index层级: 3.限制在overflow下 relativ ...
- PHP语言开发微信公众平台(订阅号)之开启开发者模式
(1)打开上一篇我们从花生壳官网获得的外网网址就会看到localhost根目录下的文件(这里不再赘述php环境的搭建).注:因为外网网址在能联网时,访问外网网址的任何人都能看到根目录下的所有文件,不仅 ...
- 丑数(USACO)
这个题是一个动态规划加优化的经典题 1246 丑数 USACO 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 对 ...
- Brackets--我的初恋编辑器
大学毕业以后,因为一些个人琐事没有找到自己专业的对口工作,整天混混沌沌得过着也没有打破现状的决心和机会.但是每当独自思考的时候总是一阵阵的害怕,怕自己从此一事无成一无所有.于是在某个心潮澎湃的瞬间开始 ...