命名空间
1. 非限定名称, 限定名称, 完全限定名称
- PHP 命名空间可以解决以下两类问题:
- 用户编写的代码与 PHP 内部的类(接口)/函数/常量或第三方类/函数/常量之间的名字冲突。
- 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
::class
获取类的完整名称(带上命名空间)
基础:命名空间的基本格式
<?php
// 定义代码在 'MyProject' 命名空间中
namespace MyProject;
// ... 代码 ...
<html>
<?php
namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
?>
</html>
<?php
//创建空间Nicola
namespace Nicola;
class Comment
{
}
//非限定名称,表示当前Nicola空间
//这个调用将被解析成 Nicola\Comment();
$Nicola_comment = new Comment();
//限定名称,表示相对于Nicola空间
//这个调用将被解析成 Nicola\Article\Comment();
$article_comment = new Article\Comment(); //类前面没有反斜杆\
//完全限定名称,表示绝对于Nicola空间
//这个调用将被解析成 Nicola\Comment();
$article_comment = new \Nicola\Comment(); //类前面有反斜杆\
//完全限定名称,表示绝对于Nicola空间
//这个调用将被解析成 Nicola\Article\Comment();
$article_comment = new \Nicola\Article\Comment(); //类前面有反斜杆\
//创建Nicola的子空间Article
namespace Nicola\Article;
class Comment
{
}
- 非限定名称,或不包含前缀的类名称,例如
$comment = new Comment();
。如果当前命名空间是Nicola\Article,Comment
将被解析为Nicola\Article\Comment
。如果使用Comment
的代码不包含在任何命名空间中的代码(全局空间中),则Comment
会被解析为Comment
。 - 限定名称,或包含前缀的名称,例如
$comment = new Article\Comment();
。如果当前的命名空间是Nicola
,则Comment
会被解析为Nicola\Article\Comment
。如果使用Comment
的代码不包含在任何命名空间中的代码(全局空间中),则Comment
会被解析为Comment
。 - 3.完全限定名称,或包含了全局前缀操作符的名称,例如
$comment = new \Article\Comment();
。在这种情况下,Comment 总是被解析为代码中的文字名(literal name)Article\Comment
。其实可以把这三种名称类比为文件名(例如 comment.php)、相对路径名(例如 ./article/comment.php)、绝对路径名(例如 /blog/article/comment.php),这样可能会更容易理解。
2.实例演示: 命名空间的别名, 与类别名
- PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。
- 在 PHP 中,别名是通过操作符 use 来实现的. 下面是一个使用所有可能的三种导入方式的例子
<?php
//a目录下的b
namespace a\b {
//a.php
class A{
function __construct(){
echo "A in ".__NAMESPACE__;
}
}
class B{
function __construct(){
echo "B in ".__NAMESPACE__;
}
}
function foo(){
echo "foo in ".__NAMESPACE__;
}
}
<?php
// a目录下的c
namespace a\c {
//b.php
class B{
function __construct(){
echo "B in ".__NAMESPACE__;
}
}
function f(){
echo "foo in ".__NAMESPACE__;
}
}
?>
<?php
// a目录下面的c
namespace a\c{
//c.php
include 'a.php';
include 'b.php';
use a\b\A as B,a\b\B as C;
use function a\b\foo as f;
f();//调用的是a\b\foo
namespace\f();//调用的是a\c\f
var_dump(new B());//创建的是a\b\A的对象
var_dump(new namespace\B());//创建的是a\c\B的对象
var_dump(new C());//创建的是a\b\B的对象
}
?>
3. 实例演示: 类文件的自动加载器实现的原理与实现
- 类的自动加载是指,在外面的页面中,并不需要去“引入”类文件,但是程序会在需要的时候动态加载需要的类文件。
- 在 PHP 开发过程中,如果希望从外部引入一个
class
,通常会使用include
和require
方法,去把定义这个class
的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,这么做会产生大量的require
或者include
方法调用,这样不因降低效率,而且使得代码难以维护,况且require_once
的代价很大。 spl_autoload_register()
此函数的功能就是把函数注册至 SPL 的autoload
函数栈中,并移除系统默认的autoload()
函数
<?php
spl_autoload_register(function ($name) {
include __DIR__ . '/autoload/' . $name . '.class.php';
});
$autoA = new AutoA();
var_dump($autoA);
尽管
autoload()
函数也能自动加载类和接口,但更建议使用spl_autoload_register()
函数。spl_autoload_register()
提供了一种更加灵活的方式来实现类的自动加载(同一个应用中,可以支持任意数量的加载器,比如第三方库中的)。因此,不再建议使用autoload()
函数,在以后的版本中它可能被弃用。
总结
本节课重点学习了命名空间的知识,在通常的 PHP 开发中,除了使用自己的代码以外,往往会使用很多其他的 PHP 组件。这些组件代码可能会使用相同的类名、接口名、函数或者常量名等,如果不使用命名空间就会导致命名冲突,使 PHP 执行出错。而将代码放到各自唯一的命名空间中,我们的代码就可以和其他开发者使用相同的类名、接口名、函数或者常量名等,这在团队合作中相当重要。