## 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` or `false`

### 如果我不用命名空间，是否需要关心它？

不需要。命名空间不影响现存的代码，也不影响即将要写下的不含命名空间的代码。 想要的话可以这样写：

示例 #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` or `false`

在命名空间内定义特殊的内置常量，会导致 fatal 错误

示例 #11 未定义的常量

    <?php
    namespace bar;
    const NULL = 0; // fatal error;
    const true = 'stupid'; // 也是 fatal error;
    // etc.
    ?>

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open