常见问题解答:您需要了解的关于命名空间的事情

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

本常见问题解答分为两部分:常见问题以及一些有助于全面理解的实现细节。

首先,常见问题。

  1. 如果我不使用命名空间,是否需要关心这些内容?
  2. 如何在命名空间中使用内部或全局类?
  3. 如何在自己的命名空间中使用命名空间类函数或常量?
  4. 名称如 \my\name\name 如何解析?
  5. 名称如 my\name 如何解析?
  6. 未限定的类名如 name 如何解析?
  7. 未限定的函数名或未限定的常量名如 name 如何解析?

有一些关于命名空间实现的实现细节有助于理解。

  1. 导入名称不得与同一文件中定义的类冲突。
  2. 不允许嵌套命名空间。
  3. 动态命名空间名称(带引号的标识符)应该转义反斜杠。
  4. 使用任何反斜杠引用的未定义常量会导致致命错误
  5. 无法覆盖特殊常量 null, truefalse

如果我不使用命名空间,是否需要关心这些内容?

不会。命名空间不会以任何方式影响任何现有代码,也不会影响任何尚未编写的代码,这些代码不包含命名空间。如果您愿意,可以编写此代码

示例 #1 在命名空间外部访问全局类

<?php
$a
= new \stdClass;
?>

在功能上等同于

示例 #2 在命名空间外部访问全局类

<?php
$a
= new stdClass;
?>

如何在命名空间中使用内部或全局类?

示例 #3 在命名空间中访问内部类

<?php
namespace foo;
$a = new \stdClass;

function
test(\ArrayObject $parameter_type_example = null) {}

$a = \DirectoryIterator::CURRENT_AS_FILEINFO;

// 扩展内部或全局类
class MyException extends \Exception {}
?>

如何在命名空间中使用命名空间类、函数或常量?

示例 #4 在命名空间中访问内部类、函数或常量

<?php
namespace foo;

class
MyClass {}

// 使用来自当前命名空间的类作为参数类型
function test(MyClass $parameter_type_example = null) {}
// 另一种方法是使用来自当前命名空间的类作为参数类型
function test(\foo\MyClass $parameter_type_example = null) {}

// 扩展来自当前命名空间的类
class Extended extends MyClass {}

// 访问全局函数
$a = \globalfunc();

// 访问全局常量
$b = \INI_ALL;
?>

名称如 \my\name\name 如何解析?

\ 开头的名称始终解析为它们看起来的样子,因此 \my\name 实际上是 my\name,而 \Exception 则是 Exception

示例 #5 全限定名

<?php
namespace foo;
$a = new \my\name(); // 实例化 "my\name" 类
echo \strlen('hi'); // 调用函数 "strlen"
$a = \INI_ALL; // $a 设置为常量 "INI_ALL" 的值
?>

名称如 my\name 如何解析?

包含反斜杠但不以反斜杠开头的名称,如 my\name,可以通过 2 种不同的方式解析。

如果存在将另一个名称别名为 my 的导入语句,则导入别名将应用于 my\name 中的 my

否则,当前命名空间名称将附加到 my\name 前面。

示例 #6 限定名

<?php
namespace foo;
use
blah\blah as foo;

$a = new my\name(); // 实例化 "foo\my\name" 类
foo\bar::name(); // 调用类 "blah\blah\bar" 中的静态方法 "name"
my\bar(); // 调用函数 "foo\my\bar"
$a = my\BAR; // 将 $a 设置为常量 "foo\my\BAR" 的值
?>

未限定的类名如 name 如何解析?

不包含反斜杠的类名,如 name,可以通过 2 种不同的方式解析。

如果存在将另一个名称别名为 name 的导入语句,则应用导入别名。

否则,当前命名空间名称将附加到 name 前面。

示例 #7 未限定的类名

<?php
namespace foo;
use
blah\blah as foo;

$a = new name(); // 实例化 "foo\name" 类
foo::name(); // 调用类 "blah\blah" 中的静态方法 "name"
?>

未限定的函数名或未限定的常量名如 name 如何解析?

不包含反斜杠的函数或常量名,如 name,可以通过 2 种不同的方式解析。

首先,当前命名空间名称将附加到 name 前面。

最后,如果常量或函数 name 在当前命名空间中不存在,则使用全局常量或函数 name(如果存在)。

示例 #8 未限定的函数或常量名

<?php
namespace foo;
use
blah\blah as foo;

const
FOO = 1;

function
my() {}
function
foo() {}
function
sort(&$a)
{
\sort($a); // 调用全局函数 "sort"
$a = array_flip($a);
return
$a;
}

my(); // 调用 "foo\my"
$a = strlen('hi'); // 调用全局函数 "strlen" 因为 "foo\strlen" 不存在
$arr = array(1,3,2);
$b = sort($arr); // 调用函数 "foo\sort"
$c = foo(); // 调用函数 "foo\foo" - 导入不生效

$a = FOO; // 将 $a 设置为常量 "foo\FOO" 的值 - 导入不生效
$b = INI_ALL; // 将 $b 设置为全局常量 "INI_ALL" 的值
?>

导入名称不得与同一文件中定义的类冲突。

以下脚本组合是合法的

file1.php

<?php
namespace my\stuff;
class
MyClass {}
?>

another.php

<?php
namespace another;
class
thing {}
?>

file2.php

<?php
namespace my\stuff;
include
'file1.php';
include
'another.php';

use
another\thing as MyClass;
$a = new MyClass; // 实例化来自命名空间 another 的类 "thing"
?>

即使类 MyClass 存在于命名空间 my\stuff 中,也不会发生名称冲突,因为 MyClass 的定义位于一个单独的文件中。但是,下一个例子会导致致命错误,因为 MyClass 在同一个文件中定义了 use 语句。

<?php
namespace my\stuff;
use
another\thing as MyClass;
class
MyClass {} // 致命错误: MyClass 与导入语句冲突
$a = new MyClass;
?>

不允许嵌套命名空间。

PHP 不允许嵌套命名空间

<?php
namespace my\stuff {
namespace
nested {
class
foo {}
}
}
?>
但是,很容易模拟嵌套命名空间,如下所示
<?php
namespace my\stuff\nested {
class
foo {}
}
?>

动态命名空间名称(引用标识符)应转义反斜杠

非常重要的一点是,由于反斜杠在字符串中用作转义字符,因此在字符串中使用时始终应加倍。否则有产生意想不到的后果的风险

示例 #9 使用命名空间名称的双引号字符串的危险

<?php
$a
= "dangerous\name"; // \n 在双引号字符串中是换行符!
$obj = new $a;

$a = 'not\at\all\dangerous'; // 这里没有问题。
$obj = new $a;
?>
在单引号字符串中,反斜杠转义序列更安全,但仍然建议在所有字符串中转义反斜杠作为最佳实践。

使用任何反斜杠引用的未定义常量会导致致命错误

任何未限定的未定义常量,如 FOO,将产生一个通知,解释 PHP 假设 FOO 是常量的值。任何限定或完全限定的常量,如果包含反斜杠,如果未找到将产生致命错误。

示例 #10 未定义常量

<?php
namespace bar;
$a = FOO; // 产生通知 - 未定义的常量 "FOO" 假设为 "FOO";
$a = \FOO; // 致命错误,未定义的命名空间常量 FOO
$a = Bar\FOO; // 致命错误,未定义的命名空间常量 bar\Bar\FOO
$a = \Bar\FOO; // 致命错误,未定义的命名空间常量 Bar\FOO
?>

无法覆盖特殊常量 nulltruefalse

任何尝试定义命名空间常量为特殊内置常量都会导致致命错误

示例 #11 未定义常量

<?php
namespace bar;
const
NULL = 0; // 致命错误;
const true = 'stupid'; // 同样是致命错误;
// 等等
?>

添加注释

用户贡献的注释 6 个注释

manolachef at gmail dot com
12 年前
有一种方法可以使用 define 函数定义一个命名空间常量,该常量是一个特殊的内置常量,并将第三个参数 case_insensitive 设置为 false

<?php
namespace foo;
define(__NAMESPACE__ . '\NULL', 10); // 在当前命名空间中定义常量 NULL
var_dump(NULL); // 将显示 10
var_dump(null); // 将显示 NULL
?>

无需在对 define() 的调用中指定命名空间,就像通常那样
<?php
namespace foo;
define(INI_ALL, 'bar'); // 产生通知 - 常量 INI_ALL 已定义。 但是:

define(__NAMESPACE__ . '\INI_ALL', 'bar'); // 在当前命名空间中定义常量 INI_ALL
var_dump(INI_ALL); // 将显示字符串(3)"bar"。到目前为止没有意料之外的事情。 但是:

define('NULL', 10); // 在当前命名空间中定义常量 NULL...
var_dump(NULL); // 将显示 10
var_dump(null); // 将显示 NULL
?>

如果参数 case_insensitive 设置为 true
<?php
namespace foo;
define (__NAMESPACE__ . '\NULL', 10, true); // 产生通知 - 常量 null 已定义
?>
shaun at slickdesign dot com dot au
7 年前
在使用变量从命名空间内创建类或调用静态方法时,请记住它们需要完整的命名空间才能使用相应的类;你不能使用别名或短名称,即使它是在同一个命名空间中调用。忽略这一点会导致你的代码使用错误的类,抛出致命缺少类异常,或者抛出错误或警告。

在这些情况下,你可以使用魔术常量 __NAMESPACE__,或者直接指定完整的命名空间和类名。函数 class_exists 也需要完整的命名空间和类名,并且可以用来确保不会因为缺少类而抛出致命错误。

<?php

namespace Foo;
class
Bar {
public static function
test() {
return
get_called_class();
}
}

namespace
Foo\Foo;
class
Bar extends \Foo\Bar {
}

var_dump( Bar::test() ); // string(11) "Foo\Foo\Bar"

$bar = 'Foo\Bar';
var_dump( $bar::test() ); // string(7) "Foo\Bar"

$bar = __NAMESPACE__ . '\Bar';
var_dump( $bar::test() ); // string(11) "Foo\Foo\Bar"

$bar = 'Bar';
var_dump( $bar::test() ); // FATAL ERROR: Class 'Bar' not found or Incorrect class \Bar used
theking2 at king dot ma
2 年前
就像类名一样,命名空间目前不区分大小写。因此这里不会显示任何错误。

<?php declare(strict_types=1);
namespace
Foo;
class
Bar {
public function
__construct() {
echo
'Map constructed';
}
}

$foobar = new \foo\bar();
teohad at NOSPAM dot gmail dot com
8 年前
[编辑注:这种行为是由 PHP 7.0 中的一个错误引起的,该错误已在 PHP 7.0.7 中修复。]

关于条目 "导入的名称不能与同一个文件中定义的类冲突"。
- 我发现从 PHP 7.0 开始,情况不再如此。
在 PHP 7.0 中,你可以拥有一个与导入的类(或命名空间,或同时两者)同名的类。

<?php
namespace ns1 {
class
ns1 {
public static function
write() {
echo
"ns1\\ns1::write()\n";
}
}
}

namespace
ns1\ns1 {
class
ns1c {
public static function
write() {
echo
"ns1\\ns1\\ns1c::write()\n";
}
}
}

namespace
ns2 {
use
ns1\ns1 as ns1; // both a class in ns1, and a namespace ns1\ns1

// the next class causes fatal error in php 5.6, not in 7.0
class ns1 {
public static function
write() {
echo
"ns2\\ns1::write()\n";
}
}

ns1::write(); // calls imported ns1\ns1::write()
ns1\ns1c::write(); // calls imported ns1\ns1\ns1c::write()
namespace\ns1::write(); // calls ns2\ns1::write()
}
?>
phpcoder
9 年前
关于 "函数和常量都不能通过 use 语句导入"。实际上你可以在 PHP 5.6+ 中做到这一点。

<?php

// importing a function (PHP 5.6+)
use function My\Full\functionName;

// aliasing a function (PHP 5.6+)
use function My\Full\functionName as func;

// importing a constant (PHP 5.6+)
use const My\Full\CONSTANT;
?>
okaresz
11 年前
为了纠正 manolachef 的回答:define() 总是定义全局命名空间中的常量。

正如 nl-x at bita dot nl 在 https://php.net/manual/en/function.define.php, 中的说明,常量 "NULL" 可以使用 define() 区分大小写地定义,但只能用 constant() 检索,使 NULL 大写关键字的含义成为 null 类型唯一的取值。
To Top