深入理解PHP内核(十三)类的结构和实现
原文链接:http://www.orlion.ga/1117/
先看一下类的结构:
struct _zend_class_entry {
char type; // 类型:ZEND_INTERNAL_CLASS / ZEND_USER_CLASS
char *name;// 类名称
zend_uint name_length; // 即sizeof(name) - 1
structͺ_zend_class_entry *parent; // 继承的父类
intͺrefcount; // 引用数
zend_bool constants_updated;
zend_uint ce_flags; // ZEND_ACC_IMPLICIT_ABSTRACT_CLASS: 类存在abstract方法
// ZEND_ACC_EXPLICIT_ABSTRACT_CLASS: 在类名称前加abstract关键字
// ZEND_ACC_FINAL_CLASS
// ZEND_ACC_INTERFACE
HashTable function_table; // 方法
HashTable default_properties; // 默认属性
HashTable properties_info; // 属性信息
HashTable default_static_members;// 类本身所具有的静态变量
HashTable *static_members; // type == ZEND_USER_CLASS时,取&default_static_members;
// type == ZEND_INTERAL_CLASS时,设NULL
HashTable constants_table; // 常量
struct _zend_function_entry *builtin_functions;// 方法定义入口
union _zend_function *constructor;
union _zend_function *destructor;
union _zend_function *clone;
/* 魔术方法 */
union _zend_function *__get;
union _zend_function *__set;
union _zend_function *__unset;
union _zend_function *__isset;
union _zend_function *__call;
union _zend_function *__tostring;
union _zend_function *serialize_func;
union _zend_function *unserialize_func;
zend_class_iterator_funcs iterator_funcs;// 迭代
/* 类句柄 */
zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC);
zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object,
intby_ref TSRMLS_DC);
/* 类声明的接口 */
int(*interface_gets_implemented)(zend_class_entry *iface,
zend_class_entry *class_type TSRMLS_DC);
/* 序列化回调函数指针 */
int(*serialize)(zval *object unsignedchar**buffer, zend_uint *buf_len,
zend_serialize_data *data TSRMLS_DC);
int(*unserialize)(zval **object, zend_class_entry *ce,
constunsignedchar*buf,
zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
zend_class_entry **interfaces; // 类实现的接口
zend_uint num_interfaces; // 类实现的接口数
char *filename; // 类的存放文件地址 绝对地址
zend_uint line_start; // 类定义的开始行
zend_uint line_end; // 类定义的结束行
char *doc_comment;
zend_uint doc_comment_len;
struct _zend_module_entry *module; // 类所在的模块入口EG(current_module)
};
类的结构中,type有两种类型,数字标记为1和2。分别为宏定义,分别是内置的类和用户自定义的类
#define ZEND_INTERNAL_CLASS 1
#define ZEND_USER_CLASS 2
父类和接口都是存在struct _zend_class_entry中,即接口也是已类的形式存在,类的常规成员方法放在函数结构体哈希表中,而魔术方法单独存放。如在类定义中的 union _zend_function *constructor;定义就是类的构造魔术方法,它是以函数的形式存在类结构中,初始化时这些魔术方法都会被设置为NULL。
类的实现
类的定义是以class关键字开始,在Zend/zend_language_scanner.l文件中,找到class对应的token为T_CLASS。根据此token,在Zend/zend_language_parser.y文件中,找到编译时调用的函数:
unticked_class_declaration_statement:
class_entry_type T_STRING extends_from
{ zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
implements_list
'{'
class_statement_list
'}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
| interface_entry T_STRING
{ zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); }
interface_extends_list
'{'
class_statement_list
'}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
;
class_entry_type:
T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = 0;
}
| T_ABSTRACT T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type =
ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
| T_FINAL T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type =
ZEND_ACC_FINAL_CLASS; }
;
上面的class_entry_type语法说明在语法分析阶段将类分为三种类型:常规类(T_CLASS),抽象类(T_ABSTRACT T_CLASS)和final类(T_FINAL T_CLASS)。他们分别对应的类型在内核中为:
常规类(T_CLASS)对应的type=0
抽象类(T_ABSTRACT T_CLASS)对应type=ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
final类(T_FINAL T_CLASS)对应type=ZEND_ACC_FINAL_CLASS
除了上面三种类型外,类还有另外两种类型没有加abstract关键字的抽象类和接口:
没有加abstract关键字的抽象类,它对应的type=ZEND_ACC_IMPLICIT_ABSTRACT_CLASS。由于没在class前面没有abstract关键字,在语法分析时并没有分析出来这是一个抽象类,但是由于类中没有抽象方法,在函数注册时判断成员函数是抽象方法或继承类中的成员方法是抽象方法,会将这个类设置为此种抽象类型。
接口,其type=ZEND_ACC_INTERFACE。接口类型的区分是在interface关键字解析时设置。
这五种类型在Zend/zend_complie.h文件中定义如下:
#define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20
#define ZEND_ACC_FINAL_CLASS 0x40
#define ZEND_ACC_INTERFACE 0x80
常规类为0。语法解析完后就知道一个类是抽象类还是final类、普通的类、接口。定义类时调用了zend_do_begin_class_declaration和zend_do_end_class_declaration函数,从这两个函数传入的参数,zend_do_begin_class_declaration函数用来处理类名,类的类别和父类,zend_do_end_class_declaration函数用来处理接口和类的中间代码,这两个函数在Zend/zend_complie.c文件中可以找到其实现。
在zend_do_begin_class_declaration中,首先会对传入的类名做一个转化,统一成小写(这也是类名不区分大小写的原因)。
类名重复(如定义了两个Person类)错误的判断过程在中间代码生成时,关于类名的判断是通过T_STRING token,在语法解析时做的判断,但是这只能识别出类名是一个字符串。假如类名为一些关键字如声明class self会报错,这个错误的判断定义在zend_do_begin_class_declaration函数,与self关键字一样,还有parent,static两个关键字的判断在同一个地方。当这个函数执行完后,我们会得到类声明生成的中间代码为ZEND_DECLARE_CLASS。如果我们声明内部类的话,生成的中间代码为:ZEND_DECLARE_INHERITED_CLASS。
根据生成的中间代码,我们在Zend/zend_vm_execute.h文件中找到其对应的执行函数ZEND_DECLARE_CLASS_SPEC_HANDLER。这个函数通过调用do_bind_class函数将此类加入到EG(class_table)。在添加到列表的同时,也判断该类是否存在,如果存在,则添加失败,报错,只是这个判断在编译开启时是不会生效的。
类相关的各个结构均保存在struct _zend_class_entry结构体中,这些具体的类别在语法分析过程中进行区分。识别出类的类别,类的类名等,并将识别出来的结果存放到类的结构中。
深入理解PHP内核(十三)类的结构和实现的更多相关文章
- 【Swift学习】Swift编程之旅---类和结构体(十三)
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件.你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口. 注意:通常一个类 ...
- 站在OC的基础上快速理解Swift的类与结构体
阅读此文章前,您已经有一定的Object-C语法基础了!) 2014年,Apple推出了Swift,最近开始应用到实际的项目中. 首先我发现在编写Swift代码的时候,经常会遇到Xcode不能提示,卡 ...
- 深入理解PHP内核(九)变量及数据类型-静态变量
原文链接:http://www.orlion.ga/251/ 通常静态变量是静态分配的,他们的生命周期和程序的生命周期一样长,只有在程序退出后才结束生命周期,这和局部变量相反,有的语言中全局变量也是静 ...
- java之jvm学习笔记十三(jvm基本结构)
java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...
- 深入理解php内核 编写扩展 I:介绍PHP和Zend
内容: 编写扩展I - PHP和Zend起步 原文:http://devzone.zend.com/public/view/tag/Extension Part I: Introduction to ...
- 《深入理解Java虚拟机》类文件结构
上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...
- swift学习笔记3——类、结构体、枚举
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- 【JavaScript】获取未知类的结构
目录结构: // contents structure [-] 为什么需要获取类的结构 关于JavaScript中的类 定义类的方法 第一种 第二种 DEMO HTML页面 date文件 注意事项 参 ...
- 深入理解PHP内核(二)概览-PHP生命周期与Zend引擎
本文参考自<深入理解PHP内核>,地址:https://github.com/reeze/tipi 本文链接:http://www.orlion.ml/232/ 1.SAPI接口 SAPI ...
随机推荐
- 【转载】Bandits for Recommendation Systems (Part I)
[原文链接:http://engineering.richrelevance.com/bandits-recommendation-systems/.] [本文链接:http://www.cnblog ...
- nil、Nil、NULL和NSNull的理解
http://blog.sina.com.cn/s/blog_4930f8e60101h71b.html 其实早就想研究一下nil.Nil.NULL和NSNull之间的区别,只是工作上除了nil,其它 ...
- asp.net导出dbf报错“未在本地计算机上注册“VFPOLEDB”提供程序。”
导出dbf文件报错,提示“未在本地计算机上注册“VFPOLEDB”提供程序.” 可以尝试一下方法: 方法一:下载VFPOLEDBSetup.msi 安装 如果方法一不行:继续方法二:下载vfp9.0 ...
- Webstorm 2016内置web服务器配置
运行three.js的官方的例子.本来想用IIS来运行,运行不了.所以用webstorm,用鼠标右键的方式,来运行,如下图 但是有一天,我把IIS配置好了,可以在IIS中运行了(只是把build文件夹 ...
- .net笔记
一.垃圾回收 1.运行.NET应用程序时,程序创建出来的对象都会被CLR跟踪, 2.哪些对象还会被用到(存在引用关系):哪些对象不会再被用到(不存在引用关系),CLR都是有记录的. 3.CLR会整理不 ...
- Understanding ASP.NET MVC Filters and Attributes
这篇文章把Asp.net MVC的filter介绍的很详细,值得收藏. http://www.dotnet-tricks.com/Tutorial/mvc/b11a280114-Understandi ...
- 【腾讯Bugly干货分享】iOS10 SiriKit QQ适配详解
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ece0331288fb4d31137da6 1. 概述 苹果在iOS10开放 ...
- Linux文件系统应用---系统数据备份和迁移(用户角度)
1 前言 首先承诺:对于从Windows系统迁移过来的用户,困扰大家的 “Linux系统下是否可以把系统文件和用户文件分开到C盘和D盘中” 的问题也可以得到完满解决. 之前的文章对Linux的文 ...
- [.net 面向对象编程基础] (9) 类和类的实例
[.net 面向对象编程基础] (9) 类和类的实例 类 ,顾名思义就是分类.类别的意思.我们要面向对象编程,就需要对不同的事物进行分类.类可以说是.net面向对象的核心. 类:就是具有相同的属性和功 ...
- 使用ACE_Get_Opt解析命令行
当我们用C++开发一些C++控制台小工具时,会需要一些用户输入的参数来决定程序如何工作和执行,而用户输入参数的方式大部分都是采用命令行参数的方式. 比如上一篇文章 玩转Windows服务系列--命令行 ...