php class 多重继承 bug,php5多重继承的bug

class a extends b {

};

class b extends c{

};

class c{

};

?>

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

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

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(&$$, &$1 TSRMLS_CC); }

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,因为是个派生类,在编译阶段,当遇到它的定义的时候,会:

zend_do_begin_class_declaratio

在这个函数中,会调用:

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

来产生一个:

sprintf(result->value.str.val, "%c%s%s%s", '/0', name, filename, char_pos_buf

);

的字符串,来做为一个编译器的classname存入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

);

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

top_statement:

statement

...

| class_declaration_statement { zend_do_early_binding(TSRMLS_C); }

...

...

;

在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 */

}

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

ZEND_APIzend_class_entry*do_bind_inherited_class(zend_op*opline, HashTable*class_table, zend_class_entry*parent_ce, zend_boolcompile_timeTSRMLS_DC)

{

.......

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

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)

.....

}

这个时候问题就来了:

因为我们的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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值