PHP内核探索:命名空间

命名空间是一种特殊的作用域
服务器君一共花费了163.761 ms进行了4次数据库查询,努力地为您提供了这个页面。
试试阅读模式?希望听取您的建议

在维基百科中,对命名空间的定义是: 命名空间(英语:Namespace)表示标识符(identifier)的上下文(context)。一个标识符可在多个命名空间中定义, 它在不同命名空间中的含义是互不相干的。在编程语言中,命名空间是一种特殊的作用域,它包含了处于该作用域内的标识符, 且本身也用一个标识符来表示,这样便将一系列在逻辑上相关的标识符用一个标识符组织了起来。 函数和类的作用域可被视作隐式命名空间,它们和可见性、可访问性和对象生命周期不可分割的联系在一起。

命名空间可以看作是一种封装事物的方法,同时也可以看作是组织代码结构的一种形式,在很多语言中都可以见到这种抽象概念和组织形式。 在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:

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

PHP从5.3.0版本开始支持命名空间特性。看一个定义和使用命名空间的示例:

<?php
namespace tipi;
class Exception {
    public static $var = 'think in php internal';
}
 
const E_ALL = "E_ALL IN Tipi";
 
function strlen(){
    echo 'strlen in tipi';
}
 
echo Exception::$var;
echo strlen(Exception::$var);
?>

如上所示,定义了命名空间tipi,在这个命名空间内定义了一个Exception类,一个E_ALL常量和一个函数strlen。 这些类、常量和函数PHP默认已经实现。假如没有这个命名空间,声明这些类、常量或函数时会报函数重复声明或类重复声明的错误, 并且常量的定义也不会成功。

从PHP语言来看,命名空间通过 namespace 关键字定义,在命名空间内,可以包括任何合法的PHP代码,但是它的影响范围仅限于类、常量和函数。 从语法上来讲,PHP支持在一个文件中定义多个命名空间,但是不推荐这种代码组织方式。 当需要将全局的非命名空间中的代码与命名空间中的代码组合在一起,全局代码必须用一个不带名称的 namespace 语句加上大括号括起来。

此时,思考一下,在PHP内核中,命名空间的定义是如何实现的呢? 当在多个命名空间中存在多个相同的函数或类时,如何区分? 命名空间内的函数如何调用?

命名空间的定义

命名空间在PHP中的实现方案比较简单,不管是函数,类或者常量, 在声明的过程中都将命名空间与定义的函数名以\合并起来,作为函数名或类名存储在其对应的容器中。 如上面示例中的Exception类,最后存储的类名是tipi\Exception。 对于整个PHP实现的架构来说,这种实现方案的代价和对整个代码结构的调整都是最小的。

下面我们以Exception类为例说明整个命名空间的实现。 命名空间实现的关键字是namespace,从此关键字开始我们可以找到在编译时处理此关键字的函数为 zend_do_begin_namespace。 在此函数中,关键是在对CG(current_namespace)的赋值操作,这个值在后面类声明或函数等声明时都会有用到。

在前面我们讲过,类声明的实现在编译时会调用Zend/zend_complie.c文件中的zend_do_begin_class_declaration函数, 在此函数中对于命名空间的处理代码如下:

if (CG(current_namespace)) {
    /* Prefix class name with name of current namespace */
    znode tmp;
 
    tmp.u.constant = *CG(current_namespace);
    zval_copy_ctor(&tmp.u.constant);
    zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC);
    class_name = &tmp;
    efree(lcname);
    lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));
}

这段代码的作用是如果当前存在命名空间,则给类名加上命名空间的前缀, 如前面提到示例中的tipi\Exception类,添加tipi\的操作就是在这里执行的。 在zend_do_build_namespace_name函数中最终会调用zend_do_build_full_name函数实现类名的合并。 在函数和常量的声明中存在同样的名称合并操作。这也是命名空间仅对类、常量和函数有效的原因。

使用命名空间

以函数调用为例,当需要调用函数时,会调用zend_do_begin_function_call函数。 在此函数中,当使用到命名空间时会检查函数名,其调用的函数为zend_resolve_non_class_name。 在zend_resolve_non_class_name函数中会根据类型作出判断并返回相关结果:

  1. 完全限定名称的函数: 程序首先会做此判断,其判断的依据是第一个字符是否为"\",这种情况下,在解析时会直接返回。 如类似于\strlen这样以\开头的全局调用或类似于前面定义的\tipi\Exception调用。
  2. 所有的非限定名称和限定名称(非完全限定名称):根据当前的导入规则 程序判断是否为别名,并从编译期间存储别名的HashTable中取出对应的命名空间名称,将其与现有的函数名合并。 关于别名的存储及生成在后面的内容中会说明,
  3. 在命名空间内部: 所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。最后判断是否在当前命名空间, 最终程序都会返回一个合并了命名空间的函数名。

别名/导入

允许通过别名引用或导入外部的完全限定名称,是命名空间的一个重要特征。 这有点类似于在类 unix 文件系统中可以创建对其它的文件或目录的符号连接。 PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。

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

在PHP中,别名是通过操作符 use 来实现的。从而我们可以从源码中找到编译时调用的函数是zend_do_use。 别名在编译为中间代码过程中存放在CG(current_import)中,这是一个HashTable。 zend_do_use整个函数的实现基本上是一个查找,判断是否错误,最后写入到HashTable的过程。 其中针对命名空间和类名都有导入的处理过程,而对于常量和函数来说却没有, 这就是PHP不支持导入函数或常量的根本原因所在。

延伸阅读

此文章所在专题列表如下:

  1. PHP内核探索:从SAPI接口开始
  2. PHP内核探索:一次请求的开始与结束
  3. PHP内核探索:一次请求生命周期
  4. PHP内核探索:单进程SAPI生命周期
  5. PHP内核探索:多进程/线程的SAPI生命周期
  6. PHP内核探索:Zend引擎
  7. PHP内核探索:再次探讨SAPI
  8. PHP内核探索:Apache模块介绍
  9. PHP内核探索:通过mod_php5支持PHP
  10. PHP内核探索:Apache运行与钩子函数
  11. PHP内核探索:嵌入式PHP
  12. PHP内核探索:PHP的FastCGI
  13. PHP内核探索:如何执行PHP脚本
  14. PHP内核探索:PHP脚本的执行细节
  15. PHP内核探索:操作码OpCode
  16. PHP内核探索:PHP里的opcode
  17. PHP内核探索:解释器的执行过程
  18. PHP内核探索:变量概述
  19. PHP内核探索:变量存储与类型
  20. PHP内核探索:PHP中的哈希表
  21. PHP内核探索:理解Zend里的哈希表
  22. PHP内核探索:PHP哈希算法设计
  23. PHP内核探索:翻译一篇HashTables文章
  24. PHP内核探索:哈希碰撞攻击是什么?
  25. PHP内核探索:常量的实现
  26. PHP内核探索:变量的存储
  27. PHP内核探索:变量的类型
  28. PHP内核探索:变量的值操作
  29. PHP内核探索:变量的创建
  30. PHP内核探索:预定义变量
  31. PHP内核探索:变量的检索
  32. PHP内核探索:变量的类型转换
  33. PHP内核探索:弱类型变量的实现
  34. PHP内核探索:静态变量的实现
  35. PHP内核探索:变量类型提示
  36. PHP内核探索:变量的生命周期
  37. PHP内核探索:变量赋值与销毁
  38. PHP内核探索:变量作用域
  39. PHP内核探索:诡异的变量名
  40. PHP内核探索:变量的value和type存储
  41. PHP内核探索:全局变量Global
  42. PHP内核探索:变量类型的转换
  43. PHP内核探索:内存管理开篇
  44. PHP内核探索:Zend内存管理器
  45. PHP内核探索:PHP的内存管理
  46. PHP内核探索:内存的申请与销毁
  47. PHP内核探索:引用计数与写时复制
  48. PHP内核探索:PHP5.3的垃圾回收机制
  49. PHP内核探索:内存管理中的cache
  50. PHP内核探索:写时复制COW机制
  51. PHP内核探索:数组与链表
  52. PHP内核探索:使用哈希表API
  53. PHP内核探索:数组操作
  54. PHP内核探索:数组源码分析
  55. PHP内核探索:函数的分类
  56. PHP内核探索:函数的内部结构
  57. PHP内核探索:函数结构转换
  58. PHP内核探索:定义函数的过程
  59. PHP内核探索:函数的参数
  60. PHP内核探索:zend_parse_parameters函数
  61. PHP内核探索:函数返回值
  62. PHP内核探索:形参return value
  63. PHP内核探索:函数调用与执行
  64. PHP内核探索:引用与函数执行
  65. PHP内核探索:匿名函数及闭包
  66. PHP内核探索:面向对象开篇
  67. PHP内核探索:类的结构和实现
  68. PHP内核探索:类的成员变量
  69. PHP内核探索:类的成员方法
  70. PHP内核探索:类的原型zend_class_entry
  71. PHP内核探索:类的定义
  72. PHP内核探索:访问控制
  73. PHP内核探索:继承,多态与抽象类
  74. PHP内核探索:魔术函数与延迟绑定
  75. PHP内核探索:保留类与特殊类
  76. PHP内核探索:对象
  77. PHP内核探索:创建对象实例
  78. PHP内核探索:对象属性读写
  79. PHP内核探索:命名空间
  80. PHP内核探索:定义接口
  81. PHP内核探索:继承与实现接口
  82. PHP内核探索:资源resource类型
  83. PHP内核探索:Zend虚拟机
  84. PHP内核探索:虚拟机的词法解析
  85. PHP内核探索:虚拟机的语法分析
  86. PHP内核探索:中间代码opcode的执行
  87. PHP内核探索:代码的加密与解密
  88. PHP内核探索:zend_execute的具体执行过程
  89. PHP内核探索:变量的引用与计数规则
  90. PHP内核探索:新垃圾回收机制说明

本文地址:http://www.nowamagic.net/librarys/veda/detail/1524,欢迎访问原出处。

不打个分吗?

转载随意,但请带上本文地址:

http://www.nowamagic.net/librarys/veda/detail/1524

如果你认为这篇文章值得更多人阅读,欢迎使用下面的分享功能。
小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏

大家都在看

阅读一百本计算机著作吧,少年

很多人觉得自己技术进步很慢,学习效率低,我觉得一个重要原因是看的书少了。多少是多呢?起码得看3、4、5、6米吧。给个具体的数量,那就100本书吧。很多人知识结构不好而且不系统,因为在特定领域有一个足够量的知识量+足够良好的知识结构,系统化以后就足以应对大量未曾遇到过的问题。

奉劝自学者:构建特定领域的知识结构体系的路径中再也没有比学习该专业的专业课程更好的了。如果我的知识结构体系足以囊括面试官的大部分甚至吞并他的知识结构体系的话,读到他言语中的一个词我们就已经知道他要表达什么,我们可以让他坐“上位”毕竟他是面试官,但是在知识结构体系以及心理上我们就居高临下。

所以,阅读一百本计算机著作吧,少年!

《代码之美》 聂雪军 (译者)

《代码之美》介绍了人类在一个奋斗领域中的创造性和灵活性:计算机系统的开发领域。在每章中的漂亮代码都是来自独特解决方案的发现,而这种发现是来源于作者超越既定边界的远见卓识,并且识别出被多数人忽视的需求以及找出令人叹为观止的问题解决方案。《代码之美》33章,有38位作者,每位作者贡献一章。每位作者都将自己心目中对于“美丽的代码”的认识浓缩在一章当中,张力十足。38位大牛,每个人对代码之美都有自己独特的认识,现在一览无余的放在一起,对于热爱程序的每个人都不啻一场盛宴。 虽然《代码之美》的涉猎范围很广,但也只能代表一小部分在这个软件开发这个最令人兴奋领域所发生的事情。

更多计算机宝库...