深入理解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 ...
随机推荐
- Thread and ThreadPool
C#中Thread与ThreadPool的比较 Thread类,一次使用一个线程,来创建和删除线程.这种方式建立和删除线程是很昂贵的(cpu密集型). Threadpool类 对于大多数的情况下是使用 ...
- GetRelevantAnimTimeRemainingFraction节点Bug
初始设置: 动画状态机: MoveStop->Idle的条件: 使用该节点的目的: 在动画蓝图的过渡条件中使用该节点,保证上一个状态的动作完成后进入下一个动作.需求是MoveStop动作完成后才 ...
- Android中自定义checkbox样式
1.首先在drawable文件夹中添加drawable文件checkbox_style.xml.
- 导入aar文件出错
错误日志: > Could not resolve all dependencies for configuration ':app:_debugCompile'. > Could not ...
- QQ分组显示列表ExpandableListView组件应用源码
ExpandableListView又称为可扩展的ListView组件,他和ListView组件很相似 不过每行的显示有两个xml文件,一个xml文件用于定义分组列表的显示风格, 还有一个xml文件用 ...
- 配置tomcat编码格式
tomcat默认编码格式为“ISO-8859-1”,打开tomcat的“server.xml”文件,找到下面行并修改 <Connector connectionTimeout="200 ...
- SQL入门经典(八) 之存储过程
存储过程(stored procedure)有时候称为sproc,它是真正的脚本-或者更准确的说,他是批处理(batch)-它存储于数据库中,而不是淡出的文件中.无论如何,这个比较并不是很确定.存储过 ...
- Java多线程4:synchronized锁机制
脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过 ...
- nodejs获取客户端IP Address
在网上看见很多问node.js如何获取客户端IP,所以记录下来,以供大家参考. function getClientIp(req) { return req.headers['x-forwarded- ...
- FusionCharts简单教程(五)-----FusionCharts中的那些特殊符号
在FusionCharts中有些特殊字符,我们需要进行编码操作才能够使用,否则就无法正常显示. 欧元符号 在FusionCharts里显示"€",你需要用 ...