3:
4: function __autoload ($classname) {
0 NOP
0 RECV 1
5: if (!class_exists($classname)) {
1 SEND_VAR !0
2 DO_FCALL 'class_exists' [extval:1]
3 BOOL_NOT $0 =>RES[~1]
4 JMPZ ~1, ->8
6: require_once ($classname. ".class.php");
5 CONCAT !0, '.class.php' =>RES[~2]
6 INCLUDE_OR_EVAL ~2, REQUIRE_ONCE
7: }
7 JMP ->8
8: }
8 RETURN null
9:
10: $p = new Person('Fred', 35);
1 FETCH_CLASS 'Person' =>RES[:0]
2 NEW :0 =>RES[$1]
3 SEND_VAL 'Fred'
4 SEND_VAL 35
5 DO_FCALL_BY_NAME [extval:2]
6 ASSIGN !0, $1
11:
12: var_dump ($p);
7 SEND_VAR !0
8 DO_FCALL 'var_dump' [extval:1]
13: ?>在 autoload.php 的第 10 行代码中我们需要为类 Person 实例化一个对象。因此
autoload 机制一定会在该行编译后的 opcode 中有所体现。从上面的第 10 行代码生成的
OPCODE 中我们知道,在实例化对象 Person 时,首先要执行 FETCH_CLASS 指令。我们就
从
PHP 对 FETCH_CLASS 指令的处理过程开始我们的探索之旅。
通过查阅
PHP 的源代码(我使用的是 PHP 5.3alpha2 版本)可以发现如下的调用序列:
ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, ...) (zend_vm_def.h 1864 行)
=> zend_fetch_class (zend_execute_API.c 1434 行)
=>zend_lookup_class_ex (zend_execute_API.c 964 行)
=> zend_call_function(&fcall_info, &fcall_cache) (zend_execute_API.c 1040 行)
在最后一步的调用之前,我们先看一下调用时的关键参数:
/* 设置 autoload_function 变量值为"__autoload" */
fcall_info.function_name = &autoload_function; // Ooops, 终于发现"__autoload"了
...