详细解读php的命名空间(一)


Posted in PHP onFebruary 21, 2018

php的命名空间功能已经出来很久了,但是一直以来没怎么深究过,这次赶着有时间所以特意翻着手册做一个整理和总结帮助自己完善完善,原本准备一篇写完,但发现内容其实还是蛮多的,放一起太长看着累,所以分两篇博客要好些。

一:命名空间概念:命名空间是一种封装事物的方法,类似于目录和文件。

命名空间解决的问题(手册上也写的很清楚,下面按照自己的理解简化了):

1:解决程序编写者自己写的类、常量、函数和php内部的或者第三方的出现名称冲突的情况。

2:创建别名,帮助解决类、常量、函数名称过长的情况,帮助提高代码的可读性,另外名称过长其实通常都是因为为了缓解第一类问题导致的。

二:如何定义命名空间

1:命名空间用关键字namespace声明,同时命名空间必须位于其他代码之前,包括任何非php代码以及空白符(php的declare关键字除外),否则会抛出一个fatal error。

例如:

<?php 
namespace Index; 
?>

注意1:如果命名空间namespace前没有任何代码及空白符,但还是出现fatal error,这个应该是由于bom头导致的,去掉bom头就可以了。
注意2:在命名空间下,虽然可以放置所有合法的php代码,但是受命名空间影响的仅有类(抽象类以及traits)和接口、常量和函数。

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

例如:

<?php 
namespace Index\Col\File; 
define('MESSAGE','hello world'); 
?>

3:一个文件中可以定义多个命名空间,定义的语法有两种,一种是简单组合语法,另一种是大括号形式语法,另外一个文件定义多个命名空间的使用一般是多个文件合并成一个文件的场景,但不到万不得已最好不要这样,因为这样增加了代码的复杂度,可读性会降低,一般情况也没有这种使用的必要

简单组合语法:

<?php 
namespace Index; 
const INSTANCE=1; 
 
namespace Col; 
const INSTANCE=2; 
?>

大括号语法,一个文件多个命名空间,如果还需要写上非命名空间的代码,就只能用大括号语法,并且非命名空间代码用namespace声明一个没有名称的命名空间,再用大括号即可:

<?php 
/*命名空间Index*/ 
namespace Index{ 
  const INSTANCE=1; 
} 
 
/*命名空间Col*/ 
namespace Col{ 
  const INSTANCE=2; 
} 
 
/*全局非命名空间代码*/ 
namespace { 
  const INSTANCE=3; 
} 
?>

4:多个不同的文件可以定义同一个命名空间,也就是说同一个命名空间的内容可以分别存储到多个不同的文件中,这里就不举例了。

三:命名空间的识别原理

命名空间的使用原理有三种情况,手册上其实说的详细但可能因为翻译问题导致一些凌乱,这里我简化一下用自己的例子梳理一下:

1:没有限定名称,也就是直接使用要读取的类、常量、函数、接口名称,这种情况会读取该内容所属的命名空间的类、常量、函数、接口名称,但如果命名空间内没有相关的数据,如果是类和接口名称会返回fatal error,如果是函数和常量会自动读取全局的函数和常量,如果全局中也没有,才会报fatal error。

下面举例:

<?php 
/*全局非命名空间代码*/ 
namespace { 
  const INSTANCE=1; 
 
  function test(){ 
    echo 1; 
  } 
 
  class foo{ 
    static function fool(){ 
          echo 1; 
        } 
  } 
 
  var_dump(INSTANCE);   //打印出来的是1 
 
  test();       //输出1 
 
  foo::fool();      //输出1 
 
} 
 
/*命名空间Index*/ 
namespace Index{ 
  const INSTANCE=2; 
 
  function test(){ 
    echo 2; 
  } 
 
  class foo{ 
    static function fool(){ 
          echo 2; 
        } 
  } 
 
  var_dump(INSTANCE);   //打印出来的是2 
 
  test();     //输出2 
 
  foo::fool();    //输出2 
} 
 
/*命名空间Col*/ 
namespace Col{ 
  const INSTANCE=3; 
 
  function test(){ 
    echo 3; 
  } 
 
  class foo{ 
    static function fool(){ 
          echo 3; 
        } 
  } 
 
  var_dump(INSTANCE);   //打印出来的是3 
 
  test();     //输出2 
   
  foo::fool();    //输出2 
} 
?>

上面的例子每个命名空间里输出的都没有限定名称,所以会得到当前命名空间下设置的对应数据值。

如果当前命名空间没有设置,函数和常量则会读取全局设置的对应数据值,全局没有对应的才会报fatal error,类和接口都会直接报fatal error,如下面代码所示。

<?php 
/*全局非命名空间代码*/ 
namespace { 
  const INSTANCE=1;  
  function test(){ 
    echo 1; 
  } 
 
  class foo{ 
    static function fool(){ 
          echo 1; 
        } 
  } 
 
  var_dump(INSTANCE);   //打印出来的是1  
  test();     //输出1  
  foo::fool();    //输出1  
} 
 
/*命名空间Index*/ 
namespace Index{ 
  var_dump(INSTANCE);   //打印出来的是1  
  test();     //输出1  
  foo::fool();    //fatal error 
 
} 
?>

2:限定名称,分为两种情况,一种是包含前缀的限定名称情况,一种是包含全局限定名称的情况。手册上将这两种单独分开了,但我觉得这两种可以合并成一起说,他们都是有限定名称,只是前者没有全局限定,后者有全局限定。

①包含前缀的限定名称,这种前缀可以有多个或者一个层级,但最左侧不能为\全局限定词,这种情况会读取该代码所在命名空间加上该前缀限定名称所对应数据,也就是:

所处命名空间\前缀限定\名称来读取,如果该代码是全局没有命名空间的,则直接用前缀限定名称来读取,也就是:前缀限定\名称来读取。

实例代码:

<?php 
/*命名空间Col\Index*/ 
namespace Col\Index{ 
  const INSTANCE=1; 
} 
 
/*命名空间Index*/ 
namespace Index{ 
  const INSTANCE=2; 
} 
 
/*命名空间Col*/ 
namespace Col{ 
  const INSTANCE=3; 
  var_dump(Index\INSTANCE); //打印出来的是1 读取的是Col\Index\INSTANCE 
} 
 
/*全局非命名空间代码*/ 
namespace { 
  const INSTANCE=4; 
  var_dump(Index\INSTANCE); //打印出来的是2 读取的是Index\INSTANCE 
} 
 
?>

②全局限定前缀名称:也就是在最左侧有全局操作符\进行修饰的前缀限定名称,当然也可以没有前缀限定直接全局操作符\加上名称也是可以的。但加上全局操作符后就跟目录里的绝对路径一样,只会按照全局限定后的所设置的进行读取。

具体实例如下:

<?php 
/*命名空间Col\Index*/ 
namespace Col\Index{ 
  const INSTANCE=1; 
} 
 
/*命名空间Index*/ 
namespace Index{ 
  const INSTANCE=2; 
} 
 
/*命名空间Col*/ 
namespace Col{ 
  const INSTANCE=3; 
  var_dump(\Index\INSTANCE); //打印出来的是2 读取的是Index\INSTANCE 
} 
 
/*全局非命名空间代码*/ 
namespace { 
  const INSTANCE=4; 
  var_dump(\Index\INSTANCE); //打印出来的是2 读取的是Index\INSTANCE 
} 
 
namespace Lin{ 
  const INSTANCE=5; 
  var_dump(\INSTANCE); //打印出来的是4 读取的是INSTANCE,是全局非命名空间里的INSTANCE,如果没有全局操作符\,读取的会是当前命名空间的Lin\INSTANCE=5 
} 
 
?>

四:命名空间在字符串中的转义

有时候命名空间会放在字符串中使用,如果是单引号不会通过编译器解释,所以没有任何问题,但是如果是双引号,那么就会有些意外情况了,要知道双引号里的内容是需要经过编译器进行解释然后再进行输出的,而\在编译器里的解释容易造成歧义

例如"index\name"这里就有\n会被解释成换行,除此之外还有很多这种造成意外的情况。

因此一般我们推荐命名空间如果要放在字符串中使用,最好使用单引号,一是效率,二是安全,如果使用双引号,则必须增加一个\进行转义避免歧义,例如"index\\name"这样就没有问题了。

随手双引号的举个例子:

<?php 
/*全局非命名空间代码*/ 
namespace Index\Name{ 
  class foo{ 
    function __construct(){ 
      echo 2; 
    } 
  } 
} 
 
namespace{ 
  $a= "Index\\Name\\foo"; //用\转义了\所以可以正常运行,但是如果去掉转义的话会报错Class 'Index\Nameoo',因为/f被解释成了换页符 
  $obj=new $a; 
}

这部分碍于篇幅就暂时到这里了,下一篇主要总结命名空间里的namespace和__NAMESPACE__的使用,以及别名的使用等。

PHP 相关文章推荐
Mysql数据库操作类( 1127版,提供源码下载 )
Dec 02 PHP
PHPUnit PHP测试框架安装方法
Mar 23 PHP
linux系统上支持php的 iconv()函数的方法
Oct 01 PHP
解析php中反射的应用
Jun 18 PHP
如何使用php判断服务器是否是HTTPS连接
Jul 05 PHP
调试PHP程序的多种方法介绍
Nov 06 PHP
PHP中使用php5-ffmpeg撷取视频图片实例
Jan 07 PHP
PHP中foreach()用法汇总
Jul 02 PHP
PHP实现的网站目录扫描索引工具
Sep 08 PHP
php 开发中加密的几种方法总结
Mar 22 PHP
php检测mysql表是否存在的方法小结
Jul 20 PHP
PHP获取日期对应星期、一周日期、星期开始与结束日期的方法
Jun 22 PHP
thinkphp5 migrate数据库迁移工具
Feb 20 #PHP
Laravel 5.4.36中session没有保存成功问题的解决
Feb 19 #PHP
自写的利用PDO对mysql数据库增删改查操作类
Feb 19 #PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
Feb 19 #PHP
浅析PHP数据导出知识点
Feb 17 #PHP
PHP 应用容器化以及部署方法
Feb 12 #PHP
PHP使用Redis长连接的方法详解
Feb 12 #PHP
You might like
PHP 和 MySQL 开发的 8 个技巧
2006/10/09 PHP
php为什么选mysql作为数据库? Mysql 创建用户方法
2007/07/02 PHP
javascript+php实现根据用户时区显示当地时间的方法
2015/03/11 PHP
php设置页面超时时间解决方法
2015/09/22 PHP
Yii2实现让关联字段支持搜索功能的方法
2016/08/10 PHP
javascript中巧用“闭包”实现程序的暂停执行功能
2007/04/04 Javascript
js 获取浏览器高度和宽度值(多浏览器)
2009/09/02 Javascript
让IE8支持DOM 2(不用框架!)
2009/12/31 Javascript
判断一个变量是数组Array类型的方法
2013/09/16 Javascript
javascript Array.prototype.slice的使用示例
2013/11/14 Javascript
javascript中var的重要性分析
2015/02/11 Javascript
JavaScript的ExtJS框架中表格的编写教程
2016/05/21 Javascript
第一次接触Bootstrap框架
2016/10/24 Javascript
Javascript基础回顾之(二) js作用域
2017/01/31 Javascript
JavaScript数组迭代方法
2017/03/03 Javascript
BootStrap实现文件上传并带有进度条效果
2017/09/11 Javascript
微信小程序公用参数与公用方法用法示例
2019/01/09 Javascript
[55:48]VGJ.S vs TNC Supermajor 败者组 BO3 第二场 6.6
2018/06/07 DOTA
Python中用max()方法求最大值的介绍
2015/05/15 Python
win10环境下python3.5安装步骤图文教程
2017/02/03 Python
Ubuntu下使用Python实现游戏制作中的切分图片功能
2018/03/30 Python
如何通过雪花算法用Python实现一个简单的发号器
2019/07/03 Python
简单了解django文件下载方式
2020/02/10 Python
浅谈keras中的Merge层(实现层的相加、相减、相乘实例)
2020/05/23 Python
Python配置pip国内镜像源的实现
2020/08/20 Python
利用html5的websocket实现websocket聊天室
2013/12/12 HTML / CSS
英国女性时尚品牌:Apricot
2018/12/04 全球购物
Levi’s西班牙官方网站:李维斯,著名的牛仔裤品牌
2020/08/20 全球购物
New delete 与malloc free 的联系与区别
2013/02/04 面试题
求职者简历中的自我评价
2013/10/20 职场文书
中医专业应届生求职信
2013/11/17 职场文书
总经理岗位职责范本
2014/02/02 职场文书
费城故事观后感
2015/06/10 职场文书
高中班主任心得体会
2016/01/07 职场文书
python urllib库的使用详解
2021/04/13 Python
oracle表分区的概念及操作
2021/04/24 Oracle