如果您想在命名空间或类中声明一个 __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;
// 函数调用
foo(); // 首先尝试调用在 "A" 命名空间中定义的 "foo"
// 然后调用全局函数 "foo"
\foo(); // 调用在全局范围内定义的函数 "foo"
my\foo(); // 调用在 "A\my" 命名空间中定义的函数 "foo"
F(); // 首先尝试调用在 "A" 命名空间中定义的 "F"
// 然后调用全局函数 "F"
// 类引用
new B(); // 创建在 "A" 命名空间中定义的 "B" 类的对象
// 如果未找到,则尝试自动加载类 "A\B"
new D(); // 使用导入规则,创建在 "B" 命名空间中定义的 "D" 类的对象
// 如果未找到,则尝试自动加载类 "B\D"
new F(); // 使用导入规则,创建在 "C" 命名空间中定义的 "E" 类的对象
// 如果未找到,则尝试自动加载类 "C\E"
new \B(); // 创建在全局范围内定义的 "B" 类的对象
// 如果未找到,则尝试自动加载类 "B"
new \D(); // 创建在全局范围内定义的 "D" 类的对象
// 如果未找到,则尝试自动加载类 "D"
new \F(); // 创建在全局范围内定义的 "F" 类的对象
// 如果未找到,则尝试自动加载类 "F"
// 来自另一个命名空间的静态方法/命名空间函数
B\foo(); // 调用 "A\B" 命名空间中的函数 "foo"
B::foo(); // 调用在 "A" 命名空间中定义的 "B" 类的 "foo" 方法
// 如果未找到类 "A\B",则尝试自动加载类 "A\B"
D::foo(); // 使用导入规则,调用在 "B" 命名空间中定义的 "D" 类的 "foo" 方法
// 如果未找到类 "B\D",则尝试自动加载类 "B\D"
\B\foo(); // 调用 "B" 命名空间中的函数 "foo"
\B::foo(); // 调用全局范围内的 "B" 类的 "foo" 方法
// 如果未找到类 "B",则尝试自动加载类 "B"
// 当前命名空间的静态方法/命名空间函数
A\B::foo(); // 调用 "A\A" 命名空间中 "B" 类的 "foo" 方法
// 如果未找到类 "A\A\B",则尝试自动加载类 "A\A\B"
\A\B::foo(); // 调用 "A" 命名空间中 "B" 类的 "foo" 方法
// 如果未找到类 "A\B",则尝试自动加载类 "A\B"
?>
此处提到的术语“自动加载”不应与用于自动加载对象的 __autoload 函数混淆。关于 __autoload 和命名空间的解析,我想分享以下经验
->假设您有以下目录结构
- 根目录
| - loader.php
| - ns
| - foo.php
->foo.php
<?php
命名空间 ns;
类 foo
{
公共 $say;
公共函数 __construct()
{
$this->say = "bar";
}
}
?>
-> loader.php
<?php
//全局空间 <--
函数 __autoload($c)
{
require_once $c . ".php";
}
类 foo 扩展 ns\foo // ns\foo在此处加载
{
公共函数 __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
命名空间 Glue {
/**
* 在此类中定义您的自定义结构和算法
* 用于自动加载。
*/
类 Import
{
公共静态函数 load ($classname)
{
echo '正在自动加载类 '.$classname."\n";
require_once $classname.'.php';
}
}
}
/**
* 在全局命名空间中定义函数 __autoload。
*/
命名空间 {
函数 __autoload ($classname)
{
\Glue\Import::load($classname);
}
}
?>
提到的文件系统类比在一个重要点上失败了
命名空间解析*仅*在声明时工作。编译器将所有命名空间/类引用固定为绝对路径,就像创建绝对符号链接一样。
您不能期望相对符号链接,它应该在访问期间——在 PHP 运行时进行评估。
换句话说,命名空间像__CLASS__或self::一样在解析时进行评估。*没有*发生的是像static::这样的延迟静态绑定的配套,它在运行时解析为当前类。
所以你不能这样做
命名空间 Alpha;
类 Helper {
公共静态 $Value = "ALPHA";
}
类 Base {
公共静态函数 Write() {
echo Helper::$Value;
}
}
命名空间 Beta;
类 Helper 扩展 \Alpha\Helper {
公共静态 $Value = 'BETA';
}
类 Base 扩展 \Alpha\Base {}
\Beta\Base::Write(); // 应该写入“BETA”,因为这是运行时的执行命名空间上下文。
如果您将write()函数复制到\Beta\Base中,它将按预期工作。
此处提到的术语“自动加载”不应与用于自动加载对象的 __autoload 函数混淆。关于 __autoload 和命名空间的解析,我想分享以下经验
->假设您有以下目录结构
- 根目录
| - loader.php
| - ns
| - foo.php
->foo.php
<?php
命名空间 ns;
类 foo
{
公共 $say;
公共函数 __construct()
{
$this->say = "bar";
}
}
?>
-> loader.php
<?php
//全局空间 <--
函数 __autoload($c)
{
require_once $c . ".php";
}
类 foo 扩展 ns\foo // ns\foo在此处加载
{
公共函数 __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
命名空间 org\example;
函数 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');
?>