图源:
<?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
以上就是命名空间的全部内容。
谢谢阅读。
文章评论