PHP - 命名空间


我们经常将文件组织在不同的文件夹中。通常,文件夹包含与特定目标、应用程序或类别相关的文件。一个文件夹不能包含两个同名的文件,但不同的文件夹可能有一个同名的文件,因此每个文件的路径不同。

PHP 命名空间的思想有点相似。在 PHP 中,命名空间允许在不同的上下文中使用同名的类、函数或常量,而不会发生任何冲突,从而封装了这些项目。

PHP 命名空间是类/函数等的逻辑分组,具体取决于它们的相关性。正如同名文件可以存在于两个不同的文件夹中一样,可以在两个命名空间中定义具有特定名称的类。此外,当我们指定文件的完整路径以获得访问权限时,我们需要指定类的全名以及命名空间。

随着应用程序大小变大,涉及许多类和函数定义,为每个类/函数赋予一个唯一的名称可能会变得乏味,而且并不完全优雅。使用命名空间可以让你以整洁的方式组织这样的代码块。例如,如果我们需要声明一个 calculate() 函数来计算 areatax,而不是将它们定义为 calculate_area() 和 calculate_tax() 之类的东西,我们可以创建两个命名空间 area 和 tax 并在其中使用 calculate()

命名空间的优点

以下是在 PHP 中使用命名空间的一些优点 -

  • 命名空间有助于避免由具有第三方类/函数/常量的人定义的类/函数/常量之间的名称冲突。
  • 命名空间提供了别名(或缩短)Extra_Long_Names的能力,从而提高了源代码的可读性。
  • PHP 命名空间提供了一种对相关类、接口、函数和常量进行分组的方法。命名空间名称不区分大小写。

定义命名空间

PHP 命名空间关键字用于定义新的命名空间。


 namespace myspace;

包含命名空间的 “.php” 文件必须在文件顶部声明命名空间,然后再声明任何其他命名空间(declare 指令除外)。命名空间内的类、函数和常量的声明会影响其访问。

PHP 脚本可以包含除命名空间定义之外的其他代码。要加载同一代码中定义的命名空间,PHP 有 “use” 关键字。


 use myspace;

示例

在下面的 “hello.php” 脚本中,我们在 myspace 命名空间中定义了一个 hello() 函数,并在当前脚本中加载命名空间后调用它。


<?php
   namespace myspace;
   function hello() {
      echo "Hello World";
   }
   use myspace;
   myspace\hello();
?>

它将产生以下输出 -

Hello World

注意:必须使用包含命名空间 myspace\hello() 的全名来限定 hello() 函数。

包含命名空间

您可能有一个脚本由命名空间的声明组成,另一个脚本在其中命名空间加载了 包含(include) 语句。

a.php


<?php
   namespace myspace {
      function hello() {
         echo "Hello World in myspace";
      }
   }
?>

b.php


<?php
   include 'a.php';
   myspace\hello();
?>

它将产生以下输出 -

Hello World in myspace

在某些情况下,当前脚本(如上所述的 “b.php”)也具有与包含文件中同名的函数。在命名空间前面添加的完全限定函数可帮助解析器解决名称冲突。

示例

请看下面的例子 -


<?php
   include 'a.php';
   function hello() {
      echo "Hello World from current namespace";
   }
   hello();
   myspace\hello();
?>

它将产生以下输出 -

Hello World from current namespace
Hello World in myspace

示例

如上所述,命名空间声明必须位于顶部,紧跟在开始的 <?php 标签之后。否则,解析器将引发致命错误。


<?php
   echo "hello"
   namespace myspace;
   function hello() {
      echo "Hello World";
   }
   use myspace;
   myspace\hello();
?>

它将产生以下输出 -

PHP Parse error:  syntax error, unexpected token "namespace", 
expecting "," or ";" in /home/cg/root/67771/main.php on line 4

上面的错误消息清楚地表明,只允许 “描述语句” 出现在命名空间声明之前。


<?php
   declare (strict_types=1);
   namespace myspace;
   function hello() {
      echo "Hello World";
   }
   use myspace;
   myspace\hello();
?>

相对命名空间

函数、类和常量等对象可以通过引用具有相对命名空间路径访问当前的命名空间。

在以下示例中,“b.php” 包含一个命名空间 space1\myspace,其中包含一个 hello() 函数和一个 TEMP 常量。相同的对象也在命名空间 space1 中定义,存在于 “a.php” 中。

显然,当 “b.php” 包含在 “a.php” 中时,“myspace” 是 “space1” 的子空间。因此,来自 “myspace” 的 hello() 是通过为其相对命名空间(也是 TEMP 常量)添加前缀来调用的

b.php  


<?php
   namespace space1\myspace;
   const TEMP = 10;
   function hello() {
      echo "Hello 来自当前命名空间:" . __NAMESPACE__ . ;
   }
?>

a.php  


<?php
   namespace space1;
   include 'b.php';
   function hello() {
      echo "Hello 来自当前命名空间:" . __NAMESPACE__ . ;
   }
   const TEMP = 100;
   hello();            // 当前命名空间
   myspace\hello();   // 子命名空间

   echo "TEMP : " . TEMP . " in " . __NAMESPACE__ . ;
   echo "TEMP : " . myspace\TEMP  . " \\in space1\\myspace\n";
?>

它将产生以下输出 -

Hello 来自当前命名空间:space1
Hello 来自当前命名空间:space1\myspace
TEMP : 100 in space1
TEMP : 10 in space1\myspace

绝对命名空间

您还可以通过在绝对命名空间路径前添加前缀来从任何命名空间访问函数/常量。例如,“b.php”的 hello() 是 “\space\myspace\hello()”。

a.php  


<?php
   namespace space1;
   include 'b.php';
   function hello() {
      echo "Hello from current namespace:" . __NAMESPACE__ . ;
   }
   const TEMP = 100;
   \space1\hello();                 // 当前命名空间
   \space1\myspace\hello();        // 子命名空间

   echo "TEMP: " . \space1\TEMP . " in " . __NAMESPACE__ . ;
   echo "TEMP: " . \space1\myspace\TEMP  . " in space1\\myspace\n";
?>

__NAMESPACE__ 是 PHP 中的预定义常量,它返回当前命名空间的名称。

命名空间规则

通过遵循以下规则来解决出现在不同命名空间之间的函数/类/常量名称中的任何冲突 -

  • 没有命名空间分隔符符号 (/) 的命名空间标识符表示它引用当前命名空间。这是一个非限定名称。
  • 如果它包含 myspace\space1 中的分隔符符号,则解析为 myspace 下的子命名空间 space1。这种类型的命名是相对命名空间。
  • 完全限定的命名空间的名称以 “\” 字符开头。例如,“\myspace” 或 “\myspace\space1”。
  • 完全限定名称解析为绝对命名空间。例如,\myspace\space1 解析为 myspace\space1 命名空间
  • 如果名称出现在全局命名空间中,则删除 “namespace\” 前缀。例如,“namespace\space1” 解析为 space1
  • 但是,如果它发生在另一个命名空间内,则会以不同的方式处理它。例如,如果 namespace\space1 位于 myspace 内,则它等效于 “myspace\space1”。
  • 限定名称中名称的第一段根据当前的类/命名空间导入表进行翻译。
  • 如果未应用导入规则,则当前命名空间将添加到名称前面。
  • 类名称根据类/命名空间导入表进行翻译,函数名称根据函数导入表进行翻译,常量根据常量导入表进行翻译。
  • 对于非限定名称,如果未应用导入规则,并且名称引用函数或常量,并且代码位于全局命名空间之外,则在运行时解析该名称。首先,它从当前命名空间中查找函数,然后尝试查找并调用全局函数。