如果您想要在命名空間或類別內宣告 __autoload 函式,請使用 spl_autoload_register() 函式來註冊它,它就能正常運作。
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
為了這些解析規則的目的,以下是一些重要的定義
這是一個沒有命名空間分隔符號的識別符號,例如 Foo
這是一個帶有命名空間分隔符號的識別符號,例如 Foo\Bar
這是一個以命名空間分隔符號開頭且帶有命名空間分隔符號的識別符號,例如 \Foo\Bar
。命名空間 \Foo
也是一個完全限定名稱。
這是以 namespace
開頭的識別符號,例如 namespace\Foo\Bar
。
命名空間解析規則如下:
\A\B
解析為 A\B
。
namespace
替換為當前命名空間的名稱。如果名稱出現在全域命名空間中,則移除 namespace\
前綴。例如,在命名空間 X\Y
內的 namespace\A
解析為 X\Y\A
。在全域命名空間內的相同名稱解析為 A
。
A\B\C
匯入為 C
,則名稱 C\D\E
會轉換為 A\B\C\D\E
。
A\B
內的名稱 C\D\E
會解析為 A\B\C\D\E
。
use A\B\C;
之後,使用 new C()
會解析為名稱 A\B\C()
。同樣地,在 use function A\B\foo;
之後,使用 foo()
會解析為名稱 A\B\foo
。
A\B
內的 new C()
會解析為名稱 A\B\C
。
A\B
中,以下說明如何解析對函式 foo()
的呼叫:
A\B\foo()
。
foo()
。
範例 #1:命名空間解析說明
<?php
namespace A;
use B\D, C\E as F;
// function calls
foo(); // first tries to call "foo" defined in namespace "A"
// then calls global function "foo"
\foo(); // calls function "foo" defined in global scope
my\foo(); // calls function "foo" defined in namespace "A\my"
F(); // first tries to call "F" defined in namespace "A"
// then calls global function "F"
// class references
new B(); // creates object of class "B" defined in namespace "A"
// if not found, it tries to autoload class "A\B"
new D(); // using import rules, creates object of class "D" defined in namespace "B"
// if not found, it tries to autoload class "B\D"
new F(); // using import rules, creates object of class "E" defined in namespace "C"
// if not found, it tries to autoload class "C\E"
new \B(); // creates object of class "B" defined in global scope
// if not found, it tries to autoload class "B"
new \D(); // creates object of class "D" defined in global scope
// if not found, it tries to autoload class "D"
new \F(); // creates object of class "F" defined in global scope
// if not found, it tries to autoload class "F"
// static methods/namespace functions from another namespace
B\foo(); // calls function "foo" from namespace "A\B"
B::foo(); // calls method "foo" of class "B" defined in namespace "A"
// if class "A\B" not found, it tries to autoload class "A\B"
D::foo(); // using import rules, calls method "foo" of class "D" defined in namespace "B"
// if class "B\D" not found, it tries to autoload class "B\D"
\B\foo(); // calls function "foo" from namespace "B"
\B::foo(); // calls method "foo" of class "B" from global scope
// if class "B" not found, it tries to autoload class "B"
// static methods/namespace functions of current namespace
A\B::foo(); // calls method "foo" of class "B" from namespace "A\A"
// if class "A\A\B" not found, it tries to autoload class "A\A\B"
\A\B::foo(); // calls method "foo" of class "B" from namespace "A"
// if class "A\B" not found, it tries to autoload class "A\B"
?>
這裡提到的「自動載入」一詞不應與用於自動載入物件的 __autoload 函式混淆。關於 __autoload 和命名空間的解析,我想分享以下經驗:
->假設您有以下目錄結構:
- 根目錄
| - loader.php
| - ns
| - foo.php
->foo.php
<?php
namespace ns;
class foo
{
public $say;
public function __construct()
{
$this->say = "bar";
}
}
?>
-> loader.php
<?php
//全域空間 <--
function __autoload($c)
{
require_once $c . ".php";
}
class foo extends ns\foo // ns\foo 會在此處載入
{
public function __construct()
{
parent::__construct();
echo "<br />foo" . $this->say;
}
}
$a = new ns\foo(); // ns\foo 也能在此正確載入 ns/foo.php。
echo $a->say; // 如預期印出 bar。
$b = new foo; // 正確印出 foobar。
?>
如果您的目錄/檔案與命名空間/類別保持一致,則物件 __autoload 可以正常運作。
但是...如果您嘗試賦予 loader.php 一個命名空間,顯然會出現致命錯誤。
我的範例只有一個層級的目錄,但我已經用一個非常複雜和更深層的結構進行了測試。希望對大家有所幫助。
乾杯!
在使用命名空間和使用(自定義或基本)自動載入結構時,魔術函數 __autoload 必須定義在全域範圍內,而不是在命名空間內,也不是在其他函數或方法內。
<?php
namespace Glue {
/**
* 在此類別中定義您的自定義結構和
* 自動載入演算法。
*/
class Import
{
public static function load ($classname)
{
echo '正在自動載入類別 '.$classname."\n";
require_once $classname.'.php';
}
}
}
/**
* 在全域命名空間中定義函數 __autoload。
*/
namespace {
function __autoload ($classname)
{
\Glue\Import::load($classname);
}
}
?>
提到的檔案系統類比在一個重要的點上失敗了
命名空間解析*僅*在宣告時有效。編譯器會將所有命名空間/類別參考固定為絕對路徑,就像建立絕對符號連結一樣。
您不能期望相對符號連結,它應該在存取期間 -> 在 PHP 執行期間進行評估。
換句話說,命名空間的解析方式類似 `__CLASS__` 或 `self::`,是在解析時期進行的。但*並非*像 `static::` 那樣進行後期靜態綁定,在執行時期才解析為目前的類別。
所以你無法執行以下程式碼
namespace Alpha;
class Helper {
public static $Value = "ALPHA";
}
class Base {
public static function Write() {
echo Helper::$Value;
}
}
namespace Beta;
class Helper extends \Alpha\Helper {
public static $Value = 'BETA';
}
class Base extends \Alpha\Base {}
\Beta\Base::Write(); // 預期會輸出 "BETA",因為這是執行時期的命名空間上下文。
如果你把 Write() 函式複製到 \Beta\Base 中,它就會如預期般運作。
這裡提到的「自動載入」一詞不應與用於自動載入物件的 __autoload 函式混淆。關於 __autoload 和命名空間的解析,我想分享以下經驗:
->假設您有以下目錄結構:
- 根目錄
| - loader.php
| - ns
| - foo.php
->foo.php
<?php
namespace ns;
class foo
{
public $say;
public function __construct()
{
$this->say = "bar";
}
}
?>
-> loader.php
<?php
//全域空間 <--
function __autoload($c)
{
require_once $c . ".php";
}
class foo extends ns\foo // ns\foo 會在此處載入
{
public function __construct()
{
parent::__construct();
echo "<br />foo" . $this->say;
}
}
$a = new ns\foo(); // ns\foo 也能在此正確載入 ns/foo.php。
echo $a->say; // 如預期印出 bar。
$b = new foo; // 正確印出 foobar。
?>
如果您的目錄/檔案與命名空間/類別保持一致,則物件 __autoload 可以正常運作。
但是...如果您嘗試賦予 loader.php 一個命名空間,顯然會出現致命錯誤。
我的範例只有一個層級的目錄,但我已經用一個非常複雜和更深層的結構進行了測試。希望對大家有所幫助。
乾杯!
命名空間可能不區分大小寫,但自動載入器通常會區分。
為了方便起見,請保持程式碼的大小寫與檔案名稱一致,並且不要讓自動載入器過於複雜。
大多數情況下,像這樣的程式碼就足夠了
<?php
namespace org\example;
function spl_autoload($className)
{
$file = new \SplFileInfo(__DIR__ . substr(strtr("$className.php", '\\', '/'), 11));
$path = $file->getRealPath();
if(empty($path))
{
return false;
}
else
{
return include_once $path;
}
}
\spl_autoload_register('\org\example\spl_autoload');
?>