PHP5多层继承顺序的bug

·        作者Laruence(   )

·        本文地址http://www.laruence.com/2008/08/24/427.html

·        转载请注明出处

今天guoxiaod提出了一个问题,如下:

1.       <?php

2.       class a extends b {

3.       };

4.       class b extends c{

5.       };

6.       class c{

7.       };

8.       ?>

9.        

会导致fatal error:

1.       PHP Fatal error: Class 'b' not found in /home/xinchen/1.php on line 2

2.       Fatal error: Class 'b' not found in /home/xinchen/1.phpon line 2

分析这个问题,是运行阶段出错,经过分析PHP的编译,执行过程,得出如下的parsing顺序

1.       start:

2.           top_statement_list

3.       ;

4.        

5.       top_statement_list:

6.               top_statement_list

7.       .... //有省略

8.       ;

9.        

10.    top_statement:

11.    .... //有省略

12.        |   class_declaration_statement

13.     .... //有省略

14.    ;

15.     

16.    class_declaration_statement:

17.            unticked_class_declaration_statement

18.    ;

19.     

20.    unticked_class_declaration_statement:

21.            class_entry_typeT_STRING extends_from

22.    .... //有省略

23.    ;

24.     

25.    class_entry_type:

26.            T_CLASS

27.    .... //有省略

28.    ;

29.     

30.    extends_from:

31.            /* empty*/

32.        |   T_EXTENDS fully_qualified_class_name

33.    .... //有省略

34.    ;

35.    fully_qualified_class_name:

36.            T_STRING{ zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }

37.    ;

1.       zend_do_fetch_class 会设置opcode = ZEND_FETCH_CLASS

从这个过程我们可以发现,这个应该是PHP5bug对于fully_qualified_class_name,如果fully_qualified_class_name也是继承来自一个类,那么就会出错,因为fully_qualified_class_name只是简单的去fetch_class,而如果这个时候,这个类还没有被填入到class_table就会出错。也就是说,需要有个机制,来保证父class首先被处理。

以下是我分析源码后的结论:
对于a,因为是个派生类,在编译阶段,当遇到它的定义的时候,会:

1.        zend_do_begin_class_declaration

在这个函数中,会调用:

1.        build_runtime_defined_function_key(&opline->op1.u.constant,lcname, name_len TSRMLS_CC);

来产生一个:

1.          sprintf(result->value.str.val, "%c%s%s%s",'\0', name, filename, char_pos_buf);

的字符串,来做为一个编译器的classname存入class_table:

1.           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);

最后在吸收top_statement的时候,会有一次类的生成(填入class_table);

1.       top_statement:

2.               statement

3.        ...

4.           |   class_declaration_statement     { zend_do_early_binding(TSRMLS_C); }

5.       ...

6.       ...

7.       ;

zend_do_early_binding的时候:

1.       void zend_do_early_binding(TSRMLS_D){

2.       ...

3.       ...

4.         switch (opline->opcode) {

5.             case ZEND_DECLARE_FUNCTION:

6.                 if (do_bind_function(opline, CG(function_table), 1) == FAILURE) {

7.                     return;

8.                 }

9.                 table= CG(function_table);

10.              break;

11.          case ZEND_DECLARE_CLASS:

12.          case ZEND_DECLARE_INHERITED_CLASS:

13.              is_abstract_class= 1;

14.              /* break missing intentionally */

15.          case ZEND_VERIFY_ABSTRACT_CLASS: {

16.                  zend_op*verify_abstract_class_op = opline;

17.     

18.                  if (!is_abstract_class) {

19.                      opline--;

20.                  }

21.                  if (opline->opcode == ZEND_DECLARE_CLASS) {

22.                      if (do_bind_class(opline, CG(class_table), 1 TSRMLS_CC) == NULL) {

23.                          return;

24.                      }

25.                  } elseif (opline->opcode == ZEND_DECLARE_INHERITED_CLASS) {

26.                      zval*parent_name = &(opline-1)->op2.u.constant;

27.                      zend_class_entry**pce;

28.     

29.                        if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSR

30.    MLS_CC) == FAILURE) {

31.                            return;

32.                        }

33.                        if (do_bind_inherited_class(opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL)

34.     {

35.                            return;

36.                        }

37.                        /* clear unnecessary ZEND_FETCH_CLASS opcode*/

38.    }

看到了吧,如果找不到父类,就直接返回了,也就是说,派生类在编译期如果找不到父类,就不会被真正初始化,而是推迟到执行期。会分配一个opcodeZEND_DECLARE_INHERITED_CLASSopline,用来在运行期真正生成定义的类:

1.       ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time TSRMLS_DC)

2.       {

3.       .......

4.       //hash_merg子类和父类的属性、方法

5.           if (zend_hash_add(class_table, opline->op2.u.constant.value.str.val, opline->op2.u.constant.value.str.len+1, pce, sizeof(zend_class_entry *), NULL)==FAILURE)

6.       .....

7.       }

这个时候问题就来了:
因为我们的b也是一个派生类,所以在执行ado_bind_inherited_class时候,对于b,他也需要做一个ZEND_DECLARE_INHERITED_CLASS,也就是说,此时的class_table中是没有b的。
这也就解释了,如果最基类c,定义在前的时候,就不会出错。

恩,这个应该是PHP5的一个Bug

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值