PHP 命名空间 解惑

1. PHP中的命名空间是什么?

官方解释在此: 命名空间概述

命名空间用一句话说,就是:把 类、函数、变量 等放到逻辑子文件夹中去,以避免命名冲突。

注:命名空间跟实际代码文件在文件系统中的路径没有任何关系,如果你感到迷茫,可以看看本文最后一节。

2. 被隐藏的第一个 \

在每个 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,顶级命名空间标识(第一个 \ )被省略了。

3. 被隐藏的别名

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

use TinyLara\TinyView\TinyView

的完整写法应该是:

use \TinyLara\TinyView\TinyView as TinyView;

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

4. 使用绝对路径直接调用

<?php namespace TinyLara\TinyRoute;

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

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

5. 命名空间的实际价值

命名空间的存在是为了解决下面两个问题:

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

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

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

<?php namespace A;

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

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

直接调用即可。

7. 关于代码文件的结构

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. 类映射加载优化

WRITTEN BY

avatar
2014.10.7   /   热度:26927   /   分类: PHP

评论:

Leon
2016-11-11 21:07
取消自动加载  性能会上升一下吧
kevin
2016-08-01 21:45
博主文字通俗易懂,上乘之作,特地留言
byron
2016-04-11 10:01
同意楼上的话,博主适合出书。你的博文通俗易懂.
swing07
2016-03-03 17:05
LZ   这两行的真实地址是: \TinyLara\TinyRoute、\TinyLara\TinyView\TinyView,顶级命名空间标识(第一个 \ )被省略了。你的文章是否有误导的地方了。
PHP命名空间中的  foo\Another 并不一定是指:当前目录下有个foo目录,里面有个Another 文件,文件内有个 Another Class 这个概念是完全错误的!(或者说这是一种习惯或规范)。

!!PHP命名空间的注册仅仅是一个字符符号注册而已,和任何目录、文件没有关系!!

所以命名空间的操作 一般会借助于 autoload 来自动加载文件,说白了还是需要 require 文件的。

只不过这个过程被 autoload 消化掉了,至于 autoload 怎么去引入文件 那要看你怎么编写 autoload 函数了。

例如:当autoload接收到参数为 foo\Another 时,把 foo 替换成 fooPlugin 然后去目录找文件 fooPlugin/Another.php 也很正常。

然后进行 require 文件,当 require 成功时(Another.php 内肯定要声明自己的命名空间,达到注册的目的), 此时你使用 foo\Another 系统会在已注册的命名空间符号内搜索(上面已经注册了):foo\Another直接实例化。

总结:

1、namespace 仅仅是符号注册! 和目录,文件没关系,具体引入文件需要依赖于 autoload。

2、补充autoload知识:当用户去实例化一个不存在的类/命名空间时,会触发用户已经注册的 autoload 函数,在此函数内可以进行字符串目录、文件拼接,最后require进来相应文件。此时 这个类就堂而皇之的存在了。So... 你 new 成功了。

autoload注册详见:http://cn2.php.net/manual/zh/function.spl-autoload-register.php
JohnLui
2016-03-04 10:53
@swing07:我欣赏你的激动。你需要仔细地读这篇文章。你说得是对的。
lvv787
2016-06-03 12:51
@swing07:@swing07:你说的是对的,“PHP命名空间中的  foo\Another 并不一定是指:当前目录下有个foo目录,里面有个Another 文件,文件内有个 Another Class 这个概念是完全错误的!(或者说这是一种习惯或规范)。仅仅是一个字符符号注册而已,和任何目录、文件没有关系”
xoolee
2016-02-13 20:06
自己毕业也两三年了,平时对web前后端都挺有兴趣的,不过很长时间没有碰过了,现在想辞职干相关的。现在在看laravel的教程
不知道现在php后端还可以嘛?是不是移动android ios开发的多啊。相比较呢?看博主ios php都在做 ,特请教
Snails
2015-08-04 17:33
博主,我想问问“namespace TinyLara\TinyRoute”中的“TinyLara\TinyRoute”是什么东东,一个文件吗?
JohnLui
2015-08-04 17:44
@Snails:声明当前类的命名空间。类似于目录。
不堪回首
2015-06-20 23:33
同一命名空间下的类不用引入就能相互调用吗?
JohnLui
2015-06-21 01:38
@不堪回首:对
大表哥
2015-11-21 00:36
@JohnLui:老兄弟呀,静态方法不实例化不能调用么,只是命名空间更为优雅,符合短方法名称调用。

命名空间官方手册:
在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
1、用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
2、为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

老夫看了一天laravel的源码,总结出以下几点:
1、项目主导性太强了,如果没有改装实力,容易让项目代码架构被框架主导,使用laravel得不偿失;
2、文章也提到yum安装,在极大程度上面,yum安装的灵活性不是很好,所以想要灵活的使用laravel,害得进行二次编译
3、laravel一直以优雅著称,他的优雅是建立在二次开发的基础上面的,当然这归功于laravel架构盘子拉得足够大的体现,同时也给予程序员无限创造力
4、像一般市场上的开源框架,如ZF,CI的主导性本身来说都没有laravel重
至于laravel为什么好用,也是依靠各种傻瓜式操作,使得项目能够快速上线。总体来时,laravel也是ci和TP这种入门级框架不能比拟的。上面提到了强悍的路由和视图解析以及框架自身设计理念,如果稍加改进,开发企业级项目也很靠谱了。总之,laravel如果落到有设计实力的团队手里,就是一朵花,反之,就是一枯草。

我今天看了一天的laravel,对整个项目,老夫以为,把底层部署和路由这些东西改成项目需要的,就相当于项目完成一半了。评论框不能上传图片,不然还真找你指点指点。

不好意思,愤青了一把。不过个人以为,laravel的成功归功于框架设计理念和后期创造空间。命名空间解决的途径多了去了,比如:TinyLara_TinyView_TinyView::fuck();
tom
2016-03-16 17:17
@不堪回首:可以参考这个文章:http://christoph-rumpel.com/2016/03/using-the-class-keyword-in-PHP-and-Laravel
张全蛋
2016-07-26 18:00
@不堪回首:这个文章说的应该是  laravel 框架下的..
如果裸的php环境下 还是需要先引入的
nil
2016-08-18 17:41
@张全蛋:是的。博主举得例子说的不严谨。
比如a.php 和b.php  不引入 能直接调用?
钧泽
2015-06-02 18:49
博主网站很不错哦,交换个友链行不行?
JohnLui
2015-06-02 18:54
@钧泽:对不起,友链只加线下认识的人~
呵呵
2015-05-04 18:29
不错..
一只中东土豪
2015-04-02 22:56
没想到博主这么博学多才 网站风格配色还如此清新可爱呢
bill_law6
2015-03-25 22:40
顶博主!

看完PHP语法直接入手LARAVEL4.2,然后发现需要深入了解LARAVEL框架解决一些问题,庆幸百度到了博主的文章,随着博主庖丁解牛般建立了PHP框架的概念,打开了框架学习的大门!再次感谢!
JohnLui
2015-03-25 23:44
@bill_law6:感谢支持 共同进步
你好
2016-08-22 15:07
@JohnLui:加油
alan
2015-03-22 13:30
博主的话,很通俗易懂。要是博主能出一本《PHP编程》的书就好了。现在市面上的,都没一本能把一个问题的真正核心说清楚明白。也许部分说清楚了,但往往也是长篇累牍。
JohnLui
2015-03-22 14:07
@alan:谢谢

发表评论:

© 2011-2017 岁寒  |  Powered by Emlog