這篇文章主要介紹了關於PHP中類自動加載的方式,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
最近在學習composer,發現從接觸PHP到現在已經遇到了三種關於PHP中類別的自動載入方式,其中包括PHP自帶的類別的自動載入方式、PHP的第三方的依賴管理工具composer的載入方式以及PHP的Yaf框架下的自動加載方式。本篇部落格主要是針對PHP5自備的載入方式進行詳細介紹,composer和Yaf下類的自動載入將在接下來的時間裡分兩篇和大家一起學習。
像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()函数只适用于获取当前的包含路径。
为了将双手从类的加载方式中解放出来,在PHP5及以后的版本中提供了一个自动加载的机制---autoload。Autoload可以使类在确实被需要的情况下才会被加载进来,也就是所谓的lazy loading,而不是一开始就include或者require所有的类文件。其中PHP提供的自动加载机制又分为两种---__autoload()以及spl_autoload_register()。
在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().
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从哈希表中删除。
这里需要说明的一点是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"); //第三行的另一种写法,不是使用数组的形式完成注册; }
相关推荐:
以上是PHP中類別自動載入的方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!