1、autoload机制概述
使用PHP OOP模式开发系统时,出于文件组织结构的原因,将各个类放在单独的文件,每个类专注于一个任务,是OOP开发的基本思想之一:高内聚 。这样易于对类进行复用和维护。一般地,如果需要使用到这个类时,就可以通过require_once()语句来实现。如:
//Books.class.php
<?php
class Books
{
protected $title;
protected $author;
function __construct($title, $author)
{
$this->title = $title;
$this->author = $author;
}
}
//no_autoload.php
<?php
require_once("Books.class.php");
$books = new Books("PHP5", "Gilmore");
print_r($books);
?>
以上只是一个极其简单的例子,但随着项目规模的不断扩大,如果脚本中需要多个类,就必须得require_once()多个类文件,这样一来就会变得非常麻烦,也有可能会造成遗漏或者包含进不必要的类文件。在PHP5 中引入了自动加载对象的概念。自动加载通过定义特殊的 __autoload 函数,当引用未在脚本中定义的类时就会自动调用__autoload这个函数,可以使用得PHP程序只有在使用类时才自动加载类文件,达到按需加载功能。将上面的例子修改如下:
//autoload.php
<?php
function __autoload($classname)
{
require_once($classname.'.class.php');
}
$books = new Books("PHP5", "Gilmore");
print_r($books);
?>
当运行这个脚本时,如果没有找到相应的类,这时就会调用__autoload函数,在这个函数中我们可以加载需要使用的类文件。 这个函数可以放到应用程序的全局配置文件中。
2、autoload机制的实现
我们知道,PHP的执行是将脚本编译成OPCODE字节码,Zend虚拟机再来执行这些OPCODE。来,我们将autoload.php文件
编译成OPCODE,看看PHP在这个过程中做了些什么:
利用由 Derick Rethans开发的VLD (Vulcan Logic Dissassembler)模块生成:
Branch analysis from position: 0
Return found
filename: /var/www/html/autoload.php
function name: (null)
number of ops: 11
compiled vars: !0 = $books
line # op fetch ext return operands
-------------------------------------------------------------------------------
2 0 NOP
7 1 ZEND_FETCH_CLASS :0 'Books'
2 NEW $1 :0
3 SEND_VAL 'PHP5'
4 SEND_VAL 'Gilmore'
5 DO_FCALL_BY_NAME 2
6 ASSIGN !0, $1
8 7 SEND_VAR !0
8 DO_FCALL 1 'var_dump'
9 9 RETURN 1
10* ZEND_HANDLE_EXCEPTION
Function __autoload:
Branch analysis from position: 0
Return found
filename: /var/www/html/autoload.php
function name: __autoload
number of ops: 5
compiled vars: !0 = $classname
line # op fetch ext return operands
-------------------------------------------------------------------------------
2 0 RECV 1
4 1 CONCAT ~0 !0, '.class.php'
2 INCLUDE_OR_EVAL ~0, REQUIRE_ONCE
5 3 RETURN null
4* ZEND_HANDLE_EXCEPTION
End of function __autoload.
Branch analysis from position: 0
Return found
filename: /var/www/html/Books.class.php
function name: (null)
number of ops: 3
compiled vars: none
line # op fetch ext return operands
-------------------------------------------------------------------------------
3 0 NOP
13 1 RETURN 1
2* ZEND_HANDLE_EXCEPTION
Function __autoload:
Branch analysis from position: 0
Return found
filename: /var/www/html/autoload.php
function name: __autoload
number of ops: 5
compiled vars: !0 = $classname
line # op fetch ext return operands
-------------------------------------------------------------------------------
2 0 RECV 1
4 1 CONCAT ~0 !0, '.class.php'
2 INCLUDE_OR_EVAL ~0, REQUIRE_ONCE
5 3 RETURN null
4* ZEND_HANDLE_EXCEPTION
End of function __autoload.
Class Books:
Function __construct:
Branch analysis from position: 0
Return found
filename: /var/www/html/Books.class.php
function name: __construct
number of ops: 8
compiled vars: !0 = $title, !1 = $author
line # op fetch ext return operands
-------------------------------------------------------------------------------
7 0 RECV 1
1 RECV 2
9 2 ZEND_ASSIGN_OBJ 'title'
3 ZEND_OP_DATA !0
10 4 ZEND_ASSIGN_OBJ 'author'
5 ZEND_OP_DATA !1
11 6 RETURN null
7* ZEND_HANDLE_EXCEPTION
End of function __construct.
End of class Books.
object(Books)#1 (2) {
["title:protected"]=>
string(4) "PHP5"
["author:protected"]=>
string(7) "Gilmore"
}
其中:在实例化对象Books时,首先要执行 ZEND_FETCH_CLASS指令, 具体可以查看ZEND_FETCH_CLASS指令处理的过程。
PHP在实例化一个对象时(实际上在实现接口,使用类常数或类中的静态变量,调用类中的静态方法时都是如此),首先会在系统中查找该类(或接口)是否存在,如果不存在的话就尝试使用autoload机制来加载该类。而autoload机制的主要执行过程为:
(1) 检查执行器全局变量函数指针autoload_func是否为NULL。
(2) 如果autoload_func==NULL, 则查找系统中是否定义有__autoload()函数,如果没有,则报告错误并退出。
(3) 如果定义了__autoload()函数,则执行__autoload()尝试加载类,并返回加载结果。
(4) 如果autoload_func不为NULL,则直接执行autoload_func指针指向的函数用来加载类。注意此时并不检查__autoload()函数是否定义。
PHP提供了两种方法来实现自动装载机制,一种是:使用用户定义的__autoload()函数,这通常在PHP源程序中来实现;另外一种就是设计一个函数,将autoload_func指针指向它,这通常使用C语言在PHP扩展中实现。如果既实现了 __autoload()函数,又实现了autoload_func(将autoload_func指向某一PHP函数),那么只执行 autoload_func 函数。
在很多框架中都使用这种方法来自动加载类文件,其中 Zend Framework V1.8后的自动加载中使用了另一种机制:spl_autoload_register(array(__CLASS__, 'autoload'));
3、spl_autoload机制的实现
SPL 是Standard PHP Library(标准PHP库)的缩写。它是PHP5引入的一个扩展库,其主要功能包括autoload机制的实现及包括各种Iterator接口或类。 SPL autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。SPL有两个不同的函数 spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。
spl_autoload 是SPL实现的默认的自动加载函数,它的功能比较简单。它可以接收两个参数,第一个参数是$class_name,表示类名,第二个参数$file_extensions是可选的,表示类文件的扩展名,可以在$file_extensions中指定多个扩展名,护展名之间用分号隔开即可;如果不指定的话,它将使用默认的扩展名.inc或.php。spl_autoload首先将$class_name变为小写,然后在所有的 include path中搜索$class_name.inc或$class_name.php文件(如果不指定$file_extensions参数的话),如果找到,就加载该类文件。你可以手动使用spl_autoload(”Books”, “.class.php”)来加载Person类。实际上,它跟require/include差不多,不同的它可以指定多个扩展名。根据每个类库不同的命名机制实现各自的自动加载函数,然后使用spl_autoload_register分别将其注册到SPL自动加载函数队列中。
当PHP找不到类文件会调用__autoload()函数,在这个函数中我们可以加载需要使用的类。但当注册了自己的函数或方法,PHP不会调用__autoload()函数,而会调用自定义的函数:spl_autoload_register(array('class_name','method_name')); 实现类文件自动加载。
4、autoload机制的效率
正确地使用autoload机制可以提高系统效率,因为它不会将不需要的类文件加载到系统中。
以上内容部分来自:http://hi.baidu.com/jeakccc/blog/item/fb4d49c2473ed95cb319a8f2.html