Atitit. 类与对象的存储实现

时间:2021-12-21 23:47:09

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://www.cnblogs.com/attilax/

Atiend

Atitit. 类与对象的存储实现的更多相关文章

  1. 浅谈js的类数组对象arguments

    类数组对象:arguments总所周知,js是一门相当灵活的语言.当我们在js中在调用一个函数的时候,我们经常会给这个函数传递一些参数,js把传入到这个函数的全部参数存储在一个叫做arguments的 ...

  2. atitit.编程语言 类与对象的 扩展机制.doc

    atitit.编程语言 类与对象的 扩展机制.doc 1.1. Java 下一代: 没有继承性的扩展1 1.2. 继承1 1.3. 使用cglib动态为Java类添加方法1 1.4. 工具类 1 1. ...

  3. [C++]变量存储类别,指针和引用,类与对象,继承与派生的一些摘要

    C++*有四种存储类别标识符:auto/static/register/extern 1.auto 函数或分程序内定义的变量(包括形参)可以定义为auto(自动变量).如果不指定存储类别,则隐式定义 ...

  4. 类和对象在JVM中是如何存储的,竟然有一半人回答不上来!

    前言 这篇博客主要来说说类与对象在JVM中是如何存储的,由于JVM是个非常庞大的课题,所以我会把他分成很多章节来细细阐述,具体的数量还没有决定,当然这不重要,重点在于是否可以在文章中学到东西,是否对J ...

  5. 【Python】类和对象、继承、使用文件、存储、异常、标准库(不懂)

    当你调用这个对象的方法MyObject.method(arg1, arg2)的时候,这会由Python自动转为MyClass.method(MyObject, arg1, arg2)——这就是self ...

  6. Atitit.数据库表的物理存储结构原理与架构设计与实践

    Atitit.数据库表的物理存储结构原理与架构设计与实践 1. Oracle和DB2数据库的存储模型如图: 1 1.1. 2. 表数据在块中的存储以及RowId信息3 2. 数据表的物理存储结构 自然 ...

  7. 简述JavaScript对象、数组对象与类数组对象

    问题引出 在上图给出的文档中,用JavaScript获取那个a标签,要用什么办法呢?相信第一反应一定是使用document.getElementsByTagName('a')[0]来获取.同样的,在使 ...

  8. Objective-C Runtime 运行时之一:类与对象

    Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一 ...

  9. [Java入门笔记] 面向对象编程基础(一):类和对象

    什么是面向对象编程? 我们先来看看几个概念: 面向过程程序设计 面向过程,是根据事情发展的步骤,按进行的顺序过程划分,面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实 ...

随机推荐

  1. Android实现圆形图片

     情景再现: 写Android程序也有一段时间了,今天突然被问怎么实现一个圆形图片,很多app图像是圆形的.但是用户上传的图像可不是圆的,所以问题就来了,需要我们代码实现圆形图片.但是大脑飞转想到第三 ...

  2. iOS模拟器多个虚拟机怎么处理

    1:关闭Xcode和模拟器 2:$sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService 等待输入密码 3:$rm -f ~/Libr ...

  3. EF相关知识

    一.EF控制台命令: 1.enable-migrations -EnableAutomaticMigration -Force:使用自动迁移 2.add-migration "Country ...

  4. Visual Studio图片注释image-comments扩展

            有一个开源的Visual Studio小工具image-comments,它用于在源代码注释中插入图片,您可以到这儿下载.目前支持Visual Studio 2010/2012 Sta ...

  5. PO > Create PO时关于汇率问题需要注意的步骤

      为了使得RMB采购的PO在审核时不会提示汇率丢失(如下图),在创建PO时需要注意几个步骤.     1)手动创建PO:在建立PO行之前,应该选择好正确的"地点","币 ...

  6. Java中Iterator(迭代器)的用法及其背后机制的探究

    在Java中遍历List时会用到Java提供的Iterator,Iterator十分好用,原因是: 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结 ...

  7. 这些 Drawable 的小技巧,你都了解吗?

    一.前言 在 Android 的开发过程中,Drawable 经常会被用到,一般会用 Drawable 为 View 设置一个显示的效果.而在 Android 下,也提供了很多 Drawable 的默 ...

  8. caffe学习笔记1

    博客 http://blog.csdn.net/seven_first/article/details/47378697 https://zhuanlan.zhihu.com/p/25127756?r ...

  9. CentOS 7 下 RabbitMQ 集群搭建

    环境 10.0.0.20 node1 10.0.0.21 node2 10.0.0.22 node3 搭建(在所有节点执行) 添加EPEL源 [root@node1 ~]# rpm -Uvh http ...

  10. bzoj1566: [NOI2009]管道取珠 DP

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=1566 思路 n个球,第i个球颜色为ai,对于颜色j,对答案的贡献为颜色为j的球的个数的平 ...