<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; PHP trait</title>
	<atom:link href="https://www.phppan.com/tag/php-trait/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sun, 10 May 2026 02:26:45 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>关于Mixin和Trait</title>
		<link>https://www.phppan.com/2011/07/mixin-and-trait/</link>
		<comments>https://www.phppan.com/2011/07/mixin-and-trait/#comments</comments>
		<pubDate>Mon, 11 Jul 2011 01:38:26 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Mixin]]></category>
		<category><![CDATA[PHP trait]]></category>
		<category><![CDATA[Trait]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1417</guid>
		<description><![CDATA[其实在想文章题目时，有过纠结，虽然现在有些人将Mixin翻译为“混入”，不过感觉有点怪怪的，所以还是直接用了英 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">其实在想文章题目时，有过纠结，虽然现在有些人将Mixin翻译为“混入”，不过感觉有点怪怪的，所以还是直接用了英文，至少不会出错。 言归正转。</p>
<p style="text-indent: 2em;">现在排名靠前的面向对象的编程语言中，Java、C#等都是以<strong>单继承+接口</strong>来实现面向对象，但是这在一定程序了稀释了继承的力量， 因为在业内推荐以组合的方式使用类。这在一些常见的设计模式中有明显的体现，想想在GOF的23个设计模式中有多少个是使用了继承的呢？ 大多数是以接口+组合的方式实现。其实作为一个类来说，它也比较难做，即要能代码复用，又得被实例化，偏向谁呢？ 这个时候Mixin可能就有一些用武之地了。</p>
<p style="text-indent: 2em;">Mixin最早起源于一个Lisp，Mixin鼓励代码重用，Mixin可以实现运行时的方法绑定，虽然类的属性和实例参数仍然是在编译时定义。 在面向对象编程语言，Mixin是一个提供了一些被用于继承或在子类中重用的功能的类，它类似于一种多继承， 但是实际上它是一种中小粒度的代码复用单元，而不直接用于实例化。 虽然这不是一种专业的方式进行功能复用，这在实现多继承的同时，在一定程序上避免了多继承的明显问题。</p>
<p style="text-indent: 2em;">PHP和Java类似，也是单继承+接口。 我们知道，一个类可以实现任意数量的接口，这对一个类需要实现多个抽象的时候非常有用。 然而，对于要实现了多个接口的类，每个类都需要实现这些接口，而大多数情况下，这些接口都是可以共用的。 PHP并没有提供内置机制来定义和使用这些可重用代码，虽然我们可以对一地些接口使用一个抽象类来共用代码，但是如果这些类必须继承另一个抽象类呢？ 就算是可以通过抽象类的多次继承实现代码的共用，但是整个继承体系将会变得非常复杂，如果不能实现重用，那么可能我们只得CTRL + C 和 CTRL + V了。 大多数的情况下我们其实只是需要重用一些代码而已。</p>
<p style="text-indent: 2em;">虽然PHP在之前没有提供完善的解决方案，但在新发布PHP5.4中，出现了一个关键字<strong>trait</strong>。 通过这个关键字我们可以定义抽象为一个Trait，如下示例：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">trait HelloWorld <span style="color: #ffffff;">{</span>
    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> sayHello<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/echo"><span style="color: #e2392d;">echo</span></a> <span style="color: #56db3a;">'Hello World!'</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>
<span style="color: #ffffff;">}</span>

<span style="color: #cc7833;">class</span> TheWorldIsNotEnough <span style="color: #ffffff;">{</span>
    use HelloWorld<span style="color: #e0882f;">;</span>
    <span style="color: #cc7833;">public</span> <span style="color: #cc7833;">function</span> sayHello<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/echo"><span style="color: #e2392d;">echo</span></a> <span style="color: #56db3a;">'Hello Universe!'</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>
<span style="color: #ffffff;">}</span>

<span style="color: #6d9cbe;">$o</span> <span style="color: #e0882f;">=</span> <span style="color: #cc7833;">new</span> TheWorldIsNotEnough<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #6d9cbe;">$o</span><span style="color: #e0882f;">-&gt;</span><span style="color: #ffffff;">sayHello</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> <span style="color: #bc9458; font-style: italic;">// echos Hello Universe!</span></pre>
<p style="text-indent: 2em;">更多关于Traits的信息, 请参考: <a style="color: #1299da; text-decoration: underline;" href="https://wiki.php.net/rfc/traits">Traits for PHP RFC</a></p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">trait的实现</h2>
<p style="text-indent: 2em;">因为trait是一个语言结构，所以我们从zend_language_scanner.l文件中找到trait对应的关键字标识为：T_TRAIT 在zend_lang_parse.y文件中根据标识找到：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">class_entry_type<span style="color: #e0882f;">:</span>
        T_CLASS         <span style="color: #ffffff;">{</span> $$.<span style="color: #ffffff;">u</span>.<span style="color: #ffffff;">op</span>.<span style="color: #ffffff;">opline_num</span> <span style="color: #e0882f;">=</span> CG<span style="color: #ffffff;">(</span>zend_lineno<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> $$.<span style="color: #ffffff;">EA</span> <span style="color: #e0882f;">=</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span> <span style="color: #ffffff;">}</span>
    <span style="color: #e0882f;">|</span>   T_ABSTRACT T_CLASS <span style="color: #ffffff;">{</span> $$.<span style="color: #ffffff;">u</span>.<span style="color: #ffffff;">op</span>.<span style="color: #ffffff;">opline_num</span> <span style="color: #e0882f;">=</span> CG<span style="color: #ffffff;">(</span>zend_lineno<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> $$.<span style="color: #ffffff;">EA</span> <span style="color: #e0882f;">=</span> ZEND_ACC_EXPLICIT_ABSTRACT_CLASS<span style="color: #e0882f;">;</span> <span style="color: #ffffff;">}</span>
    <span style="color: #e0882f;">|</span>   T_TRAIT <span style="color: #ffffff;">{</span> $$.<span style="color: #ffffff;">u</span>.<span style="color: #ffffff;">op</span>.<span style="color: #ffffff;">opline_num</span> <span style="color: #e0882f;">=</span> CG<span style="color: #ffffff;">(</span>zend_lineno<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> $$.<span style="color: #ffffff;">EA</span> <span style="color: #e0882f;">=</span> ZEND_ACC_TRAIT<span style="color: #e0882f;">;</span> <span style="color: #ffffff;">}</span>
    <span style="color: #e0882f;">|</span>   T_FINAL T_CLASS <span style="color: #ffffff;">{</span> $$.<span style="color: #ffffff;">u</span>.<span style="color: #ffffff;">op</span>.<span style="color: #ffffff;">opline_num</span> <span style="color: #e0882f;">=</span> CG<span style="color: #ffffff;">(</span>zend_lineno<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> $$.<span style="color: #ffffff;">EA</span> <span style="color: #e0882f;">=</span> ZEND_ACC_FINAL_CLASS<span style="color: #e0882f;">;</span> <span style="color: #ffffff;">}</span>
<span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">T_TRAIT作为一个关键字和class并级，它作为一个另一种类型的类存在。它将与接口、类处于同一字段标识。 其定义在zend_complie.h文件，如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">#define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS    0x10</span>
<span style="color: #bd48b3; font-style: italic;">#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS    0x20</span>
<span style="color: #bd48b3; font-style: italic;">#define ZEND_ACC_FINAL_CLASS                0x40</span>
<span style="color: #bd48b3; font-style: italic;">#define ZEND_ACC_INTERFACE                  0x80</span>
<span style="color: #bd48b3; font-style: italic;">#define ZEND_ACC_TRAIT                      0x120</span></pre>
<p style="text-indent: 2em;">以上的标识只是对应类的ce_flags结构，除此之外，在为的结构方面也有调整，如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">struct</span> _zend_class_entry <span style="color: #ffffff;">{</span>
    ...<span style="color: #bc9458; font-style: italic;">//   省略，木有修改</span>
    zend_class_entry <span style="color: #e0882f;">**</span>interfaces<span style="color: #e0882f;">;</span>  <span style="color: #bc9458; font-style: italic;">//  接口列表</span>
    zend_uint num_interfaces<span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  接口数</span>

    zend_class_entry <span style="color: #e0882f;">**</span>traits<span style="color: #e0882f;">;</span>  <span style="color: #bc9458; font-style: italic;">//  traits列表</span>
    zend_uint num_traits<span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//      traits数</span>
    zend_trait_alias <span style="color: #e0882f;">**</span>trait_aliases<span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  别名</span>
    zend_trait_precedence <span style="color: #e0882f;">**</span>trait_precedences<span style="color: #e0882f;">;</span>

<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">从上面的结构可以看出，PHP为traits增加了对应的字段存储。从PHP的介绍中我们可知，trait可以动态绑定，则其执行应该是中间代码执行期间。 因此，如果使用了trait关键字，将会有对应的中间代码：<strong>ZEND_BIND_TRAITS</strong> 生成。 ZEND_BIND_TRAITS关键字最终调用zend_complie.c文件中的zend_do_bind_traits函数完成traits类的绑定，如下代码：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">ZEND_API <span style="color: #1299da;">void</span> zend_do_bind_traits<span style="color: #ffffff;">(</span>zend_class_entry <span style="color: #e0882f;">*</span>ce TSRMLS_DC<span style="color: #ffffff;">)</span> <span style="color: #bd48b3; font-style: italic;">/* {{{ */</span>
<span style="color: #ffffff;">{</span>

    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>ce<span style="color: #e0882f;">-&gt;</span>num_traits <span style="color: #e0882f;">&lt;=</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #ff8400;">return</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>

    <span style="color: #bd48b3; font-style: italic;">/* complete initialization of trait strutures in ce */</span>
    zend_traits_init_trait_structures<span style="color: #ffffff;">(</span>ce TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

    <span style="color: #bd48b3; font-style: italic;">/* first care about all methods to be flattened into the class */</span>
    zend_do_traits_method_binding<span style="color: #ffffff;">(</span>ce TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

    <span style="color: #bd48b3; font-style: italic;">/* then flatten the properties into it, to, mostly to notfiy developer about problems */</span>
    zend_do_traits_property_binding<span style="color: #ffffff;">(</span>ce TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

    <span style="color: #bd48b3; font-style: italic;">/* verify that all abstract methods from traits have been implemented */</span>
    zend_verify_abstract_class<span style="color: #ffffff;">(</span>ce TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

    <span style="color: #bd48b3; font-style: italic;">/* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */</span>
    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>ce<span style="color: #e0882f;">-&gt;</span>ce_flags <span style="color: #e0882f;">&amp;</span> ZEND_ACC_IMPLICIT_ABSTRACT_CLASS<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        ce<span style="color: #e0882f;">-&gt;</span>ce_flags <span style="color: #e0882f;">-=</span> ZEND_ACC_IMPLICIT_ABSTRACT_CLASS<span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>
<span style="color: #ffffff;">}</span>
<span style="color: #bd48b3; font-style: italic;">/* }}} */</span></pre>
<p style="text-indent: 2em;">以上的绑定过程并不是和接口或类一样的的简单的合并操作，在合并操作之前需要处理引用和别名等情况， 此时类结构中的trait_aliases和trait_precedences就发挥作用了。 trait定义的结构最终也是一个类。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/07/mixin-and-trait/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
