命名空间
什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。具体举个例子,文件 foo.txt
可以同时在目录 /home/greg
和 /home/other
中存在,但在同一个目录中不能存在两个 foo.txt
文件。另外,在目录 /home/greg
外访问 foo.txt
文件时,我们必须将目录名以及目录分隔符放在文件名之前得到 /home/greg/foo.txt
。这个原理应用到程序设计领域就是命名空间的概念。
上面描述来自:php手册:命名空间概述
从上面的解释中我们可以清晰的看到,命名空间主要的作用就是方便管理类!就如下面的例子一样:当没有命名空间的时候,如果我们用下面的定义:
<?php
class Demo {
public function one ()
{
echo __METHOD__;
}
}
class Demo {
public function two ()
{
echo __METHOD__;
}
}
运行结果:
PHP Fatal error: Cannot redeclare class Demo
会直接报错的,因为类名相同导致冲突了。但是如果引入命名空间的话,就可以很方便的解决此冲突问题:
<?php
namespace One;
class Demo {
public function one ()
{
echo __METHOD__;
}
}
namespace Two;
class Demo {
public function two ()
{
echo __METHOD__;
}
}
$demo1 = new \One\Demo();
$demo2 = new \Two\Demo();
$demo1->one();
$demo2->two();
运行结果:
One\Demo::oneTwo\Demo::two
可见命名空间完美的解决了类名冲突的问题。
注意,上面代码只做演示,在实际的编程实践中,非常不提倡在同一个文件中定义多个命名空间。
自动加载
在没有命名空间之前,我们使用所需要的类的时候是这样的:
<?php
require_once 'glass/demo.php';
require_once 'glass/demo1.php';
require_once 'glass/demo2.php';
$demo = new Demo();
$demo1 = new Demo1();
$demo2 = new Demo2();
有了命名空间之后,我们使用类是这样的:
<?php
require_once 'autoload.php';
$demo = new Demo();
$demo1 = new Demo1();
$demo2 = new Demo2();
可以明显看到,第一种方法有以下缺点:
- 类加载死板不灵活
- 需要知道类的完整路径
反观第二种方法,全自动加载,不需要手动 require_once
;不需要知道类完整的路径!你说,哪一种使用方便呢?^ – ^.
__autoload() 函数
void __autoload ( string $class )
该函数的主要作用是在 PHP 运行中如果类未加载就会触发这个函数,PHP 会将未加载类的类名(包括命名空间)作为 __autoload
函数的第一个参数传递进来并执行函数。而 __autoload
函数就是的主要责任就是通过类名(包括命名空间)来加载这个类!
你可以通过定义这个函数来启用类的自动加载。
具体使用方法如下:
首先,在项目中创建 index.php
文件:
<?php
function __autoload ($className)
{
$filepath = $className . '.php';
if (!file_exists($filepath)) {
throw new Exception('FILE NOT FOUND.');
}
require_once $filepath;
}
$demo = new Demo();
$demo->echo();
紧接着创建 Demo.php
文件:
<?php
class Demo {
public function echo ()
{
echo __CLASS__;
}
}
启动 PHP 内置服务器,在命令行运行:
php -S localhost:3000
在浏览器输入 localhost:3000
,内容如下:
Demo
这样,一个简单的自动加载就完成了。整个过程非常简单:初始化一个未加载的类 -> PHP找不到这个类 -> 触发 __autoload
函数 -> __autoload
根据类型检测并加载当前目录下的类。
spl_autoload_register 函数
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
其主要作用和 __autoload
函数相同,都是用来加载类。不过 spl_autoload_register
函数可以定义多次,而 __autoload
只能定义一次。如果 spl_autoload_register
定义多次,那么在类加载的时候就会按照定义的顺序从上到下执行,一直到找到类或遍历完所有的 spl_autoload_register
为止。
注意:如果使用 spl_autoload_register 那么 __autoload 函数将失效。
参数 | 解释 |
---|---|
autoload_function | 欲注册的自动装载函数。 |
throw | 此参数设置了 autoload_function 无法成功注册时, spl_autoload_register()是否抛出异常。 |
prepend | 如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。 |
来看个 Demo 。就上面的示例代码来说,我们也可以这样写:
index.php
<?php
spl_autoload_register(function ($className) {
$filepath = $className . '.php';
if (!file_exists($filepath)) {
throw new Exception('FILE NOT FOUND.');
}
require_once $filepath;
});
$demo = new Demo();
$demo->echo();
命名空间 + 自动加载
且看下面的代码:
<?php
spl_autoload_register(function ($className) {
var_dump($className);
});
$demo = new \Xiao\Teng\Demo();
$demo->echo();
输出结果如下:
string(14) "Xiao\Teng\Demo"
可以看到,打印的值中包含了命名空间,此时我们将命名空间与目录映射就可以实现一个基本符合 PSR4
自动加载规范的自动加载啦!具体代码如下:
在项目根目录创建 index.php :
<?php
spl_autoload_register(function ($className) {
$filepath = str_replace('\\', '/', $className) . '.php';
if (!file_exists($filepath)) {
throw new Exception('FILE NOT FOUND.');
}
require_once $filepath;
});
$demo = new \Xiao\Teng\Demo();
$demo->echo();
紧接着创建 Xiao/Teng/Demo.php
:
<?php
namespace Xiao\Teng;
class Demo {
public function echo ()
{
echo __CLASS__;
}
}
运行结果如下:
Xiao\Teng\Demo
这里要注意两点,第一:命名空间与路径的映射要理清楚;第二:需要加载的类文件的命名空间要填写完整,否则还是提示类无法找到!
此篇为小滕的《Thinkphp5入门系列课程》第三篇。
喜欢的给个订阅呗!