热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

原创:PHP内核研究之类的实现

原创:PHP内核研究之类的实现

这几天比较忙哦..没有时间写..周末了多写几篇吧.
目前大部分语言都支持类.
类是什么?类就是面向对象,简称OOP.英文名字 Object Oriented Programming.
面向对象是什么?是一种编程架构.
OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成,OOP达到了软件工程的三个目标:重用性、灵活性和扩展性.
因为我们讲的不是这里只简单描述,如果你还不知道什么是类,什么是面向对象..那么这篇文章目前不适合你哦.

1
2
3
classPerson{
  
};

上面是创建一个PHP类.class是PHP的关键字.通过它我们就能找到Zend是如何创建类的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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; }
;

T_CLASS,T_ABSTRACT T_CLASS和T_FINAL 是PHP的三种类的模式
T_CLASS:是一个标准类.
T_ABSTRACT:是声明一个抽象类
T_FINAL:声明一个不容许继承和扩展的类.
当然还有interface
他们定义在Zend/zend_complie.h的文件中

1
2
3
4
#define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS    0x10    //没有声明为抽象,但是内部有抽象方法
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS    0x20   //抽象
#define ZEND_ACC_FINAL_CLASS                0x40  //Final
#define ZEND_ACC_INTERFACE                  0x80 //接口

这三个规则 记录当前行,并设置类的类型.
在定义类的时候调用了 zend_do_begin_class_declaration和zend_do_end_class_declaration两个方法,
类的关键字 ,类的名称和所继承的父类作为参数传递给这两个函数.
zend_do_begin_class_declaration是用来声明类,设置类型,创建一个
zend_do_end_class_declaration用来处理类中的属性及方法.
在讲到两个函数之前一定先要说说 保存类的结构zend_class_entry
它定义在Zend/zend.h中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct_zend_class_entry {
        chartype;
        char*name;//类名称
        zend_uint name_length;
        struct_zend_class_entry *parent;//所继承的父类
        intrefcount; //引用数
        zend_bool constants_updated;//类的类型
        zend_uint ce_flags;//类的类型 抽象?接口?Final?
        HashTable function_table; //函数表
        HashTable default_properties;//属性
        HashTable properties_info; //函数的访问级别
        HashTable default_static_members;//静态成员
        HashTable *static_members;//静态成员,当是用户声明的类等于default_static_members,内置的类为NULL
        HashTable constants_table;
        conststruct_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 *__callstatic;
        union_zend_function *__tostring;
        union_zend_function *serialize_func;
        union_zend_function *unserialize_func;
  
        zend_class_iterator_funcs iterator_funcs;
  
        /* handlers */
        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);/* a class implements this interface */
        union_zend_function *(*get_static_method)(zend_class_entry *ce,char* method,intmethod_len TSRMLS_DC);
  
        /* serializer callbacks */
        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;
};

清楚了这个结构之后 下面来看看zend_do_begin_class_declaration函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
voidzend_do_begin_class_declaration(constznode *class_token, znode *class_name,constznode *parent_class_name TSRMLS_DC)/* {{{ */
{
        zend_op *opline;
        intdoing_inheritance = 0;
        zend_class_entry *new_class_entry;
        char*lcname;
        interror = 0;
        zval **ns_name;
  
        if(CG(active_class_entry)) {
                zend_error(E_COMPILE_ERROR,"Class declarations may not be nested");
                return;
        }
  
        lcname = zend_str_tolower_dup(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len);
  
        if(!(strcmp(lcname,"self") &&strcmp(lcname,"parent"))) {
                efree(lcname);
                zend_error(E_COMPILE_ERROR,"Cannot use '%s' as class name as it is reserved", class_name->u.constant.value.str.val);
        }
  
        /* Class name must not conflict with import names */
        if(CG(current_import) &&
                        zend_hash_find(CG(current_import), lcname, Z_STRLEN(class_name->u.constant)+1, (void**)&ns_name) == SUCCESS) {
                error = 1;
        }
       if(CG(current_namespace)) {
                /* Prefix class name with name of current namespace */
                znode tmp;
  
                tmp.u.cOnstant= *CG(current_namespace);
                zval_copy_ctor(&tmp.u.constant);
                zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC);
                class_name = &tmp;
                efree(lcname);
                lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));
        }
  
        if(error) {
                char*tmp = zend_str_tolower_dup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name));
  
                if(Z_STRLEN_PP(ns_name) != Z_STRLEN(class_name->u.constant) ||
                        memcmp(tmp, lcname, Z_STRLEN(class_name->u.constant))) {
                        zend_error(E_COMPILE_ERROR,"Cannot declare class %s because the name is already in use", Z_STRVAL(class_name->u.constant));
                }
                efree(tmp);
        }
  
        new_class_entry = emalloc(sizeof(zend_class_entry));
        new_class_entry->type = ZEND_USER_CLASS;
        new_class_entry->name = class_name->u.constant.value.str.val;
        new_class_entry->name_length = class_name->u.constant.value.str.len;
  
        zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC);
        new_class_entry->filename = zend_get_compiled_filename(TSRMLS_C);
        new_class_entry->line_start = class_token->u.opline_num;
        new_class_entry->ce_flags |= class_token->u.EA.type;
if(parent_class_name && parent_class_name->op_type != IS_UNUSED) {
                switch(parent_class_name->u.EA.type) {
                        caseZEND_FETCH_CLASS_SELF:
                                zend_error(E_COMPILE_ERROR,"Cannot use 'self' as class name as it is reserved");
                                break;
                        caseZEND_FETCH_CLASS_PARENT:
                                zend_error(E_COMPILE_ERROR,"Cannot use 'parent' as class name as it is reserved");
                                break;
                        caseZEND_FETCH_CLASS_STATIC:
                                zend_error(E_COMPILE_ERROR,"Cannot use 'static' as class name as it is reserved");
                                break;
                        default:
                                break;
                }
                doing_inheritance = 1;
        }
  
        opline = get_next_op(CG(active_op_array) TSRMLS_CC);
        opline->op1.op_type = IS_CONST;
        build_runtime_defined_function_key(&opline->op1.u.constant, lcname, new_class_entry->name_length TSRMLS_CC);
  
        opline->op2.op_type = IS_CONST;
        opline->op2.u.constant.type = IS_STRING;
        Z_SET_REFCOUNT(opline->op2.u.constant, 1);
  
        if(doing_inheritance) {
                opline->extended_value = parent_class_name->u.var;
                opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
        }else{
                opline->opcode = ZEND_DECLARE_CLASS;
        }
opline->op2.u.constant.value.str.val = lcname;
        opline->op2.u.constant.value.str.len = new_class_entry->name_length;
  
        zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &new_class_entry,sizeof(zend_class_entry *), NULL);
        CG(active_class_entry) = new_class_entry;
  
        opline->result.u.var = get_temporary_variable(CG(active_op_array));
        opline->result.op_type = IS_VAR;
        CG(implementing_class) = opline->result;
  
        if(CG(doc_comment)) {
                CG(active_class_entry)->doc_comment = CG(doc_comment);
                CG(active_class_entry)->doc_comment_len = CG(doc_comment_len);
                CG(doc_comment) = NULL;
                CG(doc_comment_len) = 0;
        }
}

lcname = zend_str_tolower_dup(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len);
把所有类全部转换为小写处理.这就是为什么PHP大小写不敏感的原因.
if (!(strcmp(lcname, “self”) && strcmp(lcname, “parent”))) {
efree(lcname);
zend_error(E_COMPILE_ERROR, “Cannot use ‘%s’ as class name as it is reserved”, class_name->u.constant.value.str.val);
}
类的名字不能是self和parent.
第23-26行 用来检测类名是否重复定义.
第27-37行 用来设置命名空间,这是PHP5.3的新特性
第39-47行 用来抛出重复定义的错误
第49-57行 初始化保存类的结构
zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC);函数是用来初始化结构里面的HashTable,魔术方法.
这个函数里面也有上面提到( HashTable *static_members; //静态成员,当是用户声明的类等于default_static_members,内置的类为NULL)的原因
第58-73行 同样用来检测父类的类名是否包含 保留关键字 self,parent,static
剩下的就是用来生成一个OP,
是内部类:那么生成的OP中间代码就是 ZEND_DECLARE_INHERITED_CLASS
是用户类:OP中间代码就是ZEND_DECLARE_CLASS
在这之后..Zend引擎会调用zend_execute函数执行OP的中间代码ZEND_DECLARE_CLASS_SPEC_HANDLER
它定义在Zend/zend_vm_execute.h中.
这个函数将执行关键代码
EX_T(opline->result.u.var).class_entry = do_bind_class(opline, EG(class_table), 0 TSRMLS_CC) ;
do_bind_class会将此类放到class_table中.当然 ,在这个函数里还会判断该类是否存在.不存在会抛出错误
Internal Zend error – Missing class information for %s
如果存在 则会添加成功
那么到这里类就创建成功了.
下一张节就要深入到 类内部了哦…

 

推荐阅读
  • selenium通过JS语法操作页面元素
    做过web测试的小伙伴们都知道,web元素现在很多是JS写的,那么既然是JS写的,可以通过JS语言去操作页面,来帮助我们操作一些selenium不能覆盖的功能。问题来了我们能否通过 ... [详细]
  • 探讨‘驓’字在新华字典中的发音、笔画结构、常见组合及命名使用建议。 ... [详细]
  • 狮子数学:第六百零五章 阿贝尔定理(第九十九讲)
    本章节深入探讨了阿贝尔定理的核心内容及其在数学分析中的应用,通过具体的例子和详细的证明过程,帮助读者更好地理解和掌握这一重要理论。 ... [详细]
  • Adversarial Personalized Ranking for Recommendation
    目录概主要内容基础对抗扰动对抗训练细节代码HeX.,HeZ.,DuX.andChuaT.Adversarialpersonalizedrankingforrecommendatio ... [详细]
  • 本文详细介绍了PHP中几个常用的数组回调函数,包括array_filter、array_map、array_walk和array_reduce。通过具体的语法、参数说明及示例,帮助开发者更好地理解和使用这些函数。 ... [详细]
  • 本文详细探讨了PHP中使用const和define定义常量的方法及其差异。了解这些区别有助于开发者根据具体需求选择合适的方式定义常量。 ... [详细]
  • 本文介绍了一个来自AIZU ONLINE JUDGE平台的问题,即清洁机器人2.0。该问题来源于某次编程竞赛,涉及复杂的算法逻辑与实现技巧。 ... [详细]
  • 深入解析Java SE、Java EE和Java Web的核心知识体系
    通过一系列图表全面解析Java SE、Java EE与Java Web的核心知识体系,帮助开发者快速掌握这些关键技术领域的要点。 ... [详细]
  • 本文详细探讨了如何根据不同的应用场景选择合适的PHP版本,包括多版本切换技巧、稳定性分析及针对WordPress等特定平台的版本建议。 ... [详细]
  • 本文探讨了如何使用Scrapy框架构建高效的数据采集系统,以及如何通过异步处理技术提升数据存储的效率。同时,文章还介绍了针对不同网站采用的不同采集策略。 ... [详细]
  • 使用CorelDRAW X7轻松绘制卡通风格杯子教程
    本文将引导您通过CorelDRAW X7软件,利用贝塞尔工具和交互式填充功能,创作出一个既可爱又生动的卡通杯子。我们将详细介绍每个步骤,帮助您掌握绘制技巧。 ... [详细]
  • egg实现登录鉴权(七):权限管理
    权限管理包含三部分:访问页面的权限,操作功能的权限和获取数据权限。页面权限:登录用户所属角色的可访问页面的权限功能权限:登录用户所属角色的可访问页面的操作权限数据权限:登录用户所属 ... [详细]
  • 本文介绍了用户界面(User Interface, UI)的基本概念,以及在iOS应用程序中UIView及其子类的重要性和使用方式。文章详细探讨了UIView如何作为用户交互的核心组件,以及它与其他UI控件和业务逻辑的关系。 ... [详细]
  • 本文探讨了线性表中元素的删除方法,包括顺序表和链表的不同实现策略,以及这些策略在实际应用中的性能分析。 ... [详细]
  • 本文深入解析宋代著名词人宋方君的作品《风流子》,通过细腻的译文和独到的赏析,带领读者走进词人的内心世界,感受其独特的艺术魅力。 ... [详细]
author-avatar
冷香之流光氤氲
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有