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中的一个魔术方法,在代码中当调用不存在的类时会自动调用该方法。

语法

1
_autoload ( string $class ) : void  // class: 待加载的类名

用法

1
2
3
4
5
6
7
8
<?php

function __autoload($className)
{
echo 'autoload自动加载', $className,'<br />';
}

$myClass1 = new MyClass1();

如果调用某个未经include/require的类,在报错之前,还可以__autoload()函数得到一次介入机会,系统会调用__autoload()函数,并把类名传给__autoload()函数,就可以在__autoload()里加载需要的类。

例如:

1
2
3
4
5
<?php

function __autoload($classname) {
require_once ($classname . ".class.php");
}

因此,当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在__autoload()函数中,将类名与实际的磁盘文件对应起来,就可以实现lazy loading的效果。

缺陷

但现在问题来了,假如在一个系统的实现中,假如需要使用很多其它的类库,这些类库可能是由不同的开发工程师开发,其类名与实际的磁盘文件的映射规则不尽相同。这时假如要实现类库文件的自动加载,就必须在__autoload()函数中将所有的映射规则全部实现,因此__autoload()函数有可能会非常复杂,甚至无法实现。最后可能会导致__autoload()函数十分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很大的负面影响。所有问题都出现在__autoload() 是全局函数只能定义一次 ,那么如何来解决这个问题呢?答案就是使用一个 __autoload调用堆栈 ,不同的映射关系写到不同的 __autoload函数 中去,然后统一注册统一管理,这个就是 PHP5 引入的 SPL Autoload

spl_autoload_register

spl_autoload_register()SPL(Standard PHP Library)提供的方法。

语法

1
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()函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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();

结果输出:

1
2
3
4
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

还可以调用spl_autoload_register()函数以注册一个回调函数,而不是为函数提供一个字符串名称。如提供一个如array(‘class’,’method’)这样的数组,使得可以使用某个对象的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 静态方法
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();

这样,就可以通过spl_autoload_regsiter()这个函数注册多个我们自己的 autoload() 函数,当 PHP 找不到类名时,PHP就会调用这个堆栈,然后去调用自定义的 autoload() 函数,实现自动加载功能。

有用就打赏一下作者吧!