PHP类的自动加载
在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,这么做会产生大量的require或者include方法调用,这样不因降低效率,而且使得代码难以维护,况且require_once的代价很大。
在PHP5之前,各个PHP框架如果要实现类的自动加载,一般都是按照某种约定自己实现一个遍历目录,自动加载所有符合约定规则的文件的类或函数。在PHP5后,当加载PHP类时,如果类所在文件没有被包含进来,或者类名出错,Zend引擎会自动调用**__autoload()函数。此函数需要用户自己实现__autoload()函数。 在PHP5.1.2版本后,可以使用spl_autoload_register()**函数自定义自动加载处理函数。当没有调用此函数,默认情况下会使用SPL自定义的spl_autoload函数。
# __autoload
__autoload() 是php中的一个魔术方法,在代码中当调用不存在的类时会自动调用该方法。
# 语法
_autoload ( string $class ) : void // class: 待加载的类名
# 用法
<?php
function __autoload($className)
{
echo 'autoload自动加载', $className,'<br />';
}
$myClass1 = new MyClass1();
2
3
4
5
6
7
8
如果调用某个未经include/require的类,在报错之前,还可以**__autoload()函数得到一次介入机会,系统会调用__autoload()函数,并把类名传给__autoload()函数,就可以在__autoload()**里加载需要的类。
例如:
<?php
function __autoload($classname) {
require_once ($classname . ".class.php");
}
2
3
4
5
因此,当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在__autoload()
函数中,将类名与实际的磁盘文件对应起来,就可以实现lazy loading的效果。
# 缺陷
但现在问题来了,假如在一个系统的实现中,假如需要使用很多其它的类库,这些类库可能是由不同的开发工程师开发,其类名与实际的磁盘文件的映射规则不尽相同。这时假如要实现类库文件的自动加载,就必须在**__autoload()函数中将所有的映射规则全部实现,因此__autoload()函数有可能会非常复杂,甚至无法实现。最后可能会导致__autoload()函数十分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很大的负面影响。所有问题都出现在__autoload() 是全局函数只能定义一次** ,那么如何来解决这个问题呢?答案就是使用一个 __autoload调用堆栈 ,不同的映射关系写到不同的 __autoload函数
中去,然后统一注册统一管理,这个就是 PHP5 引入的 SPL Autoload
。
# spl_autoload_register
**spl_autoload_register()是SPL(Standard PHP Library)**提供的方法。
# 语法
spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] ) : bool
- autoload_function:注册的自动装载函数
- throw:此参数设置了
autoload_function
无法成功注册时, **spl_autoload_register()**是否抛出异常 - prepend:如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部
# 用法
一旦调用spl_autoload_register()函数,当调用未定义类时,系统会按顺序调用注册到spl_autoload_register()函数的所有函数,而不是自动调用__autoload()函数。
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
use Autoload\TestClass;
function __autoload($className)
{
echo 'autoload自动加载', $className,'<br />';
}
spl_autoload_register(function($className){
echo 'spl autoload自动加载1', $className,'<br />';
});
spl_autoload_register(function($className){
echo 'spl autoload自动加载2', $className,'<br />';
});
$myClass1 = new MyClass1();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结果输出:
spl autoload自动加载1MyClass1
spl autoload自动加载2MyClass1
Fatal error: Uncaught Error: Class 'MyClass1' not found in /www/websites/www/gitlib/php/autoload.php:20 Stack trace: #0 {main} thrown in /www/websites/www/gitlib/php/autoload.php on line 20
2
3
4
还可以调用spl_autoload_register()函数以注册一个回调函数,而不是为函数提供一个字符串名称。如提供一个如array('class','method')这样的数组,使得可以使用某个对象的方法。
// 静态方法
class MyClass
{
public static function autoload($className)
{
echo 'class autoload自动加载', $className,'<br />';
}
}
spl_autoload_register(array('MyClass', 'autoload'));
// 实例
$myClass = new MyClass();
spl_autoload_register(array('myClass', 'autoload'));
$myClass1 = new MyClass1();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这样,就可以通过**spl_autoload_regsiter()**这个函数注册多个我们自己的 autoload() 函数,当 PHP 找不到类名时,PHP就会调用这个堆栈,然后去调用自定义的 autoload() 函数,实现自动加载功能。