Atitit. 类与对象的存储实现
Atitit. 类与对象的存储实现
1. 类的结构和实现1
2. 类的方法属性都是hashtable存储的。2
3. Class的分类 常规类(T_CLASS), 抽象类(T_ABSTRACT T_CLASS)和final类(T_FINAL T_CLASS2
4. 对象的结构3
5. 参考4
1. 类的结构和实现
1.1.1.1. 的结构
首先我们看看类是什么。类是用户定义的一种抽象数据类型,它是现实世界中某些具有共性事物的抽象。 有时我们也可以理解其为对象的类别。类也可以看作是一种复合型的结构,其需要存储多元化的数据, 如属性、方法、以及自身的一些性质等。
类和函数类似,PHP内置及PHP扩展均可以实现自己的内部类,也可以由用户使用PHP代码进行定义。 当然我们在编写代码时通常是自己定义。
这里定义了一个父类ParentClass,一个接口Ifce,一个子类Tipi。子类继承父类ParentClass, 实现接口Ifce,并且有一个静态变量$sa,一个类常量 CA,一个公用方法,一个私有方法和一个公用静态方法。 这些结构在Zend引擎内部是如何实现的?类的方法、成员变量是如何存储的?访问控制,静态成员是如何标记的?
首先,我们看看类的内部存储结构:
01 |
struct _zend_class_entry { |
02 |
char type; // 类型:ZEND_INTERNAL_CLASS / ZEND_USER_CLASS |
03 |
char *name;// 类名称 |
04 |
zend_uint name_length; // 即sizeof(name) - 1 |
05 |
struct _zend_class_entry *parent; // 继承的父类 |
06 |
int refcount; // 引用数 |
07 |
zend_bool constants_updated; |
08 |
09 |
zend_uint ce_flags; // ZEND_ACC_IMPLICIT_ABSTRACT_CLASS: 类存在abstract方法 |
10 |
// ZEND_ACC_EXPLICIT_ABSTRACT_CLASS: 在类名称前加了abstract关键字 |
11 |
// ZEND_ACC_FINAL_CLASS |
12 |
// ZEND_ACC_INTERFACE |
13 |
HashTable function_table; // 方法 |
14 |
HashTable default_properties; // 默认属性 |
15 |
HashTable properties_info; // 属性信息 |
16 |
HashTable default_static_members;// 类本身所具有的静态变量 |
17 |
HashTable *static_members; // type == ZEND_USER_CLASS时,取&default_static_members; |
18 |
// type == ZEND_INTERAL_CLASS时,设为NULL |
19 |
HashTable constants_table; / |
2. 类的方法属性都是hashtable存储的。
常规的成员方法存放在函数结构体的哈希表中, 而魔术方法则单独保存。 如在类定义中的 union _zend_function *constructor; 定义就是类的构造魔术方法, 它是以函数的形式存在于类结构中,并且与常规的方法分隔开来了。在初始化时,这些魔术方法都会被设置为NULL。
在PHP语言中,变量都是保存在哈希表中,称为变量符号表,其中变量名为哈希表的键,变量名对应的容器zval的指针为哈希表中的值。所有全局变量放在一 张主符号表中(也就是数组$GLOBALS对应的哈希表)。PHP语言有个特性,变量在命名时,$变量标识符后不能以数字开头。例如我们在以下代码:
3. Class的分类 常规类(T_CLASS), 抽象类(T_ABSTRACT T_CLASS)和final类(T_FINAL T_CLASS
上面的class_entry_type语法说明在语法分析阶段将类分为三种类型:常规类(T_CLASS), 抽象类(T_ABSTRACT T_CLASS)和final类(T_FINAL T_CLASS )。 他们分别对应的类型在内核中为:
4. 对象的结构
对象在PHP中是使用一种zend_object_value的结构体来存储。
1 |
typedef struct _zend_object_value { |
2 |
zend_object_handle handle; |
3 |
// unsigned int类型,EG(objects_store).object_buckets的索引 |
4 |
zend_object_handlers *handlers; |
5 |
} zend_object_value; |
PHP内核会将所有的对象存放在一个对象列表容器中,这个列表容器是保存在EG(objects_store)里的一个全局变量。 上面的handle字段就是这个列表中object_buckets的索引。当我们需要在PHP中存储对象的时候, PHP内核会根据handle索引从对象列表中获取相对应的对象。而获取的对象有其独立的结构,如下代码所示:
1 |
typedef struct _zend_object { |
2 |
zend_class_entry *ce; |
3 |
HashTable *properties; |
4 |
HashTable *guards; /* protects from __get/__set ... recursion */ |
5 |
} zend_object; |
ce是存储该对象的类结构,properties是一个HashTable,用来存放对象的属性。
在zend_object_value结构体中除了索引字段外还有一个包含对象处理方法的字段:handlers。 它的类型是zend_object_handlers,我们可以在Zend/zend_object_handlers.h文件中找到它的定义。 这是一个包含了多个指针函数的结构体,这些指针函数包括对对象属性的操作,对对象方法的操作,克隆等。 此字段会在对象创建的时候初始化。
4.1.1.1. 对象的创建
在PHP代码中,对象的创建是通过关键字 new 进行的。从此关键字出发,我们遍历词法分析,语法分析和编译成中间代码等过程, 得到其最后执行的函数为 ZEND_NEW_SPEC_HANDLER 。
zend_objects_new函数会初始化对象自身的相关信息,包括对象归属于的类,对象实体的存储索引,对象的相关处理函数。 在这里将对象放入对象池中的函数为zend_objects_store_put。
在将对象放入对象池,返回对象的存放索引后,程序设置对象的处理函数为标准对象处理函数:std_object_handlers。 其位于Zend/zend_object_handles.c文件中。
4.1.1.2. 对象池
这里针对对象,我们引入一个新的概念--对象池。 我们将PHP内核在运行中存储所有对象的列表称之为对象池,即EG(objects_store)。 这个对象池的作用是存储PHP中间代码运行阶段所有生成的对象,这个思想有点类似于我们做数据库表设计时, 当一个实例与另一个实体存在一对多的关系时,将多的那一端对应的实体提取出来存储在一个独立的表一样。 这样做的好处有两个,一个是可以对象复用,另一个是节省内存,特别是在对象很大,并且我们不需要用到对象的所有信息时。 对象池的存储结构为zend_objects_store结构体,如下:
4.1.1.3. 成员变量
从前面的对象结构来看,对象的成员变量存储在properties参数中。并且每个对象都会有一套标准的操作函数, 如果需要获取成员变量,对象最后调用的是read_property,其对应的标准函数为zend_std_read_property; 如果需要设置成员变量,对象最后调用的是write_property,其对应的标准函数zend_std_write_property。 这些函数都是可以定制的,如果有不同的需求,可以通过设置对应的函数指针替换。如在dom扩展中,它的变量的获取函数和设置函数都是定制的。
4.1.1.4. 成员方法
成员方法又包括常规的成员方法和魔术方法。魔术方法在前面的第五小节已经介绍过了,这里就不再赘述。 在对象的标准函数中并没有成员方法的调用函数,默认情况下设置为NULL。在SPL扩展中,有此函数的调用设置,如下代码:
数为 ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER 此函数的调用流程如下:
第一步,处理调用的方法名,获取其值,并做检验处理:如果不是字符串,则报错
第二步,如果第一个操作数是对象,则转第三步,否则报错 Call to a member function t on a non-object
第三步,调用对象的get_method函数获取成员方法
第四步,其它处理,包括静态方法,this变量等。
而get_method函数一般是指标准实现中的get_method函数,其对应的具体函数为Zend/zend_object_handlers.c文件中zend_std_get_method函数。 zend_std_get_method函数的流程如下:
第一步,从zobj->ce->function_table中查找是否存在需要调用的函数,如果不存在,转第二步,如果存在,转第三步
第二步,如果__call函数存在,则调用zend_get_user_call_function函数获取并返回,如果不存在,则返回NULL
第三步,检查方法的访问控制,如果为私有函数,转第四步,否则转第五步
第四步,如果为同一个类或父类和这个方法在同一个作用域范围,则返回此方法,否则判断__call函数是否存在,存在则调用此函数,否则报错
第五步,处理函数重载及访问控制为protected的情况。 转第六步
第六步,返回fbc
在获得了函数的信息后,下面的操作就是执行了,关于函数的执行在前面章节已经介绍过了。
4.1. 为了操作一个对象,我们需要先获取这个对象的实例,
而这有肯定会涉及调用对象的构造方法。首先我们先了解下一个object在PHP内核中到底是如何实现的。
01 |
typedef struct _zend_object_value { |
02 |
zend_object_handle handle; |
03 |
zend_object_handlers *handlers; |
04 |
} zend_object_value; |
05 |
06 |
//此外再回顾一下zval的值value的结构。 |
07 |
typedef union _zvalue_value { |
08 |
long lval; /* long value */ |
09 |
double dval; /* double value */ |
10 |
struct { |
11 |
char *val; |
12 |
int len; |
13 |
} str; |
14 |
HashTable *ht; /* hash table value */ |
15 |
zend_object_value obj; |
16 |
} zvalue_value; |
如果我们有一个zval *tmp,那么tmp->value.obj来访问到最终保存对象实例的zend_object_value结构体,它包含两个成员:
5. 参考
PHP内核探索:类的结构和实现 -- 简明现代魔法.htm
PHP内核探索:对象 -- 简明现代魔法.htm
PHP内核探索:创建对象实例 -- 简明现代魔法.htm
作者:: 绰号:老哇的爪子 ( 全名::Attilax Akbar Al Rapanui 阿提拉克斯 阿克巴 阿尔 拉帕努伊 )
汉字名:艾提拉(艾龙), EMAIL:1466519819@qq.com
转载请注明来源: http://blog.csdn.net/attilax
Atiend
Atitit. 类与对象的存储实现相关推荐
- 【C++基础编程(二)】类和对象(重点)、文件操作
4 类(class)和对象(object) C++面向对象的三大特性:封装.继承.多态 C++认为万事万物皆对象,对象上有其属性和行为 对于一些具有相同性质的对象,我们可以抽象称他们为类 4.1 封装 ...
- 在一个电子商务网站应用中,涉及的实体信息类有很多,比如用户类User和用户地址类Address; 而每一个实体类的对象信息要存储到相应的数据库表中,如userTable和addressTable。
在一个电子商务网站应用中,涉及的实体信息类有很多,比如用户类User和用户地址类Address; 而每一个实体类的对象信息要存储到相应的数据库表中,如userTable和addressTable. ...
- 类和对象在JVM中是如何存储的,竟然有一半人回答不上来!
前言 这篇博客主要来说说类与对象在JVM中是如何存储的,由于JVM是个非常庞大的课题,所以我会把他分成很多章节来细细阐述,具体的数量还没有决定,当然这不重要,重点在于是否可以在文章中学到东西,是否对J ...
- C++_类和对象_对象特性_成员变量占用对象内存_成员函数_静态成员函数_静态变量_都不占用对象内存_他们是分开存储的---C++语言工作笔记048
然后我们来看c++中的,类的对象的大小,类中的成员变量和函数的存储问题. 首先我写个空类 然后在类外写个test01这个函数,然后 在main方法中调用这个test01这个函数
- Python编程基础 第四章 编程练习 请定义一个Circle类,具体要求为:(1)每个Circle类对象可以存储圆心和半径信息;(2)具有设置圆心和半径的方法SetCenter和SetRadius
题目内容: 请定义一个Circle类,具体要求为:(1)每个Circle类对象可以存储圆心和半径信息:(2)具有设置圆心和半径的方法SetCenter和SetRadius:(3)具有计算圆面积的方法G ...
- 类和对象- C++对象模型和this指针- 成员变量和成员函数分开存储
成员变量和成员函数分开存储 在C++中,类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上 空对象占用内存空间 class Person {};void test01() {Pers ...
- C# 类对象数据存储(Object自定义序列化)
获取Object对象的所有成员变量: FieldInfo[] fields = obj.GetType().GetFields(); // obj可以为任意类型对象 获取变量名称: string na ...
- android 类对象的存储,android - 以共享首选项存储和检索类对象
android - 以共享首选项存储和检索类对象 在Android中,我们可以在共享首选项中存储类的对象,并在以后检索该对象吗? 如果有可能怎么办? 如果不可能做到这一点的其他可能性是什么? 我知道序 ...
- Python编程基础 第四章 编程练习 请定义一个Cylinder类,具体要求为:(1)每个Cylinder类对象可以存储一个圆柱体;2)具有用于初始化半径和高的构造方法;(3)具有计算圆柱体体积
题目内容: 请定义一个Cylinder类,具体要求为:(1)每个Cylinder类对象可以存储一个圆柱体(包括半径和高):(2)具有用于初始化半径和高的构造方法:(3)具有计算圆柱体体积的方法GetV ...
- php 对象 内存格式,php类讲解17:PHP 对象的存储与传输(序列化 seria
对象的存储与传输 在实际项目应用中,有些任务在一两个页面是无法完成的,由于变量到脚本执行完毕就释放,我们本页所生成的对象想在其它页面使用时便碰到了麻烦. 如果需要将对象及其方法传递到我们想使用对象的页 ...
最新文章
- 取文字_取一个好听的女孩名字
- 干货 | 第六期课程回顾遗传病基因检测和解读
- 安装显卡驱动后分辨率低的办法
- html怎么显示直线,html怎么用鼠标画出一条直线,鼠标移动时候要能看到线条
- 奥鹏东北大学作业答案计算机网络,东北大学17秋学期《计算机网络》在线作业1参考答案...
- 零基础轻松学python pdf 小码哥_零基础轻松学Python
- 2018-2019-2 20165312《网络攻防技术》Exp7 网络欺诈防范
- java重新打开jframe,Java的; Jframe不重新绘制
- 阿里云轻量服务器使用
- DJ4-3 路由器的工作原理
- Java-坦克大战2
- 基于协同过滤 算法推荐系统图书推荐猜你喜欢
- 顺应数字化转型趋势化解“上云”风险,擎天Enclave保障数据安全
- 小马智行进军前装量产,从自研域控制器入手?
- java jtable 分页_Jtable分页查询完整实现
- XMR新算法RandomX设计原理
- MATLAB最小二乘拟合
- 提取HTML中所有图片地址的正则表达式
- SQlite.GridView
- 我为什么要用电子词典
热门文章
- yolov4训练自己的数据 灰度图像_还在为图像训练数据少发愁吗?那是因为你还不会这几招...
- TransactNamedPipe函数
- perl 的调试和性能测试
- 12、NIO--Path、、Paths、Files
- 【.NET开发之美】使用ComponentOne提高.NET DataMap中的加载速度
- 9、从命令行运行postman脚本及生成测试报告
- Win32编程day14 学习笔记
- PIC16F877A的50HZ正弦波
- Linu下建立svn版本库
- Spring MVC-学习笔记(1)认识spring mvc