自動載入(phalcon\Loader)轉載請註明來源一、php文件引入 通過 include() 或 require() 函數,可以在PHP程式執行之前在該文件中插入一個文件的內容。區別:處理錯誤的方式不同。include() 函數會生成一個警告(但是腳本會繼續執行),而 require() 函....
自動載入(phalcon\Loader)
轉載請註明來源
一、php文件引入
通過 include() 或 require() 函數,可以在PHP程式執行之前在該文件中插入一個文件的內容。
區別:處理錯誤的方式不同。include() 函數會生成一個警告(但是腳本會繼續執行),而 require() 函數會生成一個致命錯誤(fatal error)(在錯誤發生後腳本會停止執行)
* 正因為在文件不存在或被重命名後腳本不會繼續執行,因此我們推薦使用 require() 而不是 include()。
二、php類自動載入
參考文章:php手冊 和 PHP的類自動載入機制
在php5之前,各php框架實現類的載入,一般要按照某種約定實現一個遍歷目錄,自動載入符合約定條件的文件類或函數。因此在php5之前類的使用並沒有現在頻繁。
在php5之後,當載入php類的時候,如果類所在文件夾並沒有被包含進來或是類名出錯時,Zend引擎會自動調用__autoload函數。__autoload函數需要用戶自己實現。
在php5.1.2版本之後,可以使用spl_autoload_register函數自定義載入處理函數。當沒有調用此函數,預設情況下會使用spl自定義的spl_autoload函數。
1. php自動載入之__autoload
function __autoload($className) { $file = $className . '.php'; if (is_file($file)) { require($file); }else{ echo 'no this ' . $className . ' class file'; } } $demo = new Demo();
事實上,我們可以看到__autoload
至少需要做三件事(“三步走”),它們分別是:
- 根據類名確定類的文件名。
- 確定類文件所在路徑,上例用的是中用的是相對定位,我們的測試文件其實在同一目錄下。
- 將指定類所在文件載入到程式中。
在第一步和第二步中,我們必須約定類名與文件的映射方法,只有這樣我們才能夠依據類名找到其所對應的文件,實現載入。
因此__autoload自動載入中,最重要的就是指定類名與其所在文件的對應關係。當有大量的類需要包含進來的時候,我們只需要確立相應的規則,然後將類名與其對應的文件進行映射,就能夠實現惰性載入(lazy loading)了。
Tip:spl_autoload_register() 提供了一種更加靈活的方式來實現類的自動載入。因此,不再建議使用 __autoload() 函數,在以後的版本中它可能被棄用。
2. php自動載入之spl_autoload_register
引言:如果在一個php系統實現中,使用了很多的其他類庫,這些類庫可能是由不同的工程師進行開發的,因此類名與其所在文件的映射規則不盡相同。這時候如果要實現類庫的自動載入,就必須在__autoload函數中將所有的映射規則全部實現。這就會導致__autoload會非常複雜,甚至無法實現。同時還會使得__autoload函數十分臃腫。為將來系統的維護和性能帶來很大的負面影響。(__autoload的弊端)
spl_autoload_register:
註冊給定的函數作為__autoload的實現。簡單來說就是將函數註冊之SPL的__autoload函數棧中,並移除系統預設的__autload()函數。
function __autoload($className) { echo 'autload class:', $className, '<br />'; } function classLoader($className) { echo 'SPL load class:', $className, '<br />'; } spl_autoload_register('classLoader'); new Test();//結果:SPL load class:Test
Tip:
- 如果在你的程式中已經實現了__autoload()函數,它必須顯式註冊到__autoload()隊列中。因為 spl_autoload_register()函數會將Zend Engine中的__autoload()函數取代為spl_autoload()或spl_autoload_call()。
相比於__autoload只能夠定義一次。spl_autoload_register()函數可以定義多個autoload函數。因為spl_autoload_register創建了autoload函數隊列,該隊列按照定義的先後順序逐個執行。
function __autoload($className) { echo 'autload class:' . $className . '<br />'; } function classLoader($className) { echo 'SPL load class:' . $className . '<br />'; } spl_autoload_register('classLoader'); $demo = new Demo();//結果:SPL load class:Demo
函數說明
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
- autoload_function【可選】添加到自動載入棧的函數。預設為spl_autoload()。
- 還可以調用spl_autoload_register()函數以註冊一個回調函數,而不是為函數提供一個字元串名稱。如提供一個如array('class','method')這樣的數組,使得可以使用某個對象的方法。
- throw【可選】無法成功註冊時,是否拋出異常
- prepend【可選】是否將將該函數添加到隊列之首,而不是隊列的尾部。
備註:SPL自動載入功能是由spl_autoload() ,spl_autoload_register(), spl_autoload_functions() ,spl_autoload_extensions()和spl_autoload_call()函數提供的。
三、Phalcon的類自動載入
Phalcon\Loader 通用類載入器(Universal Class Loader),意在根據協議幫助項目自動載入項目中的類(This component helps to load your project classes automatically based on some conventions)。Phalcon支持四種類載入方式,先後順序分別是註冊類名、註冊命名空間、註冊首碼和註冊文件夾的方式。
Phalcon的預設文件尾碼為php,當然你自己也可以配置(setExtensions())。
1 . 註冊類名
<?php $loader = new \Phalcon\Loader(); $loader->registerClasses( array( "Some" => "library/OtherComponent/Other/Some.php", "Example\Base" => "vendor/example/adapters/Example/BaseClass.php", ) ); $loader->register(); // i.e. library/OtherComponent/Other/Some.php $some = new Some();
- 最快的自動方法
- 不利於維護
具體實現:
- 判斷是否有類被註冊。
- 判斷需要載入的類是否被註冊,如果已註冊則載入其對應路徑文件。
2. 註冊命名空間
<?php $loader = new \Phalcon\Loader(); $loader->registerNamespaces( array( "Example\Base" => "vendor/example/base/", "Example\Adapter" => "vendor/example/adapter/", "Example" => "vendor/example/", ) ); $loader->register(); // vendor/example/adapter/Some.php $some = new Example\Adapter\Some();
使用命名空間或外部庫組織代碼時,你可以利用註冊命名空間的方式來自動載入其包含的庫。
對於命名空間對應的路徑,要其末尾加一個斜杠。
具體實現:
- 判斷是否有命名空間被註冊。
判斷需要載入的類是否已以註冊的命名開始。
例如註冊的命名空間為
"Example\Base" => "vendor/example/base/"
"Example\Base" => "vendor/example/base/" $test1 = new Example\Base\Test();// vendor/example/base/Test.php $test2 = new Example\Test();// 錯誤,無法載入。
名稱處理:1、去掉命名指定空間首碼。2、將命名空間分隔符
\
轉換成文件分隔符/
- 依據文件拓展名構建完整的文件路徑,並判斷該文件是否存在,如該文件存在載入。
3. 註冊首碼
<?php $loader = new \Phalcon\Loader(); $loader->registerPrefixes( array( "Example_Base" => "vendor/example/base/", "Example_Adapter" => "vendor/example/adapter/", "Example_" => "vendor/example/", ) ); $loader->register(); // vendor/example/adapter/Some.php $some = new Example_Adapter_Some();
類似於命名空間,從2.1.0開始phalcon將不再支持首碼。
具體實現:
- 判斷是否有首碼被註冊。
判斷需要載入的類是否已以首碼開始命名。
例如註冊的首碼為
"Example_Base" => "vendor/example/base/"
"Example_Base" => "vendor/example/base/" $test1 = new Example_Base_Test();// vendor/example/base/Test.php $test2 = new Example_Test();// 錯誤,無法載入。
名稱處理:1、去掉類的首碼。2、將首碼分隔符
_
轉換成文件分隔符/
- 依據文件拓展名構建完整的文件路徑,並判斷該文件是否存在,如該文件存在載入。
4. 註冊文件夾
<?php $loader = new \Phalcon\Loader(); $loader->registerDirs( array( "library/MyComponent/", "library/OtherComponent/Other/", "vendor/example/adapters/", "vendor/example/" ) ); $loader->register(); // i.e. library/OtherComponent/Other/Some.php $some = new Some();
可以自動載入註冊目錄下的類文件。但是該方法在性能方面並不被推薦,因為Phalcon將在個文件夾下大量查找與類名相同的文件。在使用註冊目錄自動載入時,要註意註冊目錄的相關性,即將重要的目錄放在前面。
具體實現:
- 將類名中的首碼分隔符
_
或是命名空間分隔符\
替換成文件夾分割符/
- 判斷是否有文件夾被註冊。
依據文件尾碼構建可能的文件路徑
例如註冊的首碼為
"vendor/example/base/"
$test = new Test();// vendor/example/base/Test.php
5. 修改當前策略(Modifying current strategies)
即為當前自動載入數據添加額外的值。
<?php // Adding more directories $loader->registerDirs( array( "../app/library/", "../app/plugins/" ), true );
註冊時添加第二個參數值true,使其與原數組合併。
6. 安全層(Security Layer)
沒有進行任何安全檢查的自動載入器,如下:
<?php //Basic autoloader spl_autoload_register(function($className) { if (file_exists($className . '.php')) { require $className . '.php'; } });
假如我們沒有進行任何安全檢查時,如果誤啟了自動載入器,那麼惡意準備的字元串就回作為參數訪問程式中的重要文件。
<?php //This variable is not filtered and comes from an insecure source $className = '../processes/important-process'; //Check if the class exists triggering the auto-loader if (class_exists($className)) { //... }
Phalcon的做法是刪除任何無用的字元串,減少被攻擊的可能性。
7. 自動載入事件
在下麵的例子中,而不必使用類載入器,使我們獲得調試信息的流程操作:
<?php $eventsManager = new \Phalcon\Events\Manager(); $loader = new \Phalcon\Loader(); $loader->registerNamespaces(array( 'Example\\Base' => 'vendor/example/base/', 'Example\\Adapter' => 'vendor/example/adapter/', 'Example' => 'vendor/example/' )); //Listen all the loader events $eventsManager->attach('loader', function($event, $loader) { if ($event->getType() == 'beforeCheckPath') { echo $loader->getCheckedPath(); } }); $loader->setEventsManager($eventsManager); $loader->register();
Phalcon自動載入支持以下事件:
- beforeCheckClass,自動載入的過程開始前觸發,當返回布爾假可以停止活動操作。
- pathFound,當一個類裝入器定位觸發
- afterCheckClass,自動載入的過程完成後觸發。
8. 註意事項(Troubleshooting)
- 自動載入區分大小寫。
- 命名空間或首碼的方式要比文件夾的方式要快得多。