PHP中類自動加載的方式

最近在學習composer,發現從接觸PHP到如今已經遇到了三種關於PHP中類的自動加載方式,這其中包括PHP自帶的類的自動加載方式、PHP的第三方的依賴管理工具composer的加載方式以及PHP的Yaf框架下的自動加載方式。本篇博客主要是針對PHP5自帶的加載方式進行詳細介紹,composer和Yaf下類的自動加載將在接下來的時間裏分兩篇和你們一塊兒學習。php

      1.手動加載方式html

  像C和C++等語言,在PHP中須要使用另外一個文件中的相關的類、方法時,可使用include、include_once、require或者require_once將所用的文件包含進工程裏面。其中,四者的區別以下。設計模式

  • include將套用一個文件,若是文件不存在,則給出一個提示,跳過繼續執行;
  • include_once也是套用一個文件,可是隻會套用一次,若是文件不存在,則繼續執行;
  • require表示套用一個文件,若是文件不存在,則中斷程序的執行;
  • require_once也是套用一個文件,且只會套用一次,若是文件不存在,則中斷程序的執行;

  以上四種方式是須要什麼文件的時候,手動在程序當中包含進文件。這在項目的規模比較小的時候,是能夠的;可是隨着項目規模的擴大,要經過手動的方式加載每一個文件所須要的類簡直是一場噩夢。數組

  爲了省事,在加載的時候能夠經過set_include_path()設置加載的路徑,一樣也能夠經過get_include_path()獲取加載的路徑。關於set_include_path()和get_include_path(),我也是剛剛接觸,這裏只對set_include_path()做簡要的介紹,之後遇到問題再加以補充。composer

  首先,set_include_path()是在腳本中動態的對php.ini中的include_path進行動態的修改,而這個include_path就是對include和require(下文中若是不進行特別的說明,include表明include和include_once,require表明着require和require_once)的路徑進行設置,或者說是預約義。假如,咱們在一個main.php文件中須要使用projname/home/lib/mylib/test文件夾下的a.php、b.php、c.php......,若是沒有設置包含的路徑的話,那麼寫成以下的形式:框架

1
2
3
4
5
6
< ? php
 
         include ( "projname/home/lib/mylib/test/a.php" );
         include ( "projname/home/lib/mylib/test/b.php" );
         include ( "projname/home/lib/mylib/test/c.php" );
       ......

  這樣,每一個include都須要包含絕對路徑,顯得很麻煩。若是在須要被包含的文件以前加上set_include_path(「projname/home/lib/mylib/test」),那麼就能夠寫成以下所示的形式:函數

1
2
3
4
5
6
7
< ? php
 
     set_include_path( "projname/home/lib/mylib/test" );
     include ( "a.php" );
     include ( "b.php" );
     include ( "c.php" );
     ......

  相比於第一種費時費力的寫法,第二種明顯省去了不少的時間,可是仍然是要將每一個文件包含進來,只是簡化了包含的路徑而已。固然,上面所說的狀況是所須要的文件都存在於一個文件夾中,若是文件存在於不一樣的文件夾中,那麼能夠添加多條的set_include_path()語句,此時若是include或者require中的文件包含的文件名在多個目錄下出現,那麼只會包含最早出如今set_include_path目錄中的文件;若是全部的set_include_path指定的文件夾中都沒有對應的文件,而文件名剛好出如今當前的文件夾中,則直接包含當前目錄下的對應的文件。工具

  get_include_path()函數只適用於獲取當前的包含路徑。 學習

  2._autoload和spl_autoload_register()自動加載方式ui

  爲了將雙手從類的加載方式中解放出來,在PHP5及之後的版本中提供了一個自動加載的機制---autoload。Autoload可使類在確實被須要的狀況下才會被加載進來,也就是所謂的lazy loading,而不是一開始就include或者require全部的類文件。其中PHP提供的自動加載機制又分爲兩種---__autoload()以及spl_autoload_register()。

  1). __autoload機制

  在PHP5中運行程序的過程當中,若是發現某一個類並無被包含進來,那麼就會運行__autoload自動加載機制,將所須要的類加載進來。其寫法以下:

1
2
3
4
5
6
7
8
9
10
< ? php
 
     public function  __autoload( $classname ) {
         $fileName = $classname . "php" ;
         if (file_exist( $fileName )) {
             require_once ( "$fileName" );
         } else {
             echo $fileName . " doesn't exist!"
         }
     }

  根據這個程序寫法,咱們能夠獲得以下的結論:保證自動加載機制的的原則就是要使得類名和文件名具備一種對應關係,類名+後綴構成了這個類所在的文件的名字。若是這個文件確實存在,那麼就根據$fileName將該類加載進來。若是文件不存在,則提示用戶,文件不存在。總的來講自動加載機制包括三個步驟:

  • 根據類名肯定文件名,也就是肯定一種類名和文件名之間的統一對應規則;
  • 根據文件名在磁盤上找到相應的對應文件(例子中是最簡單的狀況,就是類與調用他們的PHP文件都在同一個目錄下);若是不在同一個目錄下,那麼可使用set_include_path()指定要加載的路徑;
  • 將磁盤文件加載到文件系統中,這一步只是用通常的include和require包含相應的類文件;

  __autoload()實現類的自動加載的原則就是:類名和文件名之間具備一種統一的對應關係,這是在一個系統中實現__autoload的關鍵所在。可是一個系統多是有不一樣的人員所開發,若是在開發以前沒有約定統一的標準,則可能存在不一樣的對應規則,致使須要在__autoload()中實現多種加載規則,那麼可能致使__autoload()函數很是的臃腫。爲了解決這個這個問題,PHP還提供了一個自動加載機制---spl_autoload_register().

  2). spl_autoload_register()機制

  SPL是Standard PHP Library(標準PHP庫)的縮寫,是PHP5引入的一個擴展庫。SPL autoload是經過將函數指針autoload_func指向自動裝載函數實現的。SPL具備兩個不一樣的自動裝載函數,分別是spl_autoload和spl_autoload_call,經過將autoload_fun指向這兩個不一樣的加載函數地址能夠實現不一樣的自動加載機制。

  • spl_autoload

  spl_autoload是SPL實現的默認的自動加載函數,是一個能夠接受兩個參數的函數。其中第一個函數爲$class_name,表示要加載的類名;第二個參數是$file_extension爲可選參數,表示類文件的擴展名。$file_extension中能夠指定多個擴展名,擴展名之間用分號隔開便可,不指定擴展名,則使用默認的擴展名.inc或者.php。spl_autoload首先將$class_name變爲小寫,而後在全部的include_path中搜索$ class_name.inc或者$class_name.php文件。若是找到對應的文件,就加載對應的類。其實能夠手動的使用spl_autoload("xxxx",".php")來實現xxxx類的加載。這其實和require/include差很少,可是,spl_autoload相對來講靈活一點,由於能夠指定多個擴展名。

  前面說到,spl_autoload_register中包含的函數指針autoload_func用於指定要使用的加載函數。那麼,咱們必須將對應的函數地址賦值給autoload_func,spl_autoload_register()正實現了給函數指針autoload_func賦值的功能。若是spl_autoload_register()函數中不含有任何的參數,則默認是將spl_autoload()賦值給autoload_func.

  • spl_autoload_call 

  SPL模塊的內部其實還存在着一個autoload_functions,其本質上是一個哈希表,或者爲了直觀的理解,咱們將其想像成一個容器,裏面的各個元素都是指向加載函數的指針。spl_autoload_call的實現機制其實也比較簡單,按照必定的順序遍歷這個容器,執行裏面的函數指針指向的加載函數,每執行一個指針以後都會檢查所須要的類是否已經完成加載。若是完成了加載,則退出。不然繼續接着向下執行。若是執行完全部的加載函數以後,所須要的類仍然沒有完成加載,則spl_autoload_call()直接退出。這也就是說即便使用了autoload機制,也不必定可以完成類的加載,其關鍵在於看你如何建立你的自動加載函數。

  既然,存在一個autoload_functions,那麼如何將建立的自動加載函數添加到其中呢?spl_autoload同樣,一樣使用spl_autoload_register()將加載函數註冊到autoload_functions中。固然能夠經過spl_autoload_unregister()函數將已經註冊的函數從autoload_functions從哈希表中刪除。這和前面所寫過的工廠設計模式是一致的,詳見:http://www.cnblogs.com/yue-blog/p/5771352.html。

  這裏須要說明的一點是spl_autoload_register實現自動加載的順序。spl_autoload的自動加載順序爲:首先判斷autoload_func是否爲空,若是autoload_func爲空,則查看是否認義了__autoload函數,若是沒有定義,則返回,並報錯;若是定義了__autoload()函數,則返回加載的結果。若是autoload_func不爲空,直接執行autoload_func指針指向的函數,不會檢查__autoload是否認義。也就是說優先使用spl_autoload_register()註冊過的函數。

  根據以上介紹,若是autoload_func爲非空是就不能自動執行__autoload()函數了。若是想在使用spl_autoload_register()函數的狀況下,依然可使用__autoload()函數,則能夠將__autoload函數經過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
21
22
23
24
25
<? php
 
     /**
     * @ 普通函數的調用方法,能夠調用後綴名分別爲.php和.class.php的類文件
     */
     function loadFielEndOfPhp( $classname ) {
         $fileName = $classname . ".php" ;
         if (file_exist( $fileName )) {
             require_once ( "$fileName" );
         } else {
             echo $fileName . " doesn't exist!"
         }
     }
 
     function loadFielEndOfClassPhp( $classname ) {
         $fileName = $classname . ".class.php" ;
         if (file_exist( $fileName )) {
             require_once ( "$fileName" );
         } else {
             echo $fileName . " doesn't exist!"
         }
     spl_autoload_register( "loadFielEndOfPhp" );
     spl_autoload_register( "loadFielEndOfClassPhp" );
 
}

  類中靜態的加載函數的註冊方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<? php
 
     /**
     * @ 類中靜態成員函數的調用方法,可調用後綴名爲.php和.class.php的文件
     */
     class test {
         public static function loadFielEndOfPhp( $classname ) {
             $fileName = $classname . ".php" ;
             if (file_exist( $fileName )) {
                 require_once ( "$fileName" );
             }
             else {
                 echo $fileName . " doesn't exist!"
             }
         }
 
         public static function loadFielEndOfClassPhp( $classname ) {
             $fileName = $classname . ".class.php" ;
             if (file_exist( $fileName )) {
                 require_once ( "$fileName" );
             }
             else {
                 echo $fileName . " doesn't exist!"
             }
     }
     
     spl_autoload_register( array ( "test" , "loadFielEndOfPhp" ));
     //spl_autoload_register("test::loadFielEndOfPhp");         //上一行的另外一種寫法,不是使用數組的形式完成註冊;
     spl_autoload_register( array ( "test" , "loadFielEndOfClassPhp" ));
     //spl_autoload_register("test::loadFielEndOfClassPhp");    //第三行的另外一種寫法,不是使用數組的形式完成註冊;
 
}
相關文章
相關標籤/搜索