原文链接: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内核(十三)类的结构和实现的更多相关文章

  1. 【Swift学习】Swift编程之旅---类和结构体(十三)

    与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件.你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口. 注意:通常一个类 ...

  2. 站在OC的基础上快速理解Swift的类与结构体

    阅读此文章前,您已经有一定的Object-C语法基础了!) 2014年,Apple推出了Swift,最近开始应用到实际的项目中. 首先我发现在编写Swift代码的时候,经常会遇到Xcode不能提示,卡 ...

  3. 深入理解PHP内核(九)变量及数据类型-静态变量

    原文链接:http://www.orlion.ga/251/ 通常静态变量是静态分配的,他们的生命周期和程序的生命周期一样长,只有在程序退出后才结束生命周期,这和局部变量相反,有的语言中全局变量也是静 ...

  4. java之jvm学习笔记十三(jvm基本结构)

    java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...

  5. 深入理解php内核 编写扩展 I:介绍PHP和Zend

    内容: 编写扩展I -  PHP和Zend起步 原文:http://devzone.zend.com/public/view/tag/Extension Part I: Introduction to ...

  6. 《深入理解Java虚拟机》类文件结构

    上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...

  7. swift学习笔记3——类、结构体、枚举

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  8. 【JavaScript】获取未知类的结构

    目录结构: // contents structure [-] 为什么需要获取类的结构 关于JavaScript中的类 定义类的方法 第一种 第二种 DEMO HTML页面 date文件 注意事项 参 ...

  9. 深入理解PHP内核(二)概览-PHP生命周期与Zend引擎

    本文参考自<深入理解PHP内核>,地址:https://github.com/reeze/tipi 本文链接:http://www.orlion.ml/232/ 1.SAPI接口 SAPI ...

随机推荐

  1. oracle 读书笔记

    1 动态sql即拼接字符串的sql,使用变量代替具体值,10万条语句可以被hash陈一个SQL_ID,可以只解析一次 for i in 1..100000 loop execute immediate ...

  2. UDP发送中文

    procedure TForm1.SpeedButton1Click(Sender: TObject); begin udp.Send('localhost', 1234, 'abc123'); // ...

  3. ecshop数据表说明

    ecshop数据表说明 数据库结构说明,以及自己在后台备份不需要备份的表(红色字体是不需要备份的表)备份文件在FTP或者服务器上网站根目录下data\sqldata下 数据库采用mysql,共78张表 ...

  4. oracle表空间不足时的处理方法

    由于数据文件路径下的空间不足或表空间不足时,需要更换或扩展或新增表空间时,以下简单介绍下几种处理方式(数据文件/opt/oracle/oradata/testdb.dbf,原大小为100M) 一.扩大 ...

  5. java 内存模型

    翻译自wiki百科:https://en.wikipedia.org/wiki/Java_memory_model 没找到直接在wiki上编辑中文的页面,我就在这翻译下,自己学习用. java内存模型 ...

  6. .net 第二周学习

    这周更进一步的介绍了.net,通过作业的练习,有那么一点点的成就感,相对于前端,成就感还不是很大,但是我还是会继续加油  学习.net,看着他们周末只能呆在寝室写网页,顿时我就高兴了:        ...

  7. Android 自定义View 三板斧之三——重写View来实现全新控件

    通常情况下,Android实现自定义控件无非三种方式. Ⅰ.继承现有控件,对其控件的功能进行拓展. Ⅱ.将现有控件进行组合,实现功能更加强大控件. Ⅲ.重写View实现全新的控件 本文来讨论最难的一种 ...

  8. CoinPunk项目介绍

           CoinPunk是一个bitcoin比特币钱夹服务web应用程序,你可以自己构建钱夹服务.开源,免费. 轻量级,高效 响应式设计 轻易创建新账户 详细的交易记录 构建于Node.js与H ...

  9. EF(Linq)框架使用过程中的小技巧汇总

    这篇博客总结本人在实际项目中遇到的一些关于EF或者Linq的问题,作为以后复习的笔记或者供后来人参考(遇到问题便更新). 目录 技巧1: DbFunctions.TruncateTime()的使用 技 ...

  10. 可在广域网部署运行的QQ高仿版 -- GG叽叽V3.7,优化视频聊天、控制更多相关细节

    在广域网中,由于网络的结构纷繁复杂.而且其实时状况又是千变万化的,所以,要使广域网中的视频聊天达到一个令人满意的效果,存在诸多挑战.这次发布的GG 3.7版本尝试在这一方向上做一些努力,据我自己测试, ...