在PHP中导入类和命名空间:前置反斜杠有什么区别?

54

这两者有什么区别:

use Exception;
use \Exception;

或者这些:

use Foo\Bar;
use \Foo\Bar;

根据手册

对于命名空间名称(完全限定命名空间名称包含命名空间分隔符的,例如Foo\Bar,而不是全局名称,不包含命名空间分隔符,例如FooBar),前导反斜杠是不必要且不被允许的,因为导入名称必须是完全限定的,并且不会相对于当前命名空间处理。

但我并不太理解这个说明,因为上述所有变量都可以正常工作,即它绝对不是“不被允许”的。

在查看zend_do_use之后,发现只有以下情况下才会使用is_global(当存在前导反斜杠时设置)警告:

namespace {
    use Exception;
}

这提示我:“非复合名称'Exception'的use语句没有效果”。(虽然使用 use \Exception 也同样没有效果,但不会产生警告。)

那么:我错过了什么吗?实际上有什么区别吗?

5个回答

39
手册指定反斜杠为不必要的,这自然意味着如果你仍然使用它,那么意义是等同的。但是,正如你指出的那样,手册说它被认为是不允许的,这是错误的。
然而,手册中还有其他令人困扰的地方。他们宣传这个:
// importing a global class
use \ArrayObject;
如果导入的名称不是相对于当前命名空间进行处理,那么使用 use \ArrayObjectuse ArrayObject 必须具有相同的含义。除了全局的对象之外,use ArrayObject还可能指代什么呢?在实践中,引擎将会导入全局对象。
另外,由于存在这样的错误: http://bugs.php.net/bug.php?id=49143 我认为标准意义上可能存在一些混淆。
回答你的问题:没有区别。但是,如果我是引擎开发人员,并且也信奉无前导斜杠的标准,那么我就不需要考虑有人写了 use \Exception; 这样的情况。我认为这很可能是当时的情况。

1
请注意,如果你正在使用命名空间类,并且使用composer来加载一个类,只需通过其“最后”名称(没有命名空间前缀)调用它,那么如果你在调用之前不使用use关键字,就必须要加上反斜杠前缀。例如:\QuantumPHP::log("some message");,除非你在类定义之前添加"use QuantumPHP;"。 - Fabien Haddadi

17
事实上,目前在导入命名空间时使用前导反斜杠与PHP手册中的信息没有区别:

请注意,对于带有命名空间分隔符的命名空间名称(完全限定的命名空间名称,例如Foo\Bar,而不是无需命名空间分隔符的全局名称,例如FooBar),前导反斜杠是不必要的并且不推荐使用,因为导入的名称必须是完全限定的,并且不能相对于当前命名空间进行处理。

因此,现在的真实信息是不推荐使用前导反斜杠,但是没有信息表明过去禁止使用它。

所以目前情况是:

use Exception;
use \Exception;

这两行代码的作用相同,但你最好使用第一行。


8
通常,前导反斜杠定义标识符为绝对标识符。如果缺失,则解释器会将其视为相对标识符。
这是一个绝对标识符:
$x = new \Name\Space\To\Class();

这是一个相对标识符,因为没有前导斜杠。它相对于当前命名空间:

namespace Name\Space;
$x = new To\Class;

这也是一个相对标识符。在这种情况下,它将根据use语句进行解析,因为最后一部分(别名)与类的第一部分相同:

namespace Other\Name\Space;
use Name\Space;
$x = new Space\To\Class;

然而,在namespaceuse语句中只允许使用绝对标识符(完全限定名称),因此在这里省略它是可以的。在namespace中,甚至不允许设置前导反斜杠。

有关PHP如何解析不同命名空间声明的更多信息,请参阅命名空间规则手册


-2

前导反斜杠表示全局命名空间。如果您在命名空间的作用域内,必须使用该命名空间才能访问全局命名空间。例如:

namespace A
{
    class A
    {
        public function __construct()
        {
            echo('namespace: A<br />');
        }
    }
}

namespace B\A
{
    class A
    {
        public function __construct()
        {
            echo('namespace: B\\A<br />');
        }
    }
}

namespace B
{
    class B
    {
        public function __construct()
        {
            new \A\A(); // namespace: A
            new A\A(); // namespace: B\A
        }
    }
    new B();
}

在路径前加上反斜杠,你就得到了绝对路径,没有反斜杠则是相对路径。


1
那是我见过的最令人困惑的例子。 - Marc DiMillo
顺便说一下,当我写那段代码时,并没有使用“use”关键字。选择使用“B\A”命名空间和“A”类是一个有趣的选择。:D 我在变量命名方面有出色的天赋... :D - inf3rno

-3

假设我们有

namespace MyNamespace
use Exception;
use \Exception;

那么第一次使用实际上是导入了 MyNamespace\Exception 类,而第二次只是导入了主类 \Exception

这样你就可以有像这样的东西

namespace MyNamespace;
class Exception extends \Exception{ }

然后我就可以

throw new \Exception('Exception from global namespace');
throw new \MyNamespace\Exception('Exception from MyNamespace');

1
命名空间 Foo { 类 Exception 扩展 \Exception { } 使用 Exception 作为 BigFoo; 抛出一个 BigFoo; } 这将抛出全局的 Exception,而不是本地的。上面链接的文档也说:“导入的名称必须是完全限定的,并且不会相对于当前命名空间进行处理”。 - NikiC
2
这是错误的。use语句总是使用完全限定名称。因此,use Exception;不会针对当前命名空间解析,实际上与use \Exception;相同。 - KingCrunch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接