最近在學習composer,發現從接觸PHP到現在已經遇到了三種關於PHP中類別的自動載入方式,這其中包括PHP自帶的類別的自動載入方式、PHP的第三方的依賴管理工具composer的載入方式以及PHP的Yaf框架下的自動載入方式。本篇部落格主要是針對PHP5自備的載入方式進行詳細介紹,composer和Yaf下類的自動載入將在接下來的時間裡分兩篇和大家一起學習。
1.手動載入方式
像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()作簡要的介紹,以後遇到問題再加以補充。
首先,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......,如果沒有設定包含的路徑的話,那麼寫成如下的形式:
<?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”),那麼就可以寫成如下所示的形式:
<?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()自動載入方式
為了將雙手從類別的載入方式中解放出來,在PHP5及以後的版本中提供了一個自動載入的機制-- -autoload。 Autoload可以讓類別在確實被需要的情況下才會被載入進來,也就是所謂的lazy loading,而不是一開始就include或者require所有的類別檔案。其中PHP提供的自動載入機制又分為兩種---autoload()以及spl_autoload_register()。
1). autoload機制
在PHP5中運行程式的過程中,如果發現某一個類別並沒有被包含進來,那麼就會運行autoload自動載入機制,將所需要的類別載入進來。其寫法如下:
<?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_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())。下面的程式碼範例分別說明如何註冊普通的方法和類別的靜態公有方法。 ###
普通函数的注册方法。
<?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"); }
类中静态的加载函数的注册方法。
<?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"); //第三行的另一种写法,不是使用数组的形式完成注册; }
以上是3種php類別自動載入的方式實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!