<? 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/>
When the above code is executed, an error is returned:Fatal error: Class 'B' not found.
An error occurred in the running phase. After analyzing the PHP compilation and execution process, the following parsing sequence is obtained...
- Start:
- Top_statement_list
- ;
- Top_statement_list:
- Top_statement_list
- ... // Omitted
- ;
- Top_statement:
- ... // Omitted
- | Class_declaration_statement
- ... // Omitted
- ;
- Class_declaration_statement:
- Unticked_class_declaration_statement
- ;
- Unticked_class_declaration_statement:
- Class_entry_type T_STRING extends_from
- ... // Omitted
- ;
- Class_entry_type:
- T_CLASS
- ... // Omitted
- ;
- Extends_from:
- /* Empty */
- | T_EXTENDS fully_qualified_class_name
- ... // Omitted
- ;
- Fully_qualified_class_name:
- T_STRING {zend_do_fetch_class (& $, & $1 TSRMLS_CC );}
-
- Zend_do_fetch_class will set opcode = ZEND_FETCH_CLAS
From this process, we can find that this should be a PHP5 bug. For fully_qualified_class_name, if the Delimiter is also inherited from a class, then an error will occur, because fully_qualified_class_name is just simple to fetch_class, if this class is not entered into class_table at this time, an error will occur. That is to say, a mechanism is required to ensure that the parent class is processed first.
The following is my conclusion after analyzing the source code:
For a, because it is a derived class, in the compilation phase, when it is defined, it will:
- zend_do_begin_class_declaratio
In this function, it calls:
- build_runtime_defined_function_key(&opline->op1.u.constant, lcname, name_len TSRMLS_CC)
To generate one:
- sprintf(result->value.str.val, "%c%s%s%s", '/0', name, filename, char_pos_buf
- );
To store the classname of a compiler into class_table:
- 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
- );
Finally, when the top_statement is absorbed, a class will be generated (filled with class_table );
- top_statement:
- statement
- ...
- | class_declaration_statement { zend_do_early_binding(TSRMLS_C); }
- ...
- ...
- ;
When zend_do_early_binding:
- void zend_do_early_binding(TSRMLS_D){
- ...
- ...
- switch (opline->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->opcode == ZEND_DECLARE_CLASS) {
- if (do_bind_class(opline, CG(class_table), 1 TSRMLS_CC) == NULL) {
- return;
- }
- } else if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS) {
- zval *parent_name = &(opline-1)->op2.u.constant;
- zend_class_entry **pce;
- if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &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 */
- }
As you can see, if the parent class cannot be found, it will be returned directly. That is to say, if the parent class cannot be found in the derived class during compilation, it will not be initialized, but will be postponed to the execution period. An opline with the opcode ZEND_DECLARE_INHERITED_CLASS will be allocated to generate the defined class at runtime:
- 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)
- {
- .......
- // Attributes and methods of the hash_merg subclass and parent class
- 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)
- .....
- }
The problem arises at this time:
Because our B is also a derived class, when we execute do_bind_inherited_class of a, for B, we also need to create a ZEND_DECLARE_INHERITED_CLASS, that is, there is no B in the class_table at this time.
This explains that if the base class c is defined first, no errors will occur.
Well, this should be a Bug in PHP5.
I have reported a bug to the PHP development team and sent a letter asking Rasmus Lerdof (the creator of PHP) to see what they said:
Http://bugs.php.net/bug.php? Id = 45904
Address: http://www.laruence.com/2008/08/24/427.html