免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4143 | 回复: 2
打印 上一主题 下一主题

PHP中的Trait语法深度解析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-08-28 07:20 |只看该作者 |倒序浏览
Trait是PHP5.4中新加入的一种的语法结构,用来方便我们实现对象的扩展,是除extend、implements外的另外一种扩展对象的方式。
  • 这里假设你已经熟悉trait语法的基本用法了,如果不熟悉,请访问:https://wiki.php.net/rfc/traits
  • 文章有些代码比较长,为了舒服我发了图片,点击图片可以查看清晰大图。主要的代码我都写了详细的注释。

我将在接下来的内容中介绍以下内容:
  • Trait语法在PHP内核中是如何实现的。
  • 在(1)的基础上总结我们在使用Trait语法时,应该避免的方式。


一、对象结构的修改

为了实现Trait语法,PHP内核中对实现对象的结构体做了修改:
  1. struct _zend_class_entry
  2. {
  3.         ... ...
  4.         zend_class_entry **traits;
  5.         zend_uint num_traits;
  6.         zend_trait_alias **trait_aliases;
  7.         zend_trait_precedence **trait_precedences;
  8.         ... ...
  9. }
复制代码
[p]其中增加了4个属性,其中比较关键的为两个:
@zend_class_entry **traits;
这个存放着这个类实现的各个trait,可以看出,每一个trait在内核中和对象一样,也是通过zend_class_entry结构实现的。
[p]@num_traits
这个对象实现的trait个数。

二、语法解析及功能实现

理解了上面两个属性的含义后,这是我们便可以开始看PHP是如何来解析trait语法结构的。
  1. <?php
  2. class a
  3. {
  4.         use b;
  5. }
复制代码

这样我们的对象a便使用b这个trait。”use b;”对应的token为(去除了最后的分号token):
  1. T_USE :use
  2. T_WHITESPACE :
  3. T_STRING :t_a
复制代码


我们顺着T_USE在zend_language_parser.y中寻找,其实use有两个用途,一个与trait有关,一个与namespace有关,所以在这个文件中也能相应的找个两个地方,但我们这一次只关心它与trait配对的那个。在582行左右,我们找到了相应的几条与trait有关的解析规则,它们都集中在 582行左右,这里只举例:

  1. trait_use_statement:
  2.     T_USE trait_list trait_adaptations;
  3. trait_list:
  4.     fully_qualified_class_name { zend_do_implements_trait(&$1 TSRMLS_CC); }
  5.     | trait_list ',' fully_qualified_class_name { zend_do_implements_trait(&$3 TSRMLS_CC); };
复制代码


use b;对应的是zend_do_implements_trait函数,我们检查这个函数发现,它增加了一条ZEND_ADD_TRAIT指令,对应的是zend_do_implement_trait函数(注意:和前面的相比少了一个s),意思便是向当前正在定义的对象中的(zend_class_entry **traits;)属性里添加一个新的trait,并更新(zend_uint num_traits)等。

在我们对象定义结束后,会调用zend_do_end_class_declaration()函数,而这个函数在PHP5.4版本中做了修改,如果它检测到当前定义的对象使用了trait,则会增加一条ZEND_BIND_TRAITS指令,ZEND虚拟机在执行这条指令的时候便调用zend_do_bind_traits函数来将trait中定义的属性、方法copy给对象,是的,就是完完全全的Copy!
现在一切水落石出,我们已经找到了最关键的zend_do_bind_traits函数,请点击图片看注释:


首先,将trait中定义的函数copy给对象ce,使用的zend_do_traits_method_binding函数,请点击图片看注释:





然后再把trait中定义的属性copy给对象ce,使用的是zend_do_traits_property_binding函数,请点击图片看注释:


好了,先写这么多,大体的流程我们已经分析完了,现在我们便可以总结出几点东西来了:

  • 从本质上说,trait和include文件的概念差不多
  • trait可以更加方便的实现代码复用,因为我们用继承关系实现的无法在父类中访问子类的private属性与方法,而trait就和把代码直接写在对象里效果一样。
  • 使用trait时候应该坚决避免命名冲突,尤其是同时使用多个trait时。
  • 如果产生了命名冲突,如果两者的可见性、初始值、static与否完全相同,则trait中的会覆盖掉对象中的,并抛出E_STRICT错误,否则会抛出E_COMPILE_ERROR错误,终止编译。

昨天在wordpress上写的时候,对贴大篇幅代码这块一直很头疼,要么超长折行、要么不支持源码与非源码自由切,一切换>就成>了。到最后索性使用了图片,哪位兄台可以给推荐个好用的解决方案。

论坛徽章:
59
2015七夕节徽章
日期:2015-08-24 11:17:25ChinaUnix专家徽章
日期:2015-07-20 09:19:30每周论坛发贴之星
日期:2015-07-20 09:19:42ChinaUnix元老
日期:2015-07-20 11:04:38荣誉版主
日期:2015-07-20 11:05:19巳蛇
日期:2015-07-20 11:05:26CU十二周年纪念徽章
日期:2015-07-20 11:05:27IT运维版块每日发帖之星
日期:2015-07-20 11:05:34操作系统版块每日发帖之星
日期:2015-07-20 11:05:36程序设计版块每日发帖之星
日期:2015-07-20 11:05:40数据库技术版块每日发帖之星
日期:2015-07-20 11:05:432015年辞旧岁徽章
日期:2015-07-20 11:05:44
2 [报告]
发表于 2011-08-28 10:04 |只看该作者
PHP出5.4了吗?

论坛徽章:
59
2015七夕节徽章
日期:2015-08-24 11:17:25ChinaUnix专家徽章
日期:2015-07-20 09:19:30每周论坛发贴之星
日期:2015-07-20 09:19:42ChinaUnix元老
日期:2015-07-20 11:04:38荣誉版主
日期:2015-07-20 11:05:19巳蛇
日期:2015-07-20 11:05:26CU十二周年纪念徽章
日期:2015-07-20 11:05:27IT运维版块每日发帖之星
日期:2015-07-20 11:05:34操作系统版块每日发帖之星
日期:2015-07-20 11:05:36程序设计版块每日发帖之星
日期:2015-07-20 11:05:40数据库技术版块每日发帖之星
日期:2015-07-20 11:05:432015年辞旧岁徽章
日期:2015-07-20 11:05:44
3 [报告]
发表于 2011-08-28 10:08 |只看该作者
看到出5.4 alpha3了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP