FAQ:命名空间必知必会
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
本文分两节:常见问题、有助于完全理解的实现详情。
首先,常见问题。
- 如果我不用命名空间,是否需要关心它?
- 我如何在命名空间内使用一个全局/内置的类?
- 如何在命名空间内访问它自己的类、函数、常量?
-
像
\my\name
和\name
这样的名称是如何解析的? -
像
my\name
这样的名称是如何解析的? -
像
name
这样的非限定类名是如何解析的? -
像
name
这样的非限定常量和函数名是如何解析的?
为了帮助理解,我们提供了一些命名空间实现细节。
- 在同一个文件中,导入名称不能和定义的类名发生冲突。
- 不允许嵌套 namespace。
- 动态命名空间名称(引号标识)应该转义反斜线。
- 引用一个未定义的、带反斜线的常量,会导致 fatal 错误并退出
- 不能重载特殊常量: NULL、TRUE、FALSE、ZEND_THREAD_SAFE、ZEND_DEBUG_BUILD
如果我不用命名空间,是否需要关心它?
不需要。命名空间不影响现存的代码,也不影响即将要写下的不含命名空间的代码。 想要的话可以这样写:
示例 #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
class MyException extends \Exception {}
?>
如何在命名空间内访问它自己的类、函数、常量?
示例 #4 在命名空间中访问内置的类、函数、常量
<?php
namespace foo;
class MyClass {}
// 以当前命名空间中的 class 作为参数的类型
function test(MyClass $parameter_type_example = null) {}
// 以当前命名空间中的 class 作为参数的类型的另一种方式
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(); // class "my\name" 的实例
echo \strlen('hi'); // 调用函数 "strlen"
$a = \INI_ALL; // $a 的值设置成常量 "INI_ALL"
?>
像 my\name
这样的名称是如何解析的?
像 my\name
这样包含反斜线的名称,但不以反斜线开头的名称,
能够以两种不同的方式解析。
如果有个导入语句,将其他名字设置别名为 my
,
则导入别名会应用到 my\name
的 my
部分。
如果没有导入,就会追加当前的命名空间名称为 my\name
的前缀。
示例 #6 限定名称
<?php
namespace foo;
use blah\blah as foo;
$a = new my\name(); // class "foo\my\name" 的实例
foo\bar::name(); // 调用 class "blah\blah\bar" 的静态方法 "name"
my\bar(); // 调用函数 "foo\my\bar"
$a = my\BAR; // 设置 $a 的值为 "foo\my\BAR"
?>
像 name
这样的非限定名称是如何解析的?
像 name
这样不包含反斜线的名称,
能够以两种不同的方式解析。
如果有导入语句,设置别名为 name
,就会应用导入别名。
如果没有,就会把当前命名空间添加到 name
的前缀。
示例 #7 非限定类名
<?php
namespace foo;
use blah\blah as foo;
$a = new name(); // class "foo\name" 的实例
foo::name(); // 调用 class "blah\blah" 的静态方法 "name"
?>
像 name
这样的非限定常量和函数名是如何解析的?
像 name
这样不包含反斜线的常量和函数名,能以两种不同的方式解析。
首先,当前命名空间会添加到 name
的前缀。
然后,如果当前命名空间不存在函数和常量 name
,
而全局存在,就会使用全局的函数和常量 name
。
示例 #8 非限定函数和常量名
<?php
namespace foo;
use blah\blah as foo;
const FOO = 1;
function my() {}
function foo() {}
function sort(&$a)
{
sort($a);
$a = array_flip($a);
return $a;
}
my(); // 调用 "foo\my"
$a = strlen('hi'); // 由于 "foo\strlen" 不存在,所以调用全局的 "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; // class "thing" 的实例来自于命名空间 another
?>
尽管在 my\stuff
命名空间中存在 MyClass
,
因为类定义在了独立的文件中,所以不会发生名称冲突。
不过,接下来的例子中,因为 MyClass 定义在了 use 语句的同一个文件中,
所以发生了名称冲突,导致了 fatal 错误。
<?php
namespace my\stuff;
use another\thing as MyClass;
class MyClass {} // fatal error: MyClass conflicts with import statement
$a = new MyClass;
?>
不允许嵌套 namespace
PHP 不允许嵌套 namespace
<?php
namespace my\stuff {
namespace nested {
class foo {}
}
}
?>
<?php
namespace my\stuff\nested {
class foo {}
}
?>
动态命名空间名称(引号标识)应该转义反斜线
重要的是,字符串中反斜线是一个转义字符,因此在字符串中使用时,必须要写两遍。 否则就会在无意中造成一些后果:
示例 #9 在双引号字符串中使用命名空间的危险性
<?php
$a = new "dangerous\name"; // 在双引号字符串中,\n 是换行符!
$obj = new $a;
$a = new 'not\at\all\dangerous'; // 这里没有问题
$obj = new $a;
?>
引用一个未定义的、带反斜线的常量,会导致 fatal 错误并退出
像 FOO
这样的非限定名称常量,如果使用的时候还没定义,
会产生一个 notice。PHP 会假设该常量的值是 FOO
。
如果没有找到包含反斜线的常量,无论是完全或者不完全限定的名称,都会产生 fatal 错误。
示例 #10 未定义的常量
<?php
namespace bar;
$a = FOO; // 产生 notice - undefined constants "FOO" assumed "FOO";
$a = \FOO; // fatal error, undefined namespace constant FOO
$a = Bar\FOO; // fatal error, undefined namespace constant bar\Bar\FOO
$a = \Bar\FOO; // fatal error, undefined namespace constant Bar\FOO
?>
不能重载特殊常量:NULL、TRUE、FALSE、ZEND_THREAD_SAFE、ZEND_DEBUG_BUILD
在命名空间内定义特殊的内置常量,会导致 fatal 错误
示例 #11 未定义的常量
<?php
namespace bar;
const NULL = 0; // fatal error;
const true = 'stupid'; // 也是 fatal error;
// etc.
?>

User Contributed Notes 5 notes
When creating classes or calling static methods from within namespaces using variables, you need to keep in mind that they require the full namespace in order for the appropriate class to be used; you CANNOT use an alias or short name, even if it is called within the same namespace. Neglecting to take this into account can cause your code to use the wrong class, throw a fatal missing class exception, or throw errors or warnings.
In these cases, you can use the magic constant __NAMESPACE__, or specify the full namespace and class name directly. The function class_exists also requires the full namespace and class name, and can be used to ensure that a fatal error won't be thrown due to missing classes.
<?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
There is a way to define a namespaced constant that is a special, built-in constant, using define function and setting the third parameter case_insensitive to false:
<?php
namespace foo;
define(__NAMESPACE__ . '\NULL', 10); // defines the constant NULL in the current namespace
var_dump(NULL); // will show 10
var_dump(null); // will show NULL
?>
No need to specify the namespace in your call to define(), like it happens usually
<?php
namespace foo;
define(INI_ALL, 'bar'); // produces notice - Constant INI_ALL already defined. But:
define(__NAMESPACE__ . '\INI_ALL', 'bar'); // defines the constant INI_ALL in the current namespace
var_dump(INI_ALL); // will show string(3)"bar". Nothing unespected so far. But:
define('NULL', 10); // defines the constant NULL in the current namespace...
var_dump(NULL); // will show 10
var_dump(null); // will show NULL
?>
If the parameter case_insensitive is set to true
<?php
namespace foo;
define (__NAMESPACE__ . '\NULL', 10, true); // produces notice - Constant null already defined
?>
[Editor's note: that behavior is caused by a bug in PHP 7.0, which has been fixed as of PHP 7.0.7.]
Regarding the entry "Import names cannot conflict with classes defined in the same file".
- I found that since PHP 7.0 this is no longer the case.
In PHP 7.0 you can have a class with a name that matches an imported class (or namespace or both at the same time).
<?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()
}
?>
Regarding "Neither functions nor constants can be imported via the use statement." Actually you can do it in 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;
?>
To correct manolachef's answer: define() ALWAYS defines constants in the GLOBAL namespace.
As nl-x at bita dot nl states in the note at http://www.php.net/manual/en/function.define.php, the constant "NULL" can be defined with define() case-sensitively, but can only be retrieved with constant(), leaving the meaning of NULL uppercase keyword as the only value of the type null.
备份地址:http://www.lvesu.com/blog/php/language.namespaces.faq.php