codecamp

PHP8 FAQ:命名空间必知必会

(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. 不允许嵌套 namespace。
  3. 动态命名空间名称(引号标识)应该转义反斜线。
  4. 引用一个未定义的、带反斜线的常量,会导致 fatal 错误并退出
  5. 不能重载特殊常量: 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.
?>


PHP8 名称解析规则
PHP8 枚举概览
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

PHP8 语言参考

PHP8 函数参考

PHP8 影响 PHP 行为的扩展

PHP8 Componere

PHP8 安装/配置

PHP8 外部函数接口

PHP8 选项和信息

PHP8 选项/信息 函数

PHP8 Windows Cache for PHP

PHP8 WinCache 函数

PHP8 Yac

PHP8 身份认证服务

PHP8 Radius 函数

PHP8 压缩与归档扩展

PHP8 Phar

PHP8 Zip

PHP8 ZipArchive 类

PHP8 加密扩展

PHP8 OpenSSL

PHP8 OpenSSL 函数

PHP8 Sodium 函数

PHP8 数据库扩展

PHP8 针对各数据库系统对应的扩展

PHP8 CUBRID 函数

PHP8 Firebird/InterBase

PHP8 Firebird/InterBase函数

PHP8 MongoDB介绍驱动程序体系结构和特殊功能

PHP8 MongoDB\Driver\Command 类

PHP8 MongoDB\Driver\Query 类

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }