首页 > 编程笔记 > PHP笔记 阅读:6

PHP namespace命名空间用法详解(附带实例)

在使用计算机管理文件时,通常会在硬盘中创建很多目录,将文件放在这些目录中,且要避免同一个目录中文件名称重复。

在项目开发中,经常会用到大量的类库,每个类库中又包含大量的类文件,为了避免不同类库的类文件出现命名冲突,可以将一个类库的类放在命名空间里,通过路径(如 a\b\c)来访问类库中的类。

PHP命名空间的定义

命名空间可以解决不同类库之间的命名冲突问题。namespace 关键字用于定义命名空间,基本语法格式如下:
namespace 空间名称;
在上述语法格式中,空间名称遵循标识符命名规则,由数字、字母和下画线组成,且不能以数字开头。同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容存放在不同的文件中。

在定义命名空间时,只有 declare 关键字可以出现在第一个定义命名空间的语句之前。如果第一个定义命名空间的语句之前有其他 PHP 代码,会出现 Fatal error 错误信息。其中,declare 关键字用于设置或修改指定代码块的运行时配置选项。

下面演示命名空间的定义,示例代码如下:
<?php
namespace App;

/* 此处编写PHP代码 */
在上述示例代码中,App 是定义的空间名称。

需要说明的是,根据 PHP 标准建议(PHP Standards Recommendations,PSR),定义命名空间后必须插入一个空行。关于 PSR 的相关内容,读者可参考PHP框架互操作组(PHP Framework Interop Group,PHP-FIG)的文档。

下面演示在定义命名空间前使用 declare 语句,示例代码如下:
<?php
declare (ticks = 1);
namespace App;

/* 此处编写PHP代码 */

一个目录下可以创建多个目录和文件,同样,命名空间也可以指定多个层次。非顶层的命名空间通常被称为子命名空间,定义子命名空间的示例代码如下:
<?php
namespace App\Http\Controllers;

/* 此处编写PHP代码 */
在上述示例代码中,Http 是 App 的子命令空间,Controllers 是 Http 的子命令空间。

【扩展】在同一个 PHP 脚本中定义多个命名空间

在同一个 PHP 脚本中可以定义多个命名空间。PHP 提供了简单组合和大括号“{ }”两种方式来定义多个命名空间,使用简单组合方式定义多个命名空间的示例代码如下。
<?php
namespace MyNamespace;

/* 此处编写PHP代码 */

namespace AnotherNamespace;

/* 此处编写PHP代码 */

使用大括号“{ }”方式定义多个命名空间的示例代码如下:
<?php
namespace MyNamesapce {
    /* 此处编写PHP代码 */
}
namespace AnotherNamespace {
    /* 此处编写PHP代码 */
}

PHP命名空间的访问

虽然任意合法的PHP代码都可以包含在命名空间中,但只有类、接口、函数和常量受命名空间的影响,这些受命名空间影响的内容也被称为空间成员。

PHP 提供了 3 种访问命名空间的方式,分别是非限定名称访问、限定名称访问和完全限定名称访问,具体介绍如下。

1) 非限定名称访问

非限定名称访问是指直接访问空间成员,不指定空间名称。这种方式只能访问从当前代码向上寻找到的第 1 个命名空间内的成员,当找到的命名空间中不存在指定的空间成员时,PHP 就会报错。

2) 限定名称访问

限定名称访问是指从当前命名空间开始,访问子命名空间的成员。限定名称访问的语法格式如下:
空间名称\空间成员名称;
限定名称访问只能访问当前空间的子空间成员,不能访问其父空间的成员。

3) 完全限定名称访问

完全限定名称访问是指在任意的命名空间中访问从根命名空间开始的任意空间内的成员。

完全限定名称访问的语法格式如下:
\空间名称\空间成员名称;
对于以上 3 种访问命名空间的方式,需要注意的是,命名空间引入的时机与文件载入的时机相关。在 PHP 中,文件的载入发生在代码的执行阶段,而不是代码的编译阶段。所以,不能在载入文件前访问引入的空间成员,否则程序会报错。

下面通过案例演示3种命名空间的访问方式,具体步骤如下:
① 在 VS Code 编辑器中打开 C:\web\apache2.4\htdocs 目录,创建 namespace01.php 文件,具体代码如下:
<?php
namespace two\one;

const PI = 3.14;
echo PI;    // 非限定名称访问
在上述代码中,第 2 行代码定义了 two\one 命名空间,第 4 行代码定义了常量 PI,第 5 行代码使用非限定名称访问当前命名空间的常量 PI。

② 创建 namespace02.php 文件,具体代码如下:
<?php
namespace two;

require './namespace01.php';
echo one\PI;       // 限定名称访问
echo \two\one\PI;  // 完全限定名称访问
在上述代码中,第 2 行代码定义了 two 命名空间,第 5 行代码使用限定名称访问常量 PI,第 6 行代码使用完全限定名称访问常量 PI。

【扩展】全局空间

当 PHP 脚本中没有定义命名空间时,其中的所有代码都属于全局空间。

全局空间中的类、接口、函数和常量皆为全局空间成员。全局空间成员包括 PHP 内置的成员,也包括用户自定义的成员。在含有命名空间的PHP脚本中引入全局空间脚本后,全局空间成员的访问方式为“\全局空间成员”。

下面演示如何在命名空间中访问全局空间成员,创建 namespace03.php 文件,具体代码如下:
<?php
namespace common;

const PHP_VERSION = '8.2';
echo PHP_VERSION;    // 访问空间成员:8.2
echo \PHP_VERSION;   // 访问全局成员:8.2.3
在上述代码中,定义了命名空间 common。第 5 行代码访问的是当前命名空间 common 中的常量 PHP_VERSION;第 6 行代码在访问常量 PHP_VERSION 前添加“\”,表示访问全局空间中的常量,输出的是 PHP 内置的常量值。

PHP导入命名空间

当在一个命名空间中使用其他命名空间的空间成员时,每次都在空间成员前面加上路径会比较烦琐,此时可以使用 use 关键字导入指定的命名空间或空间成员,语法格式如下:
use 命名空间或空间成员;
在上述语法格式中,use 采用类似完全限定名称的方式导入内容,并且不需要添加前导反斜线“\”。

当导入的空间成员为函数时,需要在 use 后面添加 function 关键字;当导入的空间成员为常量时,需要在 use 后面添加 const 关键字。

导入的空间成员为函数和常量的语法格式如下:
use function 函数的命名空间;
use const 常量的命名空间;
需要注意的是,使用 use 导入顶层命名空间没有任何意义,程序会出现警告信息。例如,使用“namespace App;”定义了顶层命名空间 App,若使用“use App;”进行导入,程序会出现警告信息。

为了避免导入的内容和已有内容重名,可以使用 as 关键字为导入的内容设置别名。导入命名空间并设置别名的语法格式如下:
use 空间成员 as 别名;
为空间成员设置别名后,后续的代码中只能使用别名进行操作。

下面对导入命名空间和导入空间成员分别进行讲解。

1) 导入命名空间

导入命名空间通常指在类中导入其他类的命名空间,导入其他类的命名空间后,就可以在类中直接使用。

下面演示导入命名空间,具体步骤如下:
① 创建 StudentController.php 文件,具体代码如下:
<?php
namespace App\Http\Controllers;

class StudentController
{
    public static function introduce()
    {
        return __CLASS__;
    }
}
在上述代码中,StudentController 类放在了 App\Http\Controller 命名空间中。第 8 行代码使用魔术常量 __CLASS__ 获取当前被调用的类名,该类名是包含类所在的命名空间层级的完整类名。

② 创建 Container.php 文件,具体代码如下:
<?php
namespace myframe;

use App\Http\Controllers;

class Container
{
    public static function student()
    {
        return Controllers\StudentController::introduce();
    }
}
在上述代码中,第 4 行代码导入了 App\Http\Controllers 命名空间,第 10 行代码调用了 StudentController 类的 introduce() 方法。

③ 创建 namespace04.php 文件,具体代码如下:
<?php
use myframe\Container;
require './StudentController.php';
require './Container.php';
echo Container::student();
// 输出结果:App\Http\Controllers\StudentController
在上述代码中,第 2 行代码导入了 myframe 命名空间下的 Container 类,第 5 行代码使用 Container 类调用 student() 方法。

上述代码的输出结果为“App\Http\Controllers\StudentController”,说明访问 Container 类的 student() 方法时,实际访问的是 StudentController 类的 student() 方法。

下面演示导入 StudentController 类并设置别名 Student:
① 修改 Container.php 的第 4 行代码,修改结果如下:
use App\Http\Controllers\StudentController as Student;

② 修改 Container.php 的第 10 行代码,使用别名调用类中的方法,具体代码如下:
return Student::introduce();
运行上述代码后,输出结果和修改前的相同。

2) 导入空间成员

下面演示导入空间成员,即其他命名空间下的函数和常量的命名空间,具体步骤如下:
① 创建 function.php 文件,具体代码如下:
<?php
namespace myframe;

const PREFIX = 'pre_';
function getFullName($name)
{
    return PREFIX . $name;
}
在上述代码中,定义了 PREFIX 常量和 getFullName() 函数。

② 创建 namespace05.php 文件,访问 PREFIX 常量和 getFullName() 函数,具体代码如下:
<?php
use const myframe\PREFIX;
use function myframe\getFullName;
require 'function.php';
echo PREFIX, getFullName('test');     // 输出结果:pre_ pre_test
从上述输出结果可以看出,常量和函数都已经导入成功了。

【扩展】使用use语句导入多个空间成员

PHP 允许一条 use 语句导入多个空间成员,同时也可以为导入的空间成员设置别名,具体语法格式如下:
use 空间成员1, 空间成员2 as 别名2;
在上述语法格式中,将空间成员 1 和空间成员 2 进行导入,并为空间成员 2 设置了别名。

使用一条 use 语句还可以将同一个命名空间下的多个空间成员批量导入并分别设置别名,具体语法格式如下:
// 类
use 命名空间\{类名1 as 别名, 类名2 as 别名, 类名3 as 别名, …};    
// 函数
use function 命名空间\{函数名1 as 别名, 函数名2 as 别名, 函数名3 as 别名, …};
// 常量
use const 命名空间\{常量名1 as 别名, 常量名2 as 别名, 常量名3 as 别名, …}; 

相关文章