红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 编程语言
  3. PHP
  4. 正文

PHP学习笔记14:命名空间

2021年12月9日 1447点热度 0人点赞 0条评论

image-20211129162010327

图源:php.net

在php没有引入命名空间之前,构建比较大的应用时可能会遇到命名冲突的问题,比如:

<?php
// a.php
class MyClass{
    
}
<?php
// b.php
require_once './a/a.php';
class MyClass{
    // PHP Fatal error:  Cannot declare class MyClass, because the name is already in use in ...
   
}

如果是多人协同开发的大型项目,或者需要使用第三方库,这种因为引用其它php代码而导致的命名冲突是很容易出现的。而手动修改所冲突名称也是一个不现实的解决方式。

因此php引入了命名空间。

定义

定义一个命名空间很简单:

<?php
namespace xyz\icexmoon\php_notes\ch14;

但需要注意的是,namespace语句必须是除了PHP标签之外的当前代码文件的第一行语句(declare语句除外)。即使是其它非php语句也不能出现在namespace语句之前:

<html>
<?php
namespace xyz\icexmoon\php_notes\ch14;
// PHP Fatal error:  Namespace declaration statement has to be the very first statement or after any declare call in the script in ...

命名空间的命名习惯是使用域名来避免可能的出现的重复,举例来说,我个人的域名是icexmoon.xyz,其中xyz是顶级域名,icexmoon是二级域名,所以我可以用xyz\icexmoon\project_name来定义我的某个php项目的命名空间。这肯定不会与其它人定义的命名空间冲突。

拥有域名的公司、组织和个人都可以使用这种方式定义。

如果是没有购买域名的个人,还可以借助免费的代码托管网站域名来定义命名空间,比如我的Github主页是http://github.com/icexmoon,我就可以将php项目的命名空间定义为com\github\icexmoon,一般来说这也是不会出现重复的。

现在我们看如何使用命名空间解决命名冲突:

<?php
// a.php
namespace xyz\icexmoon\php_notes\ch14\conflict2\A;
class MyClass{
    
}
<?php
// b.php
namespace xyz\icexmoon\php_notes\ch14\conflict2;
require_once './a/a.php';
class MyClass{
   
}
$mc = new MyClass;
$mc1 = new A\MyClass;

php的同一个命名空间可以出现在多个代码文件中,也就是拆分为多个代码文件。一般来说命名空间的层级结构是和代码目录结构完全一致的,所以也就是说一个目录中的代码文件共享一个命名空间。

这和Go语言的包是一致的。

比较特别的是,一个代码文件还可以有多个命名空间:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\define3\A {
    class MyClass{}
}
​
namespace {
}
​
namespace xyz\icexmoon\php_notes\ch14\define3\B {
    use xyz\icexmoon\php_notes\ch14\define3\A\MyClass as MyClass2;
    class MyClass{}
    $mc = new MyClass();
    $mc2 = new MyClass2();
}

此时定义的命名空间需要使用{}来区分,并且同一文件中定义的全局代码需要定义在代表全局命名空间的namespace {...}中。

名称解析规则

在使用命名空间的代码中使用类、函数、常量时有四种命名空间使用方式:

  • 非限定名称,如new MyClass

  • 限定名称,如new A\MyClass

  • 完全限定名称,如new \xyz\icexmoon\php_notes\ch14\conflict2\A\MyClass

  • 相对名称,如new namespace\MyClass

非限定名称,也就是当前命名空间中那些没有任何前缀的类名、函数名、常量,这就像没有引入命名空间时那样的代码,此时对于类名,如果当前命名空间中有定义,就使用该类定义,如果没有,就会报错:

<?php
namespace xyz\icexmoon\php_notes\ch14;
$mc = new MyClass();
// PHP Fatal error:  Uncaught Error: Class "xyz\icexmoon\php_notes\ch14\MyClass" not found in ...
<?php
namespace xyz\icexmoon\php_notes\ch14;
class MyClass{
​
}
$mc = new MyClass();

函数和常量的非限定名称的解析过程与类不同,如果当前命名空间中存在就使用,如果不存在,会在全局命名空间中查找,如果存在就使用,否则报错。这么设计是有意义的,因为诸如int_val和is_array之类的内置函数是非常常见的,如果每次使用都需要添加上全局命名空间名称使用(比如\int_val)就太麻烦了,常量也一样:

<?php
namespace xyz\icexmoon\php_notes\ch14;
echo intval("123");
// 123
<?php
​
namespace xyz\icexmoon\php_notes\ch14;
​
function intval(array $arr): int
{
    $total = 0;
    foreach ($arr as $val) {
        $total += \intval($val);
    }
    return $total;
}
echo intval([1, 2, 3]) . PHP_EOL;
echo \intval("123") . PHP_EOL;
// 6
// 123

限定名称指的是subnamespace\class_name这样的类名、函数名、常量。会被结合当前命名空间解析,比如如果当前命名空间是A\B,则C\MyClass会被解析为A\B\C\MyClass:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\analysis3\my_class;
​
function print_namespace()
{
    echo "namespace:" . __NAMESPACE__ . PHP_EOL;
}
<?php
​
namespace xyz\icexmoon\php_notes\ch14\analysis3;
require_once './my_class/my_class.cls.php';
function print_namespace()
{
    echo "namespace:" . __NAMESPACE__ . PHP_EOL;
}
print_namespace();
my_class\print_namespace();
// namespace:xyz\icexmoon\php_notes\ch14\analysis3
// namespace:xyz\icexmoon\php_notes\ch14\analysis3\my_class

魔术常量__NAMESPACE__可以代表代码所在的命名空间名称。

完全限定名称指的是\开头的完整的命名空间指定的类名、函数名、常量,这样的命名会明确地指向一个命名空间中的定义:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\analysis6;
​
require_once './my_class/my_class.cls.php';
function print_namespace()
{
    echo "namespace:" . __NAMESPACE__ . PHP_EOL;
}
print_namespace();
\xyz\icexmoon\php_notes\ch14\analysis6\my_class\print_namespace();
// namespace:xyz\icexmoon\php_notes\ch14\analysis6
// namespace:xyz\icexmoon\php_notes\ch14\analysis6\my_class

可以使用完全限定名称来指定内置类、常量、函数,比如\intval或\PHP_EOL。

相对名称指的是使用namespace定义的类名、函数名、常量,其中namespace可以替换为当前命名空间的名称:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\analysis7;
​
require_once './my_class/my_class.cls.php';
function print_namespace()
{
    echo "namespace:" . __NAMESPACE__ . PHP_EOL;
}
print_namespace();
namespace\my_class\print_namespace();
// namespace:xyz\icexmoon\php_notes\ch14\analysis7
// namespace:xyz\icexmoon\php_notes\ch14\analysis7\my_class

导入命名空间

如果每次使用其它命名空间的函数或类都要使用限定名称或完全限定名称,那无疑会相当麻烦。事实上使用命名空间时,最常见的是将需要使用到的其它命名空间直接导入到当前命名空间。导入的可以是类、函数、常量,或者是命名空间。

导入整个命名空间意味着可以使用该命名空间中的常量、类、函数:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\use;
​
require_once './my_class/my_class.php';
​
use xyz\icexmoon\php_notes\ch14\use\my_class;
​
$mc = new my_class\MyClass();
$mc->number = 10;
my_class\print_my_class($mc);
echo my_class\NAME_SPACE . PHP_EOL;
// MyClass(10)
// xyz\icexmoon\php_notes\ch14\use\my_class

也可以直接导入命名空间中的类名、常量、函数:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\use;
​
use xyz\icexmoon\php_notes\ch14\use\my_class\MyClass;
​
use const xyz\icexmoon\php_notes\ch14\use\my_class\NAME_SPACE;
​
use function xyz\icexmoon\php_notes\ch14\use\my_class\print_my_class;
​
require_once './my_class/my_class.php';
$mc = new MyClass();
$mc->number = 10;
print_my_class($mc);
echo NAME_SPACE . PHP_EOL;
// MyClass(10)
// xyz\icexmoon\php_notes\ch14\use\my_class

如果导入的名称和当前命名空间中的名称冲突,可以将其定义为别名:

<?php
​
namespace xyz\icexmoon\php_notes\ch14\use;
​
use xyz\icexmoon\php_notes\ch14\use\my_class\MyClass;
​
use const xyz\icexmoon\php_notes\ch14\use\my_class\NAME_SPACE;
​
use function xyz\icexmoon\php_notes\ch14\use\my_class\print_my_class as print_myclass;
​
require_once './my_class/my_class.php';
function print_my_class(MyClass $mc)
{
    echo "redeclared print_my_class function." . PHP_EOL;
    echo "MyClass[number=>{$mc->number}]" . PHP_EOL;
}
$mc = new MyClass();
$mc->number = 10;
print_myclass($mc);
// MyClass(10)
print_my_class($mc);
// redeclared print_my_class function.
// MyClass[number=>10]
echo NAME_SPACE . PHP_EOL;
// xyz\icexmoon\php_notes\ch14\use\my_class

以上就是命名空间的全部内容。

谢谢阅读。

往期内容

  • PHP学习笔记13:类和对象 V

  • PHP学习笔记12:类和对象IV

  • PHP学习笔记11:类和对象 III

  • PHP学习笔记10:类和对象 II

  • PHP学习笔记9:类和对象 I

  • PHP学习笔记8:函数

  • PHP学习笔记7:控制流

  • PHP学习笔记6:表达式和运算符

  • PHP学习笔记5:常量

  • PHP学习笔记4:变量

  • PHP学习笔记3:其它类型和类型声明

  • PHP学习笔记2:数组

  • PHP学习笔记1:基础

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2021年12月9日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

取消回复

*

code

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号