php自動載入原理 sql_autoload_register PSR-4 ...
php的自動載入
自動載入原理
我們使用另一個文件定義的一個class的時候,傳統的情況下需要require XXX.php
//Good.php
<?php
class Good{
//......
}
//use.php
<?php
require 'Good.php'; //包含進來文件
$good = new Good(); //現在可以使用Good類
但是寫require真的很煩!!現在可以使用一個自動載入函數,發現類沒有被定義的時候自動根據我們定義的規則幫我們去require需要的文件。
//Good.php
<?php
class Good{
//......
}
//use.php
<?php
function __autoload($className){
require $className . '.php'; //定義包含的規則
}
$good = new Good(); //可以使用Good類,發現沒有定義,會自動調用__autoload函數,現在'Good'作為參數傳入了__autoload函數,去包含需要的文件
spl_autoload_register
__autoload函數不能解決所有的問題,現在的項目已經非常複雜,光一個autoload函數已經不能滿足需要了,因為可能不同的模塊使用了不同的自動載入規則。
spl_autoload_register將函數註冊到SPL autoload函數隊列中,它實際上創建了 __autoload 函數的隊列
,按定義時的順序逐個執行。相比之下,__ autoload() 只可以定義一次。
函數原型
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
- autoload_function
欲註冊的自動裝載函數。如果沒有提供任何參數,則自動註冊 autoload 的預設實現函數spl_autoload()。
- throw
此參數設置了 autoload_function 無法成功註冊時, spl_autoload_register()是否拋出異常。
- prepend
如果是 true,spl_autoload_register() 會添加函數到隊列之首,而不是隊列尾部。
函數使用
sql_autoload_resister('load_function'); //函數名
sql_autoload_resister(array('load_object', 'load_function')); //類和靜態方法
sql_autoload_resister('load_object::load_function'); //類和方法的靜態調用
//php 5.3之後,也可以像這樣支持匿名函數了。
spl_autoload_register(function($className){
if (is_file('./lib/' . $className . '.php')) {
require './lib/' . $className . '.php';
}
});
note:使用了spl_autoload_register之後原來的__autoload函數就失效了,如果需要繼續使用,需要顯示的註冊它
if (function_exists('__autoload')) {
spl_autoload_register('__autoload');
}
//。。。。。。
spl_autoload_register('your_autoload_function'); //現在註冊另一個
是否繼續在自動載入函數隊列中查找不取決於載入函數的返回值和是否require了文件,只有真正require需要的文件才會停止
//Good.php位於app文件夾下
<?php
class Good{
//......
}
//Another.php,位於use同級文件夾下
<?php
class Another{
}
//use.php
<?php
function load1($className)
{
echo "load1\n";
if (file_exists($className.'.php')) {
echo 'found: '.$className.PHP_EOL;
require $className;
} else {
echo 'not found: '.$className.PHP_EOL;
}
}
function load2($className)
{
echo "load2\n";
require 'Another.php';
echo 'require another class'.PHP_EOL;
return true;
}
function load3($className)
{
echo "load3\n";
if (file_exists('app/'.$className.'.php')) {
echo 'found '.$className.PHP_EOL;
require 'app/'.$className.'.php';
} else {
echo 'not found: '.$className.PHP_EOL;
}
}
spl_autoload_register('load1');
spl_autoload_register('load2');
spl_autoload_register('load3');
$a = new Good();
輸出如下
load1
not found: Good
load2
require another class
load3
found Good
可以使用spl_autoload_functions()
獲得註冊的所有函數
Array
(
[0] => load1
[1] => load2
[2] => load3
)
取消註冊的自動載入函數spl_autoload_unregister
spl_autoload_unregister('load1');
spl_autoload()是__autoload()函數的預設實現
提供了autoload()的一個預設實現。如果不使用任何參數調用 spl_autoload_register() 函數,則以後在進行 autoload() 調用時會自動使用此函數。
自動載入和命名空間
PSR-4自動載入規範
完整的規範請參考PSR-4: Autoloader
標準的類名應該具有下列形式
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
需要強調的:
- 完整的類名中任意一部分中的下滑線都是沒有特殊含義的
- 所有類名都必須是大小寫敏感的
- 所有類名都必須是大小寫敏感的
當根據命名空間載入文件時候:
- 完整的類名中,去掉最前面的命名空間分隔符,前面連續的一個或多個命名空間和子命名空間,作為“命名空間首碼”,其必須與至少一個“文件基目錄”相對應。
- 緊接命名空間首碼後的子命名空間必須與相應的”文件基目錄“相匹配,其中的命名空間分隔符將作為目錄分隔符。
- 末尾的類名必須與對應的以
.php
為尾碼的文件同名。 - 自動載入器(autoloader)的實現一定不能拋出異常、一定不能觸發任一級別的錯誤信息以及不應該有返回值。
例子
完整類名 | 命名空間首碼 | 文件基目錄 | 文件路徑 |
---|---|---|---|
\Acme\Log\Writer\File_Writer | Acme\Log\Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php |
\Aura\Web\Response\Status | Aura\Web | /path/to/aura-web/src/ | /path/to/aura-web/src/Response/Status.php |
\Symfony\Core\Request | Symfony\Core | ./vendor/Symfony/Core/ | ./vendor/Symfony/Core/Request.php |
\Zend\Acl | Zend | /usr/includes/Zend/ | /usr/includes/Zend/Acl.php |
實現示例
採用了匿名函數的方式
<?php
/**
* An example of a project-specific implementation.
*
* After registering this autoload function with SPL, the following line
* would cause the function to attempt to load the \Foo\Bar\Baz\Qux class
* from /path/to/project/src/Baz/Qux.php:
*
* new \Foo\Bar\Baz\Qux;
*
* @param string $class The fully-qualified class name.
* @return void
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Foo\\Bar\\';
// base directory for the namespace prefix
$base_dir = __DIR__ . '/src/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
//下邊這一行很關鍵
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});
參考資料: