PHP 面向对象程序设计(oop)学习笔记 (五) - PHP 命名空间


Posted in PHP onJune 12, 2014

命名空间概述

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

用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
PHP 命名空间提供了一种将相关的类、函数和常量组合到一起的途径。下面是一个说明 PHP 命名空间语法的示例:

定义命名空间

虽然任意合法的PHP代码都可以包含在命名空间中,但只有三种类型的代码受命名空间的影响,它们是:类,函数和常量。命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。另外,与PHP其它的语言特征不同,同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。当然你也可以在同一个文件中定义多个命名空间。

namespace MyProject;
class MyClass
{
    #code...
}

定义子命名空间:与目录和文件的关系很象,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:

namespace MyProject\helper\http;
class MyClass
{
    #code...
}

在同一个文件中定义多个命名空间:在同一个文件中声明多个命名空间有两种方式,不过在实际编程实践中,非常不提倡在同一个文件中定义多戈命名空间。这种方式的主要用于将多个 PHP 脚本合并在同一个文件中。下面列出第一种方法。

namespace MyProject\helper\http;
class MyClass
{
    #code...
}
namespace MyProject\helper\request;
class MyClass
{
    #code...
}

不过强烈不建议使用这种方法,可以参考下面的大括号定义法:

namespace MyProject\helper\http;
{
    class MyClass
    {
        #code...
    }
}
namespace MyProject\helper\request;
{
    class MyClass
    {
        #code...
    }
}

PHP 命名空间中的元素使用

在讨论如何使用命名空间之前,必须了解 PHP 是如何知道要使用哪一个命名空间中的元素的。类名可以通过三种方式引用:

非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。详情参见 使用命名空间:后备全局函数名称/常量名称。

限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。

完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。
使用命名空间:别名/导入

允许通过别名引用或导入外部的完全限定名称,是命名空间的一个重要特征。PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。在PHP中,别名是通过操作符 use 来实现的。

注意PHP不支持导入函数或常量。

namespace foo;
use My\Full\Classname as Another;
// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;
// 导入一个全局类
use \ArrayObject;

名称解析规则

在说明名称解析规则之前,我们先看一些重要的定义:

非限定名称Unqualified name:名称中不包含命名空间分隔符的标识符,例如 Foo
限定名称Qualified name:名称中含有命名空间分隔符的标识符,例如 Foo\Bar
完全限定名称Fully qualified name:名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 \Foo\Bar。 namespace\Foo 也是一个完全限定名称。
名称解析遵循下列规则:

对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B。
所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()。
在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e() 。
非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C() 。
在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:
1) 在当前命名空间中查找名为 A\B\foo() 的函数
2) 尝试查找并调用 全局(global) 空间中的函数 foo()。
在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new D\E() 的解析过程: new C()的解析:
在当前命名空间中查找A\B\C类。
尝试自动装载类A\B\C。

new D\E()的解析:
在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
尝试自动装载类 A\B\D\E。

为了引用全局命名空间中的全局类,必须使用完全限定名称 new \C()。

Example 名称解析示例

<?php
namespace A;
use B\D, C\E as F;
// 函数调用
foo();      // 首先尝试调用定义在命名空间"A"中的函数foo()
            // 再尝试调用全局函数 "foo"
\foo();     // 调用全局空间函数 "foo" 
my\foo();   // 调用定义在命名空间"A\my"中函数 "foo" 
F();        // 首先尝试调用定义在命名空间"A"中的函数 "F" 
            // 再尝试调用全局函数 "F"
// 类引用
new B();    // 创建命名空间 "A" 中定义的类 "B" 的一个对象
            // 如果未找到,则尝试自动装载类 "A\B"
new D();    // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
            // 如果未找到,则尝试自动装载类 "B\D"
new F();    // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
            // 如果未找到,则尝试自动装载类 "C\E"
new \B();   // 创建定义在全局空间中的类 "B" 的一个对象
            // 如果未发现,则尝试自动装载类 "B"
new \D();   // 创建定义在全局空间中的类 "D" 的一个对象
            // 如果未发现,则尝试自动装载类 "D"
new \F();   // 创建定义在全局空间中的类 "F" 的一个对象
            // 如果未发现,则尝试自动装载类 "F"
// 调用另一个命名空间中的静态方法或命名空间函数
B\foo();    // 调用命名空间 "A\B" 中函数 "foo"
B::foo();   // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
            // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"
D::foo();   // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
            // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"
\B\foo();   // 调用命名空间 "B" 中的函数 "foo" 
\B::foo();  // 调用全局空间中的类 "B" 的 "foo" 方法
            // 如果类 "B" 未找到,则尝试自动装载类 "B"
// 当前命名空间中的静态方法或函数
A\B::foo();   // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"
\A\B::foo();  // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>
PHP 相关文章推荐
PHP读MYSQL中文乱码的解决方法
Dec 17 PHP
一个PHP缓存类代码(附详细说明)
Jun 09 PHP
php文件缓存类汇总
Nov 21 PHP
PHP遍历XML文档所有节点的方法
Mar 12 PHP
Zend Framework动作助手Json用法实例分析
Mar 05 PHP
Yii2 rbac权限控制操作步骤实例教程
Apr 29 PHP
php把时间戳转换成多少时间之前函数的实例
Nov 16 PHP
简单解决微信文章图片防盗链问题
Dec 17 PHP
PHP实现的最大正向匹配算法示例
Dec 19 PHP
通过源码解析Laravel的依赖注入
Jan 22 PHP
PHP7如何开启Opcode打造强悍性能详解
May 11 PHP
Laravel5.1 框架数据库操作DB运行原生SQL的方法分析
Jan 07 PHP
新浪SAE云平台下使用codeigniter的数据库配置
Jun 12 #PHP
Codeigniter整合Tank Auth权限类库详解
Jun 12 #PHP
PHP错误Cannot use object of type stdClass as array in错误的解决办法
Jun 12 #PHP
php自动识别文件编码并转换为UTF-8的方法
Jun 12 #PHP
codeigniter集成ucenter1.6双向通信的解决办法
Jun 12 #PHP
使用CodeIgniter的类库做图片上传
Jun 12 #PHP
Codeigniter出现错误提示Error with CACHE directory的解决方案
Jun 12 #PHP
You might like
zen cart新进商品的随机排序修改方法
2010/09/10 PHP
PHP读取网页文件内容的实现代码(fopen,curl等)
2011/06/23 PHP
PHP header函数分析详解
2011/08/06 PHP
PHP is_subclass_of函数的一个BUG和解决方法
2014/06/01 PHP
PHP中加速、缓存扩展的区别和作用详解(eAccelerator、memcached、xcache、APC )
2016/07/09 PHP
阿里云PHP SMS短信服务验证码发送方法
2017/07/11 PHP
jquery 结合C#后台的数组对文章的关键字自动添加链接的代码
2011/07/15 Javascript
js 操作select和option常用代码整理
2012/12/13 Javascript
jquery formValidator插件ajax验证 内容不做任何修改再离开提示错误的bug解决方法
2013/01/04 Javascript
100个不能错过的实用JS自定义函数
2014/03/05 Javascript
Node.js模拟浏览器文件上传示例
2014/03/26 Javascript
json实现添加、遍历与删除属性的方法
2016/06/17 Javascript
javascript数组去重常用方法实例分析
2017/04/11 Javascript
nodejs制作爬虫实现批量下载图片
2017/05/19 NodeJs
npm 下载指定版本的组件方法
2018/05/17 Javascript
jQuery实现左右两个列表框的内容相互移动功能示例
2019/01/27 jQuery
nodejs实现日志读取、日志查找及日志刷新的方法分析
2019/05/20 NodeJs
JavaScript 处理树数据结构的方法示例
2019/06/16 Javascript
小程序和web画三角形实现解析
2019/09/02 Javascript
基于element-ui封装可搜索的懒加载tree组件的实现
2020/05/22 Javascript
vue 在methods中调用mounted的实现操作
2020/08/07 Javascript
Python中声明只包含一个元素的元组数据方法
2014/08/25 Python
Python使用filetype精确判断文件类型
2017/07/02 Python
Python Paramiko模块的使用实际案例
2018/02/01 Python
python sklearn常用分类算法模型的调用
2019/10/16 Python
Python通过字典映射函数实现switch
2020/11/06 Python
惠普墨西哥官方商店:HP墨西哥
2016/12/01 全球购物
校园之声广播稿
2014/01/31 职场文书
淘宝店铺营销方案
2014/02/13 职场文书
青春无悔演讲稿
2014/05/08 职场文书
2014年教师政治学习材料
2014/06/02 职场文书
2014年社区国庆节活动方案
2014/09/16 职场文书
学生违反校规检讨书
2014/10/28 职场文书
英文升职感谢信
2015/01/23 职场文书
《分数的意义》教学反思
2016/02/20 职场文书
微信小程序和php的登录实现
2021/04/01 PHP