名称解析规则

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

在说明名称解析规则之前,我们先看一些重要的定义:

命名空间名称定义
非限定名称(Unqualified name)

名称中不包含命名空间分隔符的标识符,例如 Foo

限定名称(Qualified name)

名称中含有命名空间分隔符的标识符,例如 Foo\Bar

完全限定名称(Fully qualified name)

名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 \Foo\Barnamespace\Foo 也是一个完全限定名称。

相对名称(Relative name)

这是个以 namespace 开头的标识符, 例如 namespace\Foo\Bar

名称解析遵循下列规则:

  1. 完全限定名称总是会解析成没有前缀符号的命名空间名称。 \A\B 解析为 A\B
  2. 解析相对名称时,会用当前命名空间的名称替换掉 namespace。 如果名称出现在全局命名空间,会截掉 namespace\ 前缀。 例如,在命名空间 X\Y 里的 namespace\A 会被解析成 X\Y\A。 在全局命名空间里,同样的名字却被解析成 A
  3. 对于限定名称,名字的第一段会根据当前 class/namespace 导入表进行翻译。 比如命名空间 A\B\C 被导入为 C, 名称 C\D\E 会被翻译成 A\B\C\D\E
  4. 对于限定名称,如果没有应用导入规则,就将当前命名空间添加为名称的前缀。 例如,位于命名空间 A\B 内的名称 C\D\E 会解析成 A\B\C\D\E
  5. 根据符号类型和对应的当前导入表,解析非限定名称。 这也就是说,根据 class/namespace 导入表翻译类名称; 根据函数导入表翻译函数名称; 根据常量导入表翻译常量名称。 比如,在 use A\B\C; 后,类似 new C() 这样的名称会解析为 A\B\C()。 类似的,use function A\B\foo; 后, foo() 的用法,解析名称为 A\B\foo
  6. 如果没有应用导入规则,对于类似 class 符号的非限定名称,会添加当前命名空间作为前缀。 比如命名空间 A\B 内的 new C() 会把名称解析为 A\B\C
  7. 如果没有应用导入规则,非限定名称指向函数或常量,且代码位于全局命名空间之外,则会在运行时解析名称。 假设代码位于命名空间 A\B 中, 下面演示了调用函数 foo() 是如何解析的:
    1. 在当前命名空间中查找函数: A\B\foo()
    2. 它会尝试找到并调用 全局 的函数 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" 中定义的类 "B" 的 "foo" 方法
// 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>
add a note add a note

User Contributed Notes 9 notes

up
37
kdimi
14 years ago
If you like to declare an __autoload function within a namespace or class, use the spl_autoload_register() function to register it and it will work fine.
up
33
rangel
15 years ago
The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:

->Say you have the following directory structure:

- root
      | - loader.php
      | - ns
             | - foo.php

->foo.php

<?php
namespace ns;
class
foo
{
    public
$say;
   
    public function
__construct()
    {
       
$this->say = "bar";
    }
   
}
?>

-> loader.php

<?php
//GLOBAL SPACE <--
function __autoload($c)
{
    require_once
$c . ".php";
}

class
foo extends ns\foo // ns\foo is loaded here
{
    public function
__construct()
    {
       
parent::__construct();
        echo
"<br />foo" . $this->say;
    }
}
$a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
echo $a->say;   // prints bar as expected.
$b = new foo// prints foobar just fine.
?>

If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
But... if you try to give loader.php a namespace you'll obviously get fatal errors.
My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.

Cheers!
up
4
safakozpinar at NOSPAM dot gmail dot com
14 years ago
As working with namespaces and using (custom or basic) autoload structure; magic function __autoload must be defined in global scope, not in a namespace, also not in another function or method.

<?php
namespace Glue {
   
/**
     * Define your custom structure and algorithms
     * for autoloading in this class.
     */
   
class Import
   
{
        public static function
load ($classname)
        {
            echo
'Autoloading class '.$classname."\n";
            require_once
$classname.'.php';
        }
    }
}

/**
* Define function __autoload in global namespace.
*/
namespace {
   
    function
__autoload ($classname)
    {
        \
Glue\Import::load($classname);
    }

}
?>
up
1
Kavoir.com
10 years ago
For point 4, "In example, if the namespace A\B\C is imported as C" should be "In example, if the class A\B\C is imported as C".
up
-3
llmll
9 years ago
The mentioned filesystem analogy fails at an important point:

Namespace resolution *only* works at declaration time. The compiler fixates all namespace/class references as absolute paths, like creating absolute symlinks.

You can't expect relative symlinks, which should be evaluated during access -> during PHP runtime.

In other words, namespaces are evaluated like __CLASS__ or self:: at parse-time. What's *not* happening, is the pendant for late static binding like static:: which resolves to the current class at runtime.

So you can't do the following:

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(); // should write "BETA" as this is the executing namespace context at runtime.

If you copy the write() function into \Beta\Base it works as expected.
up
-6
rangel
15 years ago
The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:

->Say you have the following directory structure:

- root
      | - loader.php
      | - ns
             | - foo.php

->foo.php

<?php
namespace ns;
class
foo
{
    public
$say;
   
    public function
__construct()
    {
       
$this->say = "bar";
    }
   
}
?>

-> loader.php

<?php
//GLOBAL SPACE <--
function __autoload($c)
{
    require_once
$c . ".php";
}

class
foo extends ns\foo // ns\foo is loaded here
{
    public function
__construct()
    {
       
parent::__construct();
        echo
"<br />foo" . $this->say;
    }
}
$a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
echo $a->say;   // prints bar as expected.
$b = new foo// prints foobar just fine.
?>

If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
But... if you try to give loader.php a namespace you'll obviously get fatal errors.
My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.

Cheers!
up
-6
CJ Taylor
10 years ago
It took me playing with it a bit  as I had a hard time finding documentation on when a class name matches a namespace, if that's even legal and what behavior to expect.  It IS explained in #6 but I thought I'd share this with other souls like me that see it better by example.  Assume all 3 files below are in the same directory.

file1.php
<?php
namespace foo;

class
foo {
  static function
hello() {
    echo
"hello world!";
  }
}
?>

file2.php
<?php
namespace foo;
include(
'file1.php');

foo::hello(); //you're in the same namespace, or scope.
\foo\foo::hello(); //called on a global scope.
?>

file3.php
<?php
include('file1.php');

foo\foo::hello(); //you're outside of the namespace
\foo\foo::hello(); //called on a global scope.
?>

Depending upon what you're building (example: a module, plugin, or package on a larger application), sometimes declaring a class that matches a namespace makes sense or may even be required.  Just be aware that if you try to reference any class that shares the same namespace, omit the namespace unless you do it globally like the examples above.

I hope this is useful, particularly for those that are trying to wrap your head around this 5.3 feature.
up
-4
anrdaemon at freemail dot ru
8 years ago
Namespaces may be case-insensitive, but autoloaders most often do.
Do yourself a service, keep your cases consistent with file names, and don't overcomplicate autoloaders beyond necessity.
Something like this should suffice for most times:

<?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');
?>
up
-7
dn dot permyakov at gmail dot com
10 years ago
Can someone explain to me -  why do we need p.4 if we have p.2 (which covers both unqualified and qualified names)?
To Top