php5多重繼承的bug

來源:互聯網
上載者:User

<?php</p><p>class a extends b {</p><p>};</p><p>class b extends c{</p><p>};</p><p>class c{</p><p>};</p><p>?><br />

 

上面的代碼執行時會找報錯: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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.