Chinaunix
标题:
PHP中的Trait语法深度解析
[打印本页]
作者:
傲笛飞血
时间:
2011-08-28 07:20
标题:
PHP中的Trait语法深度解析
Trait是PHP5.4中新加入的一种的语法结构,用来方便我们实现对象的扩展,是除extend、implements外的另外一种扩展对象的方式。
这里假设你已经熟悉trait语法的基本用法了,如果不熟悉,请访问:
https://wiki.php.net/rfc/traits
文章有些代码比较长,为了舒服我发了图片,点击图片可以查看清晰大图。主要的代码我都写了详细的注释。
我将在接下来的内容中介绍以下内容:
Trait语法在PHP内核中是如何实现的。
在(1)的基础上总结我们在使用Trait语法时,应该避免的方式。
一、对象结构的修改
为了实现Trait语法,PHP内核中对实现对象的结构体做了修改:
struct _zend_class_entry
{
... ...
zend_class_entry **traits;
zend_uint num_traits;
zend_trait_alias **trait_aliases;
zend_trait_precedence **trait_precedences;
... ...
}
复制代码
[p]其中增加了4个属性,其中比较关键的为两个:
@zend_class_entry **traits;
这个存放着这个类实现的各个trait,可以看出,每一个trait在内核中和对象一样,也是通过zend_class_entry结构实现的。
[p]
@num_traits
这个对象实现的trait个数。
二、语法解析及功能实现
理解了上面两个属性的含义后,这是我们便可以开始看PHP是如何来解析trait语法结构的。
<?php
class a
{
use b;
}
复制代码
这样我们的对象a便使用b这个trait。”use b;”对应的token为(去除了最后的分号token):
T_USE :use
T_WHITESPACE :
T_STRING :t_a
复制代码
我们顺着T_USE在zend_language_parser.y中寻找,其实use有两个用途,一个与trait有关,一个与namespace有关,所以在这个文件中也能相应的找个两个地方,但我们这一次只关心它与trait配对的那个。在582行左右,我们找到了相应的几条与trait有关的解析规则,它们都集中在 582行左右,这里只举例:
trait_use_statement:
T_USE trait_list trait_adaptations;
trait_list:
fully_qualified_class_name { zend_do_implements_trait(&$1 TSRMLS_CC); }
| 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上写的时候,对贴大篇幅代码这块一直很头疼,要么超长折行、要么不支持源码与非源码自由切,一切换>就成>了。到最后索性使用了图片,哪位兄台可以给推荐个好用的解决方案。
作者:walu
出处:
http://walu.sinaapp.com/?p=60
转载请注明出处~
作者:
renxiao2003
时间:
2011-08-28 10:04
PHP出5.4了吗?
作者:
renxiao2003
时间:
2011-08-28 10:08
看到出5.4 alpha3了。
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2