PHP命名空间和在声明中使用\前缀

7
下面的代码会抛出一个错误,提示无法重新声明 Exception
namespace \NYTD\ReadingListBackend;

class Exception extends \Exception
{
}

然而,在命名空间声明中去掉\前缀则不起作用:
namespace NYTD\ReadingListBackend;

我最近开始使用PHP的命名空间。我理解带有 \ 前缀的命名空间代表一个完全限定的名称。
那么为什么在命名空间声明中不能使用前缀呢?但是当引用时可以使用 (例如new \NYTD\ReadingListBackend\Exception)。如果能提供一个详细的解释,将不胜感激,因为我在 官方文档 中没有找到任何信息。

在文档中,他们只是在调用时在开头使用 \。要声明它们时不使用 \。因此应该是:namespace NYTD\ReadingListBackend; 当你想要访问它时,只需要编写 NYTD\ReadingListBackend\Exception; - PKeidel
我认为PHP希望\NYTD\ReadingListBackend是一个常量或者类似的东西。省略掉开头的\,你就没问题了。 - Elias Van Ootegem
@Gordon,明白了。说实话,我并不想刁难你。我知道命名空间声明中的\前缀是无效的。我只是想理解为什么 - Jason McCreary
@hakre,感谢您的示例。就答案而言,这意味着什么? - Jason McCreary
不,它根本没有回答问题,我想它增加了更多的问号;) 顺便说一下,这是一个有趣的发现! - hakre
显示剩余8条评论
3个回答

8

正如其他答案中提到的那样,namespace声明始终采用完全限定名称,因此尾随的\是多余的且不允许的。

编写namespace \NYTD\ReadingListBackend { ... }将导致出现适当的解析错误。

另一方面,在使用分号符号namespace \NYTD\ReadingListBackend;时,它被解释为namespace\NYTD\ReadingListBackend;,这是对常量NYTD\ReadingListBackend的访问(这里的namespace关键字解析为当前活动的命名空间,即全局命名空间)。

因此,您的代码没有声明任何命名空间(它是全局的),只是尝试访问常量。这就是为什么最终重新定义了Exception的原因。

顺便说一下,未定义的常量访问之所以不会引发致命错误,是因为类提升。先评估您的类声明,因此PHP实际上从未到达常量访问位置(即使它在代码中排在第一位)。


+1 非常好的解释。但如果这是真的,那就意味着语法允许在命名空间中使用空格。这似乎是错误的。 - Jason McCreary
@JasonMcCreary 正确。PHP忽略所有空格和注释。它不在乎你是写namespace\Foo\Bar,还是namespace \ Foo \ Bar,或者是namespace /* a */ \ /* very */ Foo /* odd */ \ /* comment */ Bar ;) - NikiC
是的,但在这种情况下它不应忽略空格。对于$ var也是如此。尽管如此,在声明后使用echo __NAMESPACE__;验证了您的答案。这确实是使用\前缀发生的情况。 - Jason McCreary

2
所以,为什么不能在命名空间声明中使用前缀呢?
因为按照定义,当你声明一个命名空间名称时,它已经是完全限定的。
但是,在代码的命名空间部分内部,如果需要指定完全限定类名(FQCN),则需要使用前缀来向PHP表明该类名是FQCN。
令人不满意的是,PHP在解析您的示例时没有提前报错。我不能告诉你为什么。
namespace \NYTD\ReadingListBackend;
          ^- this seems wrong but no parse error

然而,PHP在使用花括号表示法(一个文件中有多个命名空间)时会出错:

namespace \NYTD\ReadingListBackend {}
                                   ^- parse error in this line

但是针对您的问题,我认为值得区分命名空间名称和类名称。


+1,目前最佳答案。根据定义,当您声明命名空间名称时,它已经是完全限定的。 :) - Jason McCreary
是的,如果你查看http://php.net/language.namespaces.nested,你会发现分隔符仅在命名空间名称之间使用。在http://php.net/language.namespaces.definition中根本不使用分隔符。 - hakre
我已经多次阅读了文档。在没有解析错误的情况下,我试图找到有关使用分隔符作为前缀的明确说明。事实上它可以运行,但最终被解释不同,这最终导致了我的困惑。 - Jason McCreary
是的,我能理解你的困惑。然而,在namespace关键字的文档中并没有说要在第一个名称前使用命名空间分隔符。在那个位置上没有需要分隔的内容。我想我会提交一个错误报告。 - hakre
无法确认或否认。我不愿意说“bug”。然而,在审查语法后,可能存在一个问题。特别是在使用命名空间块语法(即“{”)时存在不一致性。 - Jason McCreary

1

\前缀表示“根”或类似的意思。 它将一个“限定名称空间”(Foo / Bar)转换为“完全限定名称空间”(/ Foo / Bar )。只有在使用命名空间时才有用,而在声明命名空间时没有用。例如:

namespace Foo

class Bar {
    // ...
}

namespace Bar\Baz;

$b = new Bar(); // look for Bar\Baz\Bar()
$b = new Foo\Bar(); // look for Bar\Baz\Foo\Bar()
$b = new \Foo\Bar(); // look for Foo\Bar() (the right one)

在声明命名空间时,它并不是很有用,因为您总是从全局('root')命名空间声明命名空间:

namespace Foo; // the \Foo namespace
// ...
namespace Bar; // the \Bar namespace, not the \Foo\Bar namespace
// ...
namespace Foo\Bar; // the \Foo\Bar namespace

由于每个命名空间声明都是“完全限定”的,PHP不希望在命名空间前面加上\,否则会出现错误。


我了解了命名空间解析。我期望使用这个前缀要么会导致语法错误,要么作为一个完全合格的命名空间工作。而不是以不同结果运行。 - Jason McCreary

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