详细解读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 相关文章推荐
用PHP4访问Oracle815
Oct 09 PHP
PHP新手上路(七)
Oct 09 PHP
PHP include_path设置技巧分享
Jul 03 PHP
web目录下不应该存在多余的程序(安全考虑)
May 09 PHP
PHP图片验证码制作实现分享(全)
May 10 PHP
解析dedeCMS验证码的实现代码
Jun 07 PHP
linux下安装php的memcached客户端
Aug 03 PHP
php动态生成版权所有信息的方法
Mar 24 PHP
php中删除、清空session的方式总结
Oct 09 PHP
php的命名空间与自动加载实现方法
Aug 25 PHP
laravel框架使用阿里云短信发送消息操作示例
Feb 15 PHP
基于PHP实现用户登录注册功能的详细教程
Aug 04 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 图片上添加透明度渐变的效果
2009/06/29 PHP
PHP高自定义性安全验证码代码
2011/11/27 PHP
浅析Yii2 gridview实现批量删除教程
2016/04/22 PHP
PHP+Oracle本地开发环境搭建方法详解
2019/04/01 PHP
javascript实现动态增加删除表格行(兼容IE/FF)
2007/04/02 Javascript
JS 分号引起的一段调试问题
2009/06/18 Javascript
基于jquery的direction图片渐变动画效果
2010/05/24 Javascript
window.location.hash 使用说明
2010/11/08 Javascript
jQuery Tips 为AJAX回调函数传递额外参数的方法
2010/12/28 Javascript
Jquery弹出窗口插件 LeanModal的使用方法
2012/03/10 Javascript
客户端js性能优化小技巧整理
2013/11/05 Javascript
一款由jquery实现的整屏切换特效
2014/09/15 Javascript
CascadeView级联组件实现思路详解(分离思想和单链表)
2016/04/12 Javascript
jQuery获取访问者IP地址的方法(基于新浪API与QQ查询接口)
2016/05/25 Javascript
jQuery 中ajax异步调用的四种方式
2016/06/28 Javascript
JavaScript数据存储 Cookie篇
2016/07/02 Javascript
基于VuePress 轻量级静态网站生成器的实现方法
2018/04/17 Javascript
微信小程序判断用户是否需要再次授权获取个人信息
2019/07/18 Javascript
[01:08:10]2014 DOTA2国际邀请赛中国区预选赛 SPD-GAMING VS LGD-CDEC
2014/05/22 DOTA
[01:34]完美“圣”典宣传片震撼发布,12.17与你不见不散
2016/12/16 DOTA
python使用win32com在百度空间插入html元素示例
2014/02/20 Python
Python实现的矩阵类实例
2017/08/22 Python
django 外键model的互相读取方法
2018/12/15 Python
Python input函数使用实例解析
2019/11/22 Python
基于Django实现日志记录报错信息
2019/12/17 Python
python中的split()函数和os.path.split()函数使用详解
2019/12/21 Python
pytorch读取图像数据转成opencv格式实例
2020/06/02 Python
python 模拟登录B站的示例代码
2020/12/15 Python
德国高尔夫商店:Par71.de
2020/11/29 全球购物
亚马逊新加坡官方网站:Amazon.sg
2020/03/25 全球购物
几个数据库方面的面试题
2016/07/01 面试题
DTD的含义以及作用
2014/01/26 面试题
校园安全广播稿
2014/02/08 职场文书
关于感恩的演讲稿800字
2014/08/26 职场文书
python opencv将多个图放在一个窗口的实例详解
2022/02/28 Python
Kubernetes控制节点的部署
2022/04/01 Servers