命名空间

在ThinkPHP中接触到了一个新的概念,命名空间(namespaces),这个概念在PHP的CI框架中是没有的,看来CI框架确实简化了,不愧是被人嘲笑“玩具”框架,但是我倒是觉得CI本身就是奔着轻量级去的,你拿“玩具”来当工具使,本身就有问题啊。

命名空间是在PHP5.3的时候被引入的,(PHP 5 >= 5.3.0, PHP 7)。

什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。具体举个例子,文件 foo.txt 可以同时在目录/home/greg/home/other 中存在,但在同一个目录中不能存在两个 foo.txt 文件。另外,在目录 /home/greg 外访问 foo.txt 文件时,我们必须将目录名以及目录分隔符放在文件名之前得到 /home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念。


按照官方文档,在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

PHP 命名空间提供了一种将相关的类、函数和常量组合到一起的途径。


按照我的理解,就是避免冲突,就好像网络VLAN这个概念一样,隔离起来,避免冲突,从而提高了效率。

被隐藏的第一个 \

在每个 PHP 文件的最开始定义命名空间:

<?php namespace TinyLara\TinyRoute;

class TinyRoute {
  ...
}

在定义命名空间之后引入命名空间:

<?php namespace TinyLara\TinyRoute;

use TinyLara\TinyView\TinyView;

class TinyRoute {
  ...
}

上述代码中,

namespace TinyLara\TinyRoute
use TinyLara\TinyView\TinyView

这两行的真实路径是: \TinyLara\TinyRoute\TinyLara\TinyView\TinyView,顶级命名空间标识(第一个 \ )被省略了。

被隐藏的别名

在上一节中中,这一行代码

use TinyLara\TinyView\TinyView;

的完整写法应该是:

use \TinyLara\TinyView\TinyView as TinyView;

如果不指定别名,那就默认别名为类名。

使用绝对路径直接调用

<?php namespace TinyLara\TinyRoute;

class TinyRoute {
    public function foo()
    {
        return \TinyLara\TinyView\TinyView::fuck();
    }
}

使用绝对路径调用类时顶级命名空间标识(第一个 \ )不能省略。(很多人都在这个地方迷惑了)

命名空间的实际价值

目前非常流行的 Composer 就是一个基于命名空间的包管理器/依赖管理器,同样,Laravel 能达到今天的成功,很大程度上也是因为PHP5.3的普及,生恰逢时。你可以在 https://packagist.org/ 上下载到各种 composer 包,类似于 yum、npm或者gem。

同一命名空间下的类可以任意相互调用

<?php namespace A;

class ClassA {
  public static function test() {
    echo 'Success!';
  }
}
<?php namespace A;

class ClassB {
  public static function test() {
    ClassA::test(); // 直接调用即可
  }
}

关于代码文件的结构

PSR-4 命名空间规范约定了 PHP 类的命名空间应该和实际在文件系统中的位置一致,而现实中绝大多数 PHP 框架为了方便都采纳了这条规范,最明显的就是 Laravel 4 到 5 的转变。在这种情况下,我发现不少新手又迷茫了,错误地理解了我在上文中的阐述的“路径”的概念。基于此我要简单讲述一下 PHP 运行的基本流程,我相信看完你们就不会再有上面的误解了。

PHP 运行流程

在一个典型的 Apache + mod_php 架构的 PHP 运行环境中,一个 PHP 网站是这样运行的:

  1. Apache 收到用户的 HTTP 请求
  2. 这个请求是以 .php 结尾或者是一个不存在的路径(.htaccess 会将其转发到 index.php)
  3. Apache 的 mod_php 会启动一个新的 PHP 进程(PHP 解释器),读取 HTTP 请求的 URL 中的那个 .php 文件或者 index.php
  4. 被读取进 PHP 解释器的字符串被按照 PHP 的语法进行解析。为了方便理解,我们将这些经过解析的字符串所生成的 context(上下文)命名为 Matrix
  5. 然后 PHP 解释器会根据从 Matrix 中解析出的特定 PHP 语句(如 require)载入其他 PHP 文件,并将其内容以字符串的形式加入 Matrix
  6. 最终 Matrix 变成一个数万行代码的巨型上下文(为了便于理解可以想象成巨长的代码文件字符串),PHP 解释器会按照 PHP 语法执行 Matrix,进行数据库连接、网络请求、文件读写等操作
  7. 每一次的 echo 都会被写入到输出缓冲区,最终这个巨长的代码字符串被执行完毕,PHP 进程退出内存
  8. 缓冲区中就是要发给用户的 HTTP response,其实就是一堆字符串,只不过它遵守 HTML 规范,可以被浏览器解析。这一堆字符串被 Apache 发送回用户的浏览器,浏览器渲染,用户看到内容

命名空间在哪里?

命名空间从始至终就是一个“内部伪概念”,只是用于解决类和变量的命名冲突,从来就跟实际文件结构没有半毛钱的关系。让大家疑惑的其实是自动加载,当它和命名空间混杂在一起的时候,就不容易理解了。。命名空间从来就是一个纯 PHP 内部的概念,你可以把整个 Laravel 框架的所有文件合并成一个巨大的 PHP 文件,取消自动加载,除了性能会损失一些,功能不会受到任何影响,命名空间依旧运转良好。
实际上 Laravel 提供类似的功能:Laravel 5 程序优化技巧——3. 类映射加载优化

use关键字

在ThinkPHP中最开始的Home模块中有这样一段代码

<?php
// 本类由系统自动生成,仅供测试用途
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){
    .....
}

其中的use Think\Controller;我很不理解,而class IndexController extends Controller我能理解,因为这和CI框架中的控制器语法相似。
但是其实我对ThinkPHP的理解还是不足,在ThinkPHP官方文档中有以下这段

# 表示引入 Think\Controller 命名空间便于直接使用
use Think\Controller;
# 所以
use Think\Controller;
class IndexController extends Controller
# 等同于
class IndexController extends \Think\Controller

也就是说,use Think\Controller;引入了
namespace.PNG

将Think目录下的Controller 直接当作class IndexController extends ControllerController的别名了

stackoverflow有同样的解释

use My\Full\Namespace;
# is equivalent to
use My\Full\Namespace as Namespace;
# Namespace\Foo is now shorthand for My\Full\Namespace\Foo

Namespace = My\Full\Namespace


ThinkPHP5

使用了 use 来导入一个命名空间的类库,然后可以在当前文件中直接使用该别名而不需要使用完整
的命名空间路径访问类库。也就说,如果没有使用

use think\Controller;

就必须使用

class Index extends \think\Controller

这种完整命名空间方式。

参考资料:
PHP 命名空间 解惑
A Complete Guide to PHP Namespaces
PHP namespaces and “use”

标签: none

评论已关闭