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

php5多重继承的bug

<?phpclassaextendsb{};classbextendsc{};classc{};?>上面的代码执行时会找报错:Fatalerror:Classb

 

上面的代码执行时会找报错:Fatal error: Class 'b' not found。

 

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

 
  1. start:
  2.     top_statement_list
  3. ;
  4. top_statement_list:
  5.         top_statement_list
  6. .... //有省略
  7. ;
  8. top_statement:
  9. .... //有省略
  10.     | class_declaration_statement
  11.  .... //有省略
  12. ;
  13. class_declaration_statement:
  14.         unticked_class_declaration_statement
  15. ;
  16. unticked_class_declaration_statement:
  17.         class_entry_type T_STRING extends_from
  18. .... //有省略
  19. ;
  20. class_entry_type:
  21.         T_CLASS
  22. .... //有省略
  23. ;
  24. extends_from:
  25.         /* empty */
  26.     | T_EXTENDS fully_qualified_class_name
  27. .... //有省略
  28. ;
  29. fully_qualified_class_name:
  30.         T_STRING { zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
  31.  
 
  1. zend_do_fetch_class 会设置opcode = ZEND_FETCH_CLAS

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

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

 
  1.  zend_do_begin_class_declaratio

在这个函数中,会调用:

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

的字符串,来做为一个编译器的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
    1. );

最后在吸收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.               if (!is_abstract_class) {
  18.                   opline--;
  19.               }
  20.               if (opline->opcode == ZEND_DECLARE_CLASS) {
  21.                   if (do_bind_class(opline, CG(class_table), 1 TSRMLS_CC) == NULL) {
  22.                       return;
  23.                   }
  24.               } else if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS) {
  25.                   zval *parent_name = &(opline-1)->op2.u.constant;
  26.                   zend_class_entry **pce;
  27.                     if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSR
  28. MLS_CC) == FAILURE) {
  29.                         return;
  30.                     }
  31.                     if (do_bind_inherited_class(opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL)
  32.  {
  33.                         return;
  34.                     }
  35.                     /* clear unnecessary ZEND_FETCH_CLASS opcode */
  36. }

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

 
  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也是一个派生类,所以在执行a的do_bind_inherited_class时候,对于b,他也需要做一个ZEND_DECLARE_INHERITED_CLASS,也就是说,此时的class_table中是没有b的。
这也就解释了,如果最基类c,定义在前的时候,就不会出错。

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

我已经报bug给PHP开发组并发信询问Rasmus Lerdof(the creator of PHP),看他们怎么说了:
http://bugs.php.net/bug.php?id=45904

 

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


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
author-avatar
董可芳妍_731
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有