Php定义基类和派生类,php – 派生类后面定义在同一个文件“不存在”?

免责声明:我不声称要了解扎德的内在工作.以下是我对PHP源代码的解释,大部分来自受过教育的猜测.尽管我对结论充满信心,但术语或细节可能会被关闭.我很乐意听到有关Zend内部人士的经验的人.

调查

case ZEND_DECLARE_INHERITED_CLASS:

{

zend_op *fetch_class_opline = opline-1;

zval *parent_name;

zend_class_entry **pce;

parent_name = &CONSTANT(fetch_class_opline->op2.constant);

if ((zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSRMLS_CC) == FAILURE) ||

((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES) &&

((*pce)->type == ZEND_INTERNAL_CLASS))) {

if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) {

zend_uint *opline_num = &CG(active_op_array)->early_binding;

while (*opline_num != -1) {

opline_num = &CG(active_op_array)->opcodes[*opline_num].result.opline_num;

}

*opline_num = opline - CG(active_op_array)->opcodes;

opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED;

opline->result_type = IS_UNUSED;

opline->result.opline_num = -1;

}

return;

}

if (do_bind_inherited_class(CG(active_op_array), opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL) {

return;

}

/* clear unnecessary ZEND_FETCH_CLASS opcode */

zend_del_literal(CG(active_op_array), fetch_class_opline->op2.constant);

MAKE_NOP(fetch_class_opline);

table = CG(class_table);

break;

}

该代码立即调用zend_lookup_class查看符号表中是否存在父类…,然后根据是否找到父类来分歧.

我们先看看如果找到父类,它会做什么:

if (do_bind_inherited_class(CG(active_op_array), opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL) {

return;

}

转到do_bind_inherited_class,我们看到最后一个参数(在这个调用中为1)被称为compile_time.这听起来很有趣这个论点有什么作用?

if (compile_time) {

op1 = &CONSTANT_EX(op_array, opline->op1.constant);

op2 = &CONSTANT_EX(op_array, opline->op2.constant);

} else {

op1 = opline->op1.zv;

op2 = opline->op2.zv;

}

found_ce = zend_hash_quick_find(class_table, Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_HASH_P(op1), (void **) &pce);

if (found_ce == FAILURE) {

if (!compile_time) {

/* If we're in compile time, in practice, it's quite possible

* that we'll never reach this class declaration at runtime,

* so we shut up about it. This allows the if (!defined('FOO')) { return; }

* approach to work.

*/

zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", Z_STRVAL_P(op2));

}

return NULL;

} else {

ce = *pce;

}

好的,所以它从静态(从PHP用户的角度)或动态上下文读取父类和派生类名,具体取决于compile_time状态.然后,它尝试在类表中找到类条目(“ce”),如果没有找到,则它在编译时返回,而不会在运行时发生致命错误.

这听起来非常重要.我们回到zend_do_early_binding.如果没有找到父类,该怎么办?

if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) {

zend_uint *opline_num = &CG(active_op_array)->early_binding;

while (*opline_num != -1) {

opline_num = &CG(active_op_array)->opcodes[*opline_num].result.opline_num;

}

*opline_num = opline - CG(active_op_array)->opcodes;

opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED;

opline->result_type = IS_UNUSED;

opline->result.opline_num = -1;

}

return;

看来它正在生成will trigger a call到do_bind_inherited_class的操作码,但是这次,compile_time的值将为0(false).

最后,class_exists PHP功能的实现如何?查看来源显示此代码段:

found = zend_hash_find(EG(class_table), name, len+1, (void **) &ce);

大!这个class_table变量是与在前面看到的do_bind_inherited_class调用相关的class_table.所以class_exists的返回值取决于该类的条目是否已经被do_bind_inherited_class插入到class_table中.

结论

Zend编译器在编译时不对include指令执行操作(即使文件名是硬编码的).

如果是这样,那么根据compile_time标志没有设置,就没有理由发出类重新发生的致命错误;该错误可以无条件发送.

当编译器遇到基类没有在同一个脚本文件中声明的派生类声明时,它会将其内部数据结构中的类注册的动作推送到运行时.

这从上面的代码片段可以看出,它在脚本执行时设置了一个ZEND_DECLARE_INHERITED_CLASS_DELAYED操作码来注册该类.在这一点上,compile_time标志将为false,行为将会微乎其微.

class_exists的返回值取决于该类是否已经被注册.

由于在编译时和运行时都以不同的方式发生,所以class_exists的行为也是不同的:

祖先都包含在同一个源文件中的类在编译时注册;它们存在并且可以在该脚本中的任何点实例化>在另一个源文件中定义了祖先的类在运行时被注册;在VM执行与源代码类对应的操作码之前,这些类不存在于所有实际目的(class_exists返回false,实例化会给出致命错误)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值