关于php自动加载

自动加载能够说是现代
PHP框架的根基,任何牛逼的框架或者架构都会用到它,它发明出来的理由是啥呢?一个字:
。由于项目越来愈大,相关联的类库文件愈来愈多,咱们不可能再像小项目那样在一个文件中所有手动一个一个
require

如何才能自动加载呢?
PHP 5.2版本更新了
自动加载
须要的一个魔术方法
——
__autoload($class_name)

正是这个神奇的内置魔术函数,才能让咱们这些屌丝偷懒。咱们来看下这个如何使用它。

1. 自动加载的原理以及__autoload的使用
自动加载的原理,就是在咱们
new一个class
的时候,
PHP系统若是找不到你这个类,就会去自动调用本文件中的
__autoload($class_name)
方法,咱们
new的这个class_name 就成为这个方法的参数。因此咱们就能够在这个方法中根据咱们须要new class_name的各类判断和划分就去
require
对应的路径类文件,从而实现自动加载。

咱们先一步步来,看下
__autoload()的自动调用,看个例子:

index.php

[AppleScript]
纯文本查看
复制代码
?
1
$db = new DB ( ) ;

若是咱们不手动导入
DB类,程序可能会报错,说找不到这个类:

[AppleScript]
纯文本查看
复制代码
?
1
Fatal error : Class 'DB' not found in D : \wamp\www\testphp\autoload\ index .php on line 3
那么,咱们如今加入
__autoload()这个方法再看看:

[AppleScript]
纯文本查看
复制代码
?
1
2
3
4
5
6
$db = new DB ( ) ;
function __autoload ( $className )
{
echo $className;
exit ( ) ;
}

根据上面自动加载机制的描述,你分析下会输出什么?
没错:确定是输出:
DB
也就是咱们须要
new 的类的类名。因此,这个时候咱们就能够在__autoload()方法里,根据须要去加载类库文件了。

index.php

[AppleScript]
纯文本查看
复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
$db = new DB ( ) ;
function __autoload ( $className )
{
require $className . '.php';
}
DB.php
class DB
{
public function __construct ( )
{
echo 'Hello DB';
}
}

这样子咱们就很轻松的将咱们须要
new 的class 所有导入了进来,这样子,咱们就能够轻松的new N个class,好比:

[AppleScript]
纯文本查看
复制代码
?
1
2
3
4
5
6
7
8
9
< ?php
function __autoload ( $className )
{
require $className . '.php';
}
$db = new DB ( ) ;
$info = new Info ( ) ;
$gender = new Gender ( ) ;
$ name = new Name ( ) ;

//也是支持静态方法直接调用的

[AppleScript]
纯文本查看
复制代码
?
1
Height : : test ( ) ;
2. spl_autoload_register的使用
小的项目,用
__autoload()
就能实现基本的自动加载了。可是若是一个项目过大,或者须要不一样的自动加载来加载不一样路径的文件,这个时候
__autoload
就悲剧了,缘由是一个项目中仅能有一个这样的
__autoload()
函数,由于 PHP 不容许函数重名,也就是说你不能声明2个
__autoload()
函数文件,不然会报致命错误,我了个大擦,那怎么办呢?放心,你想到的,
PHP开发大神早已经想到。

因此
spl_autoload_register()
这样又一个牛逼函数诞生了,而且取而代之它。
它执行效率更高,更灵活

先看下它如何使用吧:

当咱们去
new一个找不到的class
时,
PHP就会去自动调用sql_autoload_resister注册的函数,这个函数经过它的
参数
传进去:

sql_autoload_resister($param)
这个参数能够有多种形式:

sql_autoload_resister('load_function'); //函数名

sql_autoload_resister(array('load_object', 'load_function')); //类和静态方法

sql_autoload_resister('load_object::load_function'); //类和方法的静态调用

[AppleScript]
纯文本查看
复制代码
?
1
2
3
4
5
6
/ / php 5.3 以后,也能够像这样支持匿名函数了。
spl_autoload_register ( function ( $className ) {
if ( is_file ( '. / lib / ' . $className . '.php' ) ) {
require '. / lib / ' . $className . '.php';
}
} ) ;

index.php

[AppleScript]
纯文本查看
复制代码
?
1
2
3
4
5
6
7
function load 1 ( $className )
{
echo 1 ;
require $className . '.php';
}
spl_autoload_register ( 'load 1 ' ) ; / / 将load 1 函数注册到自动加载队列中。
$db = new DB ( ) ; / / 找不到DB类,就会自动去调用刚注册的load 1 函数了

上面就是实现了自动加载的方式,咱们一样也能够用
类加载
的方式调用,可是必须是
static方法

[AppleScript]
纯文本查看
复制代码
?
01
02
03
04
05
06
07
08
09
10
11
class autoloading {
/ / 必须是静态方法,否则报错
public static function load ( $className )
{
require $className . '.php';
}
}
/ / 2 种方法均可以
spl_autoload_register ( array ( 'autoloading' , 'load' ) ) ;
spl_autoload_register ( 'autoloading : : load' ) ;
$db = new DB ( ) ; / / 会自动找到

须要注意的是,若是你同时使用
spl_autoload_register和__autoload,__autoload会失效!!!
再说了,原本就是替换它的,就一心使用spl_autoload_register就行了。

3. 多个spl_autoload_register的使用
spl_autoload_register是能够屡次重复使用的,这一点正是解决了__autoload的短板,那么若是一个页面有多个,
执行顺序是按照注册的顺序,一个一个往下找,若是找到了就中止。

咱们来看下这个例子,
DB.php就在本目录下,Info.php在/lib/目录下。

[AppleScript]
纯文本查看
复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function load 1 ( $className )
{
echo 1 ;
if ( is_file ( $className . '.php' ) ) {
require $className . '.php';
}
}
function load 2 ( $className )
{
echo 2 ;
if ( is_file ( '. / app / ' . $className . '.php' ) ) {
require '. / app / ' . $className . '.php';
}
}
function __autoload ( $className )
{
echo 3 ;
if ( is_file ( '. / lib / ' . $className . '.php' ) ) {
require '. / lib / ' . $className . '.php';
}
}
/ / 注册了 3
spl_autoload_register ( 'load 1 ' ) ;
spl_autoload_register ( 'load 2 ' ) ;
spl_autoload_register ( '__autoload' ) ;
$db = new DB ( ) ; / / DB就在本目录下
$info = new Info ( ) ; / / Info 在 / lib / Info.php

咱们注册了
3个自动加载函数。执行结果是啥呢?

[AppleScript]
纯文本查看
复制代码
?
1
2
1 Hello DB
123 Hello Info

咱们分析下:

1.
new DB的时候,就按照注册顺序,先去找load1()函数了,发现找到了,就中止了,因此输出1 Hello Word

2.
new Info的时候,先是安装注册顺序,先找load1(), 因此输出了1,发现没找到,就去load2()里面去找,因此输出了2,仍是没这个文件,就去__autoload()函数里找,因此,先输出了3,再输出Hello Info

注意,前面说过,
spl_autoload_register使用时,__autoload会无效,有时候,咱们但愿它继续有效,就能够也将它注册进来,就能够继续使用。

咱们能够打印
spl_autoload_functions()
函数,来显示一共注册了多少个自动加载:

[AppleScript]
纯文本查看
复制代码
?
1
2
3
4
5
6
var_dump ( spl_autoload_functions ( ) ) ;
/ / 数组的形式输出
array ( size = 3 )
0 = > string 'load 1 ' ( length = 5 )
1 = > string 'load 2 ' ( length = 5 )
2 = > string '__autoload' ( length = 10 )

4. spl_autoload_register自动加载+namespace命名空间 的使用
前面已经说过,自动加载如今是
PHP现代框架的基石,基本都是spl_autoload_register来实现自动加载。namespace也是使用比较多的。因此spl_autoload_register + namespace 就成为了一个主流。根据PSR-0的规范,namespace命名已经很是规范化,因此用namespace就能找到详细的路径,从而找到类文件。

咱们举例子来看下:

[AppleScript]
纯文本查看
复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
AutoLoading\loading
< ?php
namespace AutoLoading;
class loading {
public static function autoload ( $className )
{
/ / 根据PSR - O的第 4 点 把 \ 转换层(目录风格符) DIRECTORY_SEPARATOR ,
/ / 便于兼容Linux文件找。Windows 下( / 和 \)是通用的
/ / 因为namspace 很规格,因此直接很快就能找到
$fileName = str_replace ( '\\' , DIRECTORY_SEPARATOR , DIR . '\\'. $className ) . '.php';
if ( is_file ( $fileName ) ) {
require $fileName;
} else {
echo $fileName . ' is not exist'; die;
}
}
}

上面就是一个自动加载的核心思想方法。下面咱们就来
spl_autoload_register
来注册这个函数:

index.php

[AppleScript]
纯文本查看
复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
< ?php
/ / 定义当前的目录绝对路径
define ( 'DIR' , dirname ( __FILE__ ) ) ;
/ / 加载这个文件
require DIR . ' / loading.php';
/ / 采用`命名空间`的方式注册。php 5.3 加入的
/ / 也必须是得是static静态方法调用,而后就像加载namespace的方式调用,注意:不能使用use
spl_autoload_register ( "\\AutoLoading\\loading::autoload" ) ;
/ / 调用三个namespace类
/ / 定位到Lib目录下的Name.php
Lib\Name : : test ( ) ;
/ / 定位到App目录下Android目录下的Name.php
App\Android\Name : : test ( ) ;
/ / 定位到App目录下Ios目录下的Name.php
App\Ios\Name : : test ( ) ;

因为咱们是采用
PSR-O
方式来定义
namespace的命名的,因此很好的定位到这个文件的在哪一个目录下了。很爽。对不对。

[AppleScript]
纯文本查看
复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
APP\Android\Name
namespace App\Android;
class Name
{
public function __construct ( )
{
echo __NAMESPACE__ . "<br>" ;
}
public static function test ( )
{
echo __NAMESPACE__ . ' static function test < br > ';
}
}

因此就会很容易找到文件,并输出:

[AppleScript]
纯文本查看
复制代码
?
1
2
3
Lib static function test
App\Android static function test
App\Ios static function test

好了。基本自动加载的东西就讲完了。很实用的东西。

4. 同命名空间下的相互调用
在平时咱们使用命令空间时,有时候多是在同一个命名空间下的
2个类文件在相互调用。这个时候就要注意,在自动调用的问题了。

好比
Lib\Factory.php 和 Lib\Db\MySQL.php

我想在
Lib\Factory.php 中调用 Lib\Db\MySQL.php。怎么调用呢?如下是错误的示范:


[AppleScript]
纯文本查看
复制代码
?
1
2
new Lib\Db\MySQL ( ) ;
/ / 报错,提示说 D : \wamp\www\testphp\module\Lib\Lib\Db\MySQL.php is not exist

看到没?这种方式是在
Lib\命名空间的基础上来加载的。因此会加载2个Lib。这种方式至关于相对路径在加载。

正确的作法是,若是是在同一个命名空间下平级的
2个文件。
能够直接调用,不用命名空间。

[AppleScript]
纯文本查看
复制代码
?
1
2
new MySQL ( ) ; / / 直接这样就能够了。
new Db\MySQL ( ) ; / / 若是有个Db文件夹 , 就这样。

还有一种方法就是使用
use
。使用user就能够带上Lib了。
use
使用的是绝对路径。

[AppleScript]
纯文本查看
复制代码
?
1
2
use Lib\Db\MySQL;
new MySQL ( ) ;

我想在
Lib\Db\MySQL.php 中调用 Lib\Register.php。怎么调用呢?

应该这样

[AppleScript]
纯文本查看
复制代码
?
1
2
use Lib\Register;
Register : : getInstance ( ) ;

由于如今已经在
Lib\Db这样一个命名空间了,若是你不用
use
,而是使用
Lib\Register::getInstance()
或者使用
Register::getInstance()
的话。将是在
Lib\Db这个空间下进行相对路径的加载,是
错误
的。
相关文章
相关标签/搜索