關於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這個空間下進行相對路徑的加載,是
錯誤
的。
相關文章
相關標籤/搜索