background image

    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"了
...