<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>风雪之隅 &#187; class</title>
	<atom:link href="http://www.laruence.com/tag/class/feed" rel="self" type="application/rss+xml" />
	<link>http://www.laruence.com</link>
	<description>PHP语言, PHP扩展, Zend引擎相关的研究,技术,新闻分享 - 左手代码 右手诗</description>
	<lastBuildDate>Wed, 08 Feb 2012 05:12:49 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>PHP5多层继承顺序的bug</title>
		<link>http://www.laruence.com/2008/08/24/427.html</link>
		<comments>http://www.laruence.com/2008/08/24/427.html#comments</comments>
		<pubDate>Sun, 24 Aug 2008 13:43:57 +0000</pubDate>
		<dc:creator>雪候鸟</dc:creator>
				<category><![CDATA[Linux/Unix]]></category>
		<category><![CDATA[PHP应用]]></category>
		<category><![CDATA[PHP源码分析]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[extends]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.laruence.com/?p=427</guid>
		<description><![CDATA[<p>今天guoxiaod提出了一个问题，如下：
  <coolcode lang="php" linenum="off">
<?php
class a extends b {
};
class b extends c{
};
class c{
};
?>
  </coolcode>
 会导致fatal error:
<coolcode lang="shell" linenum="off">
PHP Fatal error:  Class 'b' not found in /home/xinchen/1.php on line 2
Fatal error: Class 'b' not found in /home/xinchen/1.php on line 2
</coolcode>
 分析这个问题，是运行阶段出错，经过分析PHP的编译，执行过程，得出如下的parsing顺序...</p>]]></description>
			<content:encoded><![CDATA[<div class="copyright" >
<ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;">
<li>作者: <a href="http://www.laruence.com" >Laruence</a>(<a href="http://www.twitter.com/laruence"  target="meme"  title="Twitter" ><img src="/images/ico-twitter.png" /></a> <a href="http://t.sina.com/laruence"  target="meme"  title="新浪微博" ><img src="/images/ico-sina.png" /></a> <a href="http://fusion.google.com/add?feedurl=http://www.laruence.com/feed"  target="meme"  title="Google阅读器" ><img src="/images/ico-google.png" /></a> <a href="mailto:laruence@yahoo.com.cn"  target="meme"  title="邮件" ><img src="/images/ico-mail.png" /></a>)</li>
<li>本文地址: <a href="http://www.laruence.com/2008/08/24/427.html"  title="Permanet Link to PHP5多层继承顺序的bug" >http://www.laruence.com/2008/08/24/427.html</a></li>
</li>
<li>转载请注明出处 </li>
</ul></div>
<p>今天guoxiaod提出了一个问题，如下：</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
&lt;?php
class a extends b {
};
class b extends c{
};
class c{
};
?&gt;
  </pre>
<p> 会导致fatal error:</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
PHP Fatal error:  Class 'b' not found in /home/xinchen/1.php on line 2
Fatal error: Class 'b' not found in /home/xinchen/1.php on line 2
</pre>
<p>分析这个问题，是运行阶段出错，经过分析PHP的编译，执行过程，得出如下的parsing顺序…</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
start:
    top_statement_list
;

top_statement_list:
        top_statement_list
.... //有省略
;

top_statement:
.... //有省略
    |   class_declaration_statement
 .... //有省略
;

class_declaration_statement:
        unticked_class_declaration_statement
;

unticked_class_declaration_statement:
        class_entry_type T_STRING extends_from
.... //有省略
;

class_entry_type:
        T_CLASS
.... //有省略
;

extends_from:
        /* empty */
    |   T_EXTENDS fully_qualified_class_name
.... //有省略
;
fully_qualified_class_name:
        T_STRING { zend_do_fetch_class(&amp;$$, &amp;$1 TSRMLS_CC); }
;
</pre>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
zend_do_fetch_class 会设置opcode = ZEND_FETCH_CLASS
</pre>
<p>从这个过程我们可以发现，这个应该是PHP5的bug， 对于fully_qualified_class_name，如果fully_qualified_class_name也是继承来自一个类，那么就会出错， 因为fully_qualified_class_name只是简单的去fetch_class, 而如果这个时候，这个类还没有被填入到class_table就会出错。也就是说，需要有个机制，来保证父class首先被处理。</p>
<p>以下是我分析源码后的结论：<br/>
对于a，因为是个派生类，在编译阶段，当遇到它的定义的时候，会：</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
 zend_do_begin_class_declaration
</pre>
<p>在这个函数中，会调用:</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
 build_runtime_defined_function_key(&amp;opline-&gt;op1.u.constant, lcname, name_len TSRMLS_CC);
</pre>
<p>来产生一个：</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
   sprintf(result-&gt;value.str.val, &quot;%c%s%s%s&quot;, '\0', name, filename, char_pos_buf);
</pre>
<p>的字符串，来做为一个编译器的classname存入class_table:</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
    zend_hash_update(CG(class_table), opline-&gt;op1.u.constant.value.str.val, opline-&gt;op1.u.constant.value.str.len, &amp;new_class_entry, sizeof(zend_class_entry *), NULL);
</pre>
<p>最后在吸收top_statement的时候，会有一次类的生成(填入class_table);</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
top_statement:
        statement
 ...
    |   class_declaration_statement     { zend_do_early_binding(TSRMLS_C); }
...
...
;
</pre>
<p>在zend_do_early_binding的时候：</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
void zend_do_early_binding(TSRMLS_D){
...
...
  switch (opline-&gt;opcode) {
      case ZEND_DECLARE_FUNCTION:
          if (do_bind_function(opline, CG(function_table), 1) == FAILURE) {
              return;
          }
          table = CG(function_table);
          break;
      case ZEND_DECLARE_CLASS:
      case ZEND_DECLARE_INHERITED_CLASS:
          is_abstract_class = 1;
          /* break missing intentionally */
      case ZEND_VERIFY_ABSTRACT_CLASS: {
              zend_op *verify_abstract_class_op = opline;

              if (!is_abstract_class) {
                  opline--;
              }
              if (opline-&gt;opcode == ZEND_DECLARE_CLASS) {
                  if (do_bind_class(opline, CG(class_table), 1 TSRMLS_CC) == NULL) {
                      return;
                  }
              } else if (opline-&gt;opcode == ZEND_DECLARE_INHERITED_CLASS) {
                  zval *parent_name = &amp;(opline-1)-&gt;op2.u.constant;
                  zend_class_entry **pce;

                    if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &amp;pce TSR
MLS_CC) == FAILURE) {
                        return;
                    }
                    if (do_bind_inherited_class(opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL)
 {
                        return;
                    }
                    /* clear unnecessary ZEND_FETCH_CLASS opcode */
}
</pre>
<p>看到了吧，如果找不到父类，就直接返回了，也就是说，派生类在编译期如果找不到父类，就不会被真正初始化，而是推迟到执行期。会分配一个opcode为ZEND_DECLARE_INHERITED_CLASS的opline，用来在运行期真正生成定义的类：</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
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)
{
.......
//hash_merg子类和父类的属性、方法
    if (zend_hash_add(class_table, opline-&gt;op2.u.constant.value.str.val, opline-&gt;op2.u.constant.value.str.len+1, pce, sizeof(zend_class_entry *), NULL)==FAILURE)
.....
}
</pre>
<p>这个时候问题就来了：<br/>
因为我们的b也是一个派生类，所以在执行a的do_bind_inherited_class时候，对于b，他也需要做一个ZEND_DECLARE_INHERITED_CLASS，也就是说，此时的class_table中是没有b的。<br/>
这也就解释了，如果最基类c，定义在前的时候，就不会出错。</p>
<p>恩，这个应该是PHP5的一个Bug。 </p>
<p>我已经报bug给PHP开发组并发信询问Rasmus Lerdof(the creator of PHP)，看他们怎么说了：<br/>
<a href="http://bugs.php.net/bug.php?id=45904" >http://bugs.php.net/bug.php?id=45904</a>
</p>
<p><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script></p>
<hr/><h2>Comments</h2><ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/24</a>, <a href="http://onemouse.cn"  rel="external nofollow"  class="url" >guoxiaod</a> writes: 不过，按照这个逻辑的话，似乎就是这么设计的。</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/24</a>, <a href="http://onemouse.cn"  rel="external nofollow"  class="url" >guoxiaod</a> writes: 给 rasmus 发个信 ?</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/24</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: 报了bug了，看看PHP开发组怎么回应吧。</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/25</a>, <a href="http://onemouse.cn"  rel="external nofollow"  class="url" >guoxiaod</a> writes: 这样似乎可以想的通
bind 的时候只有 c 是存在的
执行的时候 自然找不到 b
而 如果c 在前的话 ， b 就bind 了
自然 b就可以找到了</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/25</a>, <a href="http://www.surfchen.org"  rel="external nofollow"  class="url" >surfchen</a> writes: 这个bug有人报过。http://bugs.php.net/bug.php?id=6418</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/25</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: 哦？看到了，他是针对PHP4， 另外也没关系，起码我们分析出原因了。
也就明白了，为什么单层继承不会出问题。
并且，对于上例，如果c定义在前，也不会出问题。</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/08/26</a>, chinawolfs writes: 
执行这样应该不会有什么问题的.顺序的情况</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2008/09/08</a>, x writes: [25 Aug 9:13am UTC] tularis@php.net 
Please do not submit the same bug more than once. An existing
bug report already describes this very problem. Even if you feel
that your issue is somewhat different, the resolution is likely
to be the same. 

Thank you for your interest in PHP.

Duplicate of bug #45903</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2010/09/29</a>, 好消息 writes: 好消息1.纵观社会的发展方向,<a href="http://www.hendrex.com.cn/main/20081289304942/Page/201041221543012/"  rel="nofollow" >火花机</a>行业拥有良好的发展趋势,并且已经成为一个传统而又崭新的创业领域。<a href="http://www.hendrex.com.cn/main/20081289304942/page/2010541721323/"  rel="nofollow" >上海加工中心</a>目前,随着我国经济的快速发展,对<a href="http://www.hendrex.com.cn/main/20081289304942/page/20105170525484/"  rel="nofollow" >苏州火花机</a>机械行业的加大了重视度,尤其是矿山机械。随着近几年,砂石冶金等行业生产规模的扩大,<a href="http://www.hendrex.com.cn"  rel="nofollow" >加工中心维修</a>其对人工机制砂的需求越来越大：2.秋季美容<a href="http://www.shendayumei.cn"  rel="nofollow" >护肤</a>:水嫩不干燥 <a href="http://www.shendayumei.cn"  rel="nofollow" >美容</a>秋季护理大，简要内容:爱美女性往往会过多关心面部的青春娇美,却易忽视自己颈部的保养。<a href="http://www.nj4006.com"  rel="nofollow" >400</a>尤其是在干燥的秋季,颈部皮肤容易老化,粗糙无光、松弛多皱。<a href="http://www.zjhuanbao.com.cn"  rel="nofollow" >浙江环保设备采购</a>每天晚上洗浴时,用含碱少的温和型香皂洗涤颈部,<a href="http://dt.wx35.com"  rel="nofollow" >江苏墙体广告</a>用松软毛巾擦干,再进行按摩。</li><li><a href="http://www.laruence.com/2008/08/24/427.html" >2011/03/20</a>, <a href="http://www.w3hacker.com/?p=152"  rel="external nofollow"  class="url" >PHP5多层继承顺序的bug | 万维网黑客联盟</a> writes: [...] 本文地址: http://www.laruence.com/2008/08/24/427.html [...]</li></ul><hr/><h2>Related posts:</h2><ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2010/05/28/1565.html"  rel="bookmark"  title="Permanent Link: PHP错误抑制符(@)导致引用传参失败的Bug" >PHP错误抑制符(@)导致引用传参失败的Bug</a></li><li><a href="http://www.laruence.com/2011/03/29/1949.html"  rel="bookmark"  title="Permanent Link: 深入理解PHP原理之Session Gc的一个小概率Notice" >深入理解PHP原理之Session Gc的一个小概率Notice</a></li><li><a href="http://www.laruence.com/2011/12/30/2440.html"  rel="bookmark"  title="Permanent Link: PHP5.2.*防止Hash冲突拒绝服务攻击的Patch" >PHP5.2.*防止Hash冲突拒绝服务攻击的Patch</a></li><li><a href="http://www.laruence.com/2010/09/27/1754.html"  rel="bookmark"  title="Permanent Link: PHP stream未能及时清理现场导致Core的bug" >PHP stream未能及时清理现场导致Core的bug</a></li><li><a href="http://www.laruence.com/2008/09/20/523.html"  rel="bookmark"  title="Permanent Link: PHP5.3 α2初体验" >PHP5.3 α2初体验</a></li></ul><hr/><small  style="font-size:85%;font-size:85%;">Copyright &copy; 2010 <a href="http://www.laruence.com"  target="_blank" >风雪之隅</a> 版权所有, 转载务必注明. 该Feed只供个人使用, 禁止未注明的转载或商业应用. 非法应用的, 一切法律后果自负. 如有问题, 可发E-mail至my at laruence.com.(Digital Fingerprint: 73540ba0a1738d7d07d4b6038d5615e2)</small><h2 class="related_post_title" >Related Posts:</h2><ul class="related_post"   style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2011/03/22/1929.html"  title="PHP Reflection Extension的一个bug" >PHP Reflection Extension的一个bug</a></li><li><a href="http://www.laruence.com/2010/05/28/1565.html"  title="PHP错误抑制符(@)导致引用传参失败的Bug" >PHP错误抑制符(@)导致引用传参失败的Bug</a></li><li><a href="http://www.laruence.com/2010/05/26/1541.html"  title="PHP类型转换相关的一个Bug" >PHP类型转换相关的一个Bug</a></li><li><a href="http://www.laruence.com/2009/12/05/1172.html"  title="PHP5.2.x + APC的一个bug的定位" >PHP5.2.x + APC的一个bug的定位</a></li><li><a href="http://www.laruence.com/2012/02/08/2528.html"  title="PHP-5.3.9远程执行任意代码漏洞(CVE-2012-0830)" >PHP-5.3.9远程执行任意代码漏洞(CVE-2012-0830)</a></li><li><a href="http://www.laruence.com/2012/02/02/2515.html"  title="我们什么时候应该使用异常?" >我们什么时候应该使用异常?</a></li><li><a href="http://www.laruence.com/2012/02/01/2503.html"  title="使用exit(-1)为什么得到255退出码?" >使用exit(-1)为什么得到255退出码?</a></li><li><a href="http://www.laruence.com/2012/01/11/2482.html"  title="PHP的历史" >PHP的历史</a></li><li><a href="http://www.laruence.com/2012/01/10/2469.html"  title="如何设置一个严格30分钟过期的Session" >如何设置一个严格30分钟过期的Session</a></li><li><a href="http://www.laruence.com/2012/01/07/2453.html"  title="2012年1月全球www网站技术报告" >2012年1月全球www网站技术报告</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.laruence.com/2008/08/24/427.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

