• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • Trait

    自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。

    Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

    Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。

    Example #1 Trait 示例

    <?php
    trait ezcReflectionReturnInfo {
        function getReturnType() { /*1*/ }
        function getReturnDescription() { /*2*/ }
    }
    class ezcReflectionMethod extends ReflectionMethod {
        use ezcReflectionReturnInfo;
        /* ... */
    }
    class ezcReflectionFunction extends ReflectionFunction {
        use ezcReflectionReturnInfo;
        /* ... */
    }
    ?>
    

    优先级

    从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。

    Example #2 优先顺序示例

    从基类继承的成员被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆盖。其行为 MyHelloWorld 类中定义的方法一致。优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。

    <?php
    class Base {
        public function sayHello() {
            echo 'Hello ';
        }
    }
    trait SayWorld {
        public function sayHello() {
            parent::sayHello();
            echo 'World!';
        }
    }
    class MyHelloWorld extends Base {
        use SayWorld;
    }
    $o = new MyHelloWorld();
    $o->sayHello();
    ?>
    

    以上例程会输出:

    Hello World!
    

    Example #3 另一个优先级顺序的例子

    <?php
    trait HelloWorld {
        public function sayHello() {
            echo 'Hello World!';
        }
    }
    class TheWorldIsNotEnough {
        use HelloWorld;
        public function sayHello() {
            echo 'Hello Universe!';
        }
    }
    $o = new TheWorldIsNotEnough();
    $o->sayHello();
    ?>
    

    以上例程会输出:

    Hello Universe!
    

    多个 trait

    通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。

    Example #4 多个 trait 的用法

    <?php
    trait Hello {
        public function sayHello() {
            echo 'Hello ';
        }
    }
    trait World {
        public function sayWorld() {
            echo 'World';
        }
    }
    class MyHelloWorld {
        use Hello, World;
        public function sayExclamationMark() {
            echo '!';
        }
    }
    $o = new MyHelloWorld();
    $o->sayHello();
    $o->sayWorld();
    $o->sayExclamationMark();
    ?>
    

    以上例程会输出:

    Hello World!
    

    冲突的解决

    如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。

    为了解决多个 trait 在同一个类中的命名冲突,需要使用insteadof操作符来明确指定使用冲突方法中的哪一个。

    以上方式仅允许排除掉其它方法,as操作符可以为某个方法引入别名。注意,as操作符不会对方法进行重命名,也不会影响其方法。

    Example #5 冲突的解决

    在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有冲突的方法,其定义了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk。

    Aliased_Talker 使用了as操作符来定义了talk来作为 B 的 bigTalk 的别名。

    <?php
    trait A {
        public function smallTalk() {
            echo 'a';
        }
        public function bigTalk() {
            echo 'A';
        }
    }
    trait B {
        public function smallTalk() {
            echo 'b';
        }
        public function bigTalk() {
            echo 'B';
        }
    }
    class Talker {
        use A, B {
            B::smallTalk insteadof A;
            A::bigTalk insteadof B;
        }
    }
    class Aliased_Talker {
        use A, B {
            B::smallTalk insteadof A;
            A::bigTalk insteadof B;
            B::bigTalk as talk;
        }
    }
    ?>
    
    Note:

    在 PHP 7.0 之前,在类里定义和 trait 同名的属性,哪怕是完全兼容的也会抛出E_STRICT(完全兼容的意思:具有相同的访问可见性、初始默认值)。

    修改方法的访问控制

    使用as语法还可以用来调整方法的访问控制。

    Example #6 修改方法的访问控制

    <?php
    trait HelloWorld {
        public function sayHello() {
            echo 'Hello World!';
        }
    }
    // 修改 sayHello 的访问控制
    class MyClass1 {
        use HelloWorld { sayHello as protected; }
    }
    // 给方法一个改变了访问控制的别名
    // 原版 sayHello 的访问控制则没有发生变化
    class MyClass2 {
        use HelloWorld { sayHello as private myPrivateHello; }
    }
    ?>
    

    从 trait 来组成 trait

    正如 class 能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,能够组合其它 trait 中的部分或全部成员。

    Example #7 从 trait 来组成 trait

    <?php
    trait Hello {
        public function sayHello() {
            echo 'Hello ';
        }
    }
    trait World {
        public function sayWorld() {
            echo 'World!';
        }
    }
    trait HelloWorld {
        use Hello, World;
    }
    class MyHelloWorld {
        use HelloWorld;
    }
    $o = new MyHelloWorld();
    $o->sayHello();
    $o->sayWorld();
    ?>
    

    以上例程会输出:

    Hello World!
    

    Trait 的抽象成员

    为了对使用的类施加强制要求,trait 支持抽象方法的使用。

    Example #8 表示通过抽象方法来进行强制要求

    <?php
    trait Hello {
        public function sayHelloWorld() {
            echo 'Hello'.$this->getWorld();
        }
        abstract public function getWorld();
    }
    class MyHelloWorld {
        private $world;
        use Hello;
        public function getWorld() {
            return $this->world;
        }
        public function setWorld($val) {
            $this->world = $val;
        }
    }
    ?>
    

    Trait 的静态成员

    Traits 可以被静态成员静态方法定义。

    Example #9 静态变量

    <?php
    trait Counter {
        public function inc() {
            static $c = 0;
            $c = $c + 1;
            echo "$c\n";
        }
    }
    class C1 {
        use Counter;
    }
    class C2 {
        use Counter;
    }
    $o = new C1(); $o->inc(); // echo 1
    $p = new C2(); $p->inc(); // echo 1
    ?>
    

    Example #10 静态方法

    <?php
    trait StaticExample {
        public static function doSomething() {
            return 'Doing something';
        }
    }
    class Example {
        use StaticExample;
    }
    Example::doSomething();
    ?>
    

    属性

    Trait 同样可以定义属性。

    Example #11 定义属性

    <?php
    trait PropertiesTrait {
        public $x = 1;
    }
    class PropertiesExample {
        use PropertiesTrait;
    }
    $example = new PropertiesExample;
    $example->x;
    ?>
    

    Trait 定义了一个属性后,类就不能定义同样名称的属性,否则会产生 fatal error。有种情况例外:属性是兼容的(同样的访问可见度、初始默认值)。在 PHP 7.0 之前,属性是兼容的,则会有 E_STRICT 的提醒。

    Example #12 解决冲突

    <?php
    trait PropertiesTrait {
        public $same = true;
        public $different = false;
    }
    class PropertiesExample {
        use PropertiesTrait;
        public $same = true; // PHP 7.0.0 后没问题,之前版本是 E_STRICT 提醒
        public $different = true; // 致命错误
    }
    ?>
    
    Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.
    Example using parent class:
    <?php
    class TestClass {
      public static $_bar;
    }
    class Foo1 extends TestClass { }
    class Foo2 extends TestClass { }
    Foo1::$_bar = 'Hello';
    Foo2::$_bar = 'World';
    echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
    ?>
    Example using trait:
    <?php
    trait TestTrait {
      public static $_bar;
    }
    class Foo1 {
      use TestTrait;
    }
    class Foo2 {
      use TestTrait;
    }
    Foo1::$_bar = 'Hello';
    Foo2::$_bar = 'World';
    echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World
    ?>
    
    The best way to understand what traits are and how to use them is to look at them for what they essentially are: language assisted copy and paste.
    If you can copy and paste the code from one class to another (and we've all done this, even though we try not to because its code duplication) then you have a candidate for a trait.
    Note that the "use" operator for traits (inside a class) and the "use" operator for namespaces (outside the class) resolve names differently. "use" for namespaces always sees its arguments as absolute (starting at the global namespace):
    <?php
    namespace Foo\Bar;
    use Foo\Test; // means \Foo\Test - the initial \ is optional
    ?>
    On the other hand, "use" for traits respects the current namespace:
    <?php
    namespace Foo\Bar;
    class SomeClass {
      use Foo\Test;  // means \Foo\Bar\Foo\Test
    }
    ?>
    Together with "use" for closures, there are now three different "use" operators. They all mean different things and behave differently.
    add to "chris dot rutledge at gmail dot com":
    __CLASS__ will return the name of the class in which the trait is being used (!) not the class in which trait method is being called:
    <?php
    trait TestTrait {
      public function testMethod() {
        echo "Class: " . __CLASS__ . PHP_EOL;
        echo "Trait: " . __TRAIT__ . PHP_EOL;
      }
    }
    class BaseClass {
      use TestTrait;
    }
    class TestClass extends BaseClass {
    }
    $t = new TestClass();
    $t->testMethod();
    //Class: BaseClass
    //Trait: TestTrait
    About the (Safak Ozpinar / safakozpinar at gmail)'s great note, you can still have the same behavior than inheritance using trait with this approach : 
    <?php
    trait TestTrait {
      public static $_bar;
    }
    class FooBar {
      use TestTrait;
    }
    class Foo1 extends FooBar {
    }
    class Foo2 extends FooBar {
    }
    Foo1::$_bar = 'Hello';
    Foo2::$_bar = 'World';
    echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
    It may be worth noting here that the magic constant __CLASS__ becomes even more magical - __CLASS__ will return the name of the class in which the trait is being used.
    for example
    <?php
    trait sayWhere {
      public function whereAmI() {
        echo __CLASS__;
      }
    }
    class Hello {
      use sayWHere;
    }
    class World {
      use sayWHere;
    }
    $a = new Hello;
    $a->whereAmI(); //Hello
    $b = new World;
    $b->whereAmI(); //World
    ?>
    The magic constant __TRAIT__ will giev you the name of the trait
    As already noted, static properties and methods in trait could be accessed directly using trait. Since trait is language assisted c/p, you should be aware that static property from trait will be initialized to the value trait property had in the time of class declaration. 
    Example:
    <?php
    trait Beer {
      protected static $type = 'Light';
      public static function printed(){
        echo static::$type.PHP_EOL;
      }
      public static function setType($type){
        static::$type = $type;
      }
    }
    class Ale {
      use Beer;
    }
    Beer::setType("Dark");
    class Lager {
      use Beer;
    }
    Beer::setType("Amber");
    header("Content-type: text/plain");
    Beer::printed(); // Prints: Amber
    Ale::printed();  // Prints: Light
    Lager::printed(); // Prints: Dark
    ?>
    
    Keep in mind; "final" keyword is useless in traits when directly using them, unlike extending classes / abstract classes.
    <?php
    trait Foo {
      final public function hello($s) { print "$s, hello!"; }
    }
    class Bar {
      use Foo;
      // Overwrite, no error
      final public function hello($s) { print "hello, $s!"; }
    }
    abstract class Foo {
      final public function hello($s) { print "$s, hello!"; }
    }
    class Bar extends Foo {
      // Fatal error: Cannot override final method Foo::hello() in ..
      final public function hello($s) { print "hello, $s!"; }
    }
    ?>
    But this way will finalize trait methods as expected;
    <?php
    trait FooTrait {
      final public function hello($s) { print "$s, hello!"; }
    }
    abstract class Foo {
      use FooTrait;
    }
    class Bar extends Foo {
      // Fatal error: Cannot override final method Foo::hello() in ..
      final public function hello($s) { print "hello, $s!"; }
    }
    ?>
    
    Another difference with traits vs inheritance is that methods defined in traits can access methods and properties of the class they're used in, including private ones.
    For example:
    <?php
    trait MyTrait
    {
     protected function accessVar()
     {
      return $this->var;
     }
    }
    class TraitUser
    {
     use MyTrait;
     private $var = 'var';
     public function getVar()
     {
      return $this->accessVar();
     }
    }
    $t = new TraitUser();
    echo $t->getVar(); // -> 'var'                                                                                                             
    ?>
    
    A number of the notes make incorrect assertions about trait behaviour because they do not extend the class.
    So, while "Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.
    Example using parent class:
    <?php
    class TestClass {
      public static $_bar;
    }
    class Foo1 extends TestClass { }
    class Foo2 extends TestClass { }
    Foo1::$_bar = 'Hello';
    Foo2::$_bar = 'World';
    echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
    ?>
    Example using trait:
    <?php
    trait TestTrait {
      public static $_bar;
    }
    class Foo1 {
      use TestTrait;
    }
    class Foo2 {
      use TestTrait;
    }
    Foo1::$_bar = 'Hello';
    Foo2::$_bar = 'World';
    echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World
    ?>"
    shows a correct example, simply adding
    <?php
    require_once('above');
    class Foo3 extends Foo2 {
    }
    Foo3::$_bar = 'news';
    echo Foo1::$_bar . ' ' . Foo2::$_bar . ' ' . Foo3::$_bar; 
    // Prints: Hello news news
    I think the best conceptual model of an incorporated trait is an advanced insertion of text, or as someone put it "language assisted copy and paste." If Foo1 and Foo2 were defined with $_bar, you would not expect them to share the instance. Similarly, you would expect Foo3 to share with Foo2, and it does.
    Viewing this way explains away a lot of the 'quirks' that are observed above with final, or subsequently declared private vars,
    Not very obvious but trait methods can be called as if they were defined as static methods in a regular class
    <?php
    trait Foo {
      function bar() {
        return 'baz';
      }
    }
    echo Foo::bar(),"\\n";
    ?>
    
    (It's already been said, but for the sake of searching on the word "relative"...)
    The "use" keyword to import a trait into a class will resolve relative to the current namespace and therefore should include a leading slash to represent a full path, whereas "use" at the namespace level is always absolute.
    The magic method __call works as expected using traits.
    <?php 
    trait Call_Helper{
      
      public function __call($name, $args){
        return count($args);
      }
    }
    class Foo{
      use Call_Helper;
    }
    $foo = new Foo();
    echo $foo->go(1,2,3,4); // echoes 4
    Adding to "atorich at gmail dot com":
    The behavior of the magic constant __CLASS__ when used in traits is as expected if you understand traits and late static binding (http://php.net/manual/en/language.oop5.late-static-bindings.php).
    <?php
    $format = 'Class: %-13s | get_class(): %-13s | get_called_class(): %-13s%s';
    trait TestTrait {
      public function testMethod() {
        global $format;
        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);
      }
      
      public static function testStatic() {
        global $format;
        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);
      }
    }
    trait DuplicateTrait {
      public function duplMethod() {
        global $format;
        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);
      }
      
      public static function duplStatic() {
        global $format;
        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);
      }
    }
    abstract class AbstractClass {
      
      use DuplicateTrait;
      
      public function absMethod() {
        global $format;
        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);
      }
      
      public static function absStatic() {
        global $format;
        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);
      }
    }
    class BaseClass extends AbstractClass {
      use TestTrait;
    }
    class TestClass extends BaseClass { }
    $t = new TestClass();
    $t->testMethod();
    TestClass::testStatic();
    $t->absMethod();
    TestClass::absStatic();
    $t->duplMethod();
    TestClass::duplStatic();
    ?>
    Will output:
    Class: BaseClass   | get_class(): BaseClass   | get_called_class(): TestClass  
    Class: BaseClass   | get_class(): BaseClass   | get_called_class(): TestClass  
    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass  
    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass  
    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass  
    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass
    Since Traits are considered literal "copying/pasting" of code, it's clear how the methods defined in DuplicateTrait give the same results as the methods defined in AbstractClass.
    Traits can not implement interfaces.
    (should be obvious, but tested is tested)
    <?php
    trait A
    {
      public function bar()
      {
        echo 'A::bar';
      }
    }
    trait B
    {
      public function bar()
      {
        echo 'B::bar';
      }
    }
    trait C
    {
      public function bar()
      {
        echo 'C::bar';
      }
    }
    class Foo
    {
      use A, B, C {
        C::bar insteadof A, B;
      }
    }
    $foo = new Foo();
    $foo->bar(); //C::bar
    Note that you can omit a method's inclusion by excluding it from one trait in favor of the other and doing the exact same thing in the reverse way.
    <?php
    trait A {
      public function sayHello()
      {
        echo 'Hello from A';
      }
      public function sayWorld()
      {
        echo 'World from A';
      }
    }
    trait B {
      public function sayHello()
      {
        echo 'Hello from B';
      }
      public function sayWorld()
      {
        echo 'World from B';
      }
    }
    class Talker {
      use A, B {
        A::sayHello insteadof B;
        A::sayWorld insteadof B;
        B::sayWorld insteadof A;
      }
    }
    $talker = new Talker();
    $talker->sayHello();
    $talker->sayWorld();
    ?>
    The method sayHello is imported, but the method sayWorld is simply excluded.
    The difference between Traits and multiple inheritance is in the inheritance part.  A trait is not inherited from, but rather included or mixed-in, thus becoming part of "this class".  Traits also provide a more controlled means of resolving conflicts that inevitably arise when using multiple inheritance in the few languages that support them (C++). Most modern languages are going the approach of a "traits" or "mixin" style system as opposed to multiple-inheritance, largely due to the ability to control ambiguities if a method is declared in multiple "mixed-in" classes.
    Also, one can not "inherit" static member functions in multiple-inheritance.
    Simple singleton trait.
    <?php
    trait singleton {  
      /**
       * private construct, generally defined by using class
       */
      //private function __construct() {}
      
      public static function getInstance() {
        static $_instance = NULL;
        $class = __CLASS__;
        return $_instance ?: $_instance = new $class;
      }
      
      public function __clone() {
        trigger_error('Cloning '.__CLASS__.' is not allowed.',E_USER_ERROR);
      }
      
      public function __wakeup() {
        trigger_error('Unserializing '.__CLASS__.' is not allowed.',E_USER_ERROR);
      }
    }
    /**
     * Example Usage
     */
    class foo {
      use singleton;
      
      private function __construct() {
        $this->name = 'foo';
      }
    }
    class bar {
      use singleton;
      
      private function __construct() {
        $this->name = 'bar';
      }
    }
    $foo = foo::getInstance();
    echo $foo->name;
    $bar = bar::getInstance();
    echo $bar->name;
    don't forget you can create complex (embedded) traits as well
    <?php
    trait Name {
     // ...
    }
    trait Address {
     // ...
    }
    trait Telephone {
     // ...
    }
    trait Contact {
     use Name, Address, Telephone;
    }
    class Customer {
     use Contact;
    }
    class Invoce {
     use Contact;
    }
    ?>
    
    ", но нельзя определить статические переменные в самом трейте." - зачем вводите людей в заблуждение?
    Документация помечена как ооп5 (я не знаю с какой версии есть такая возможность, до сего дня верил вам и думал что нельзя), но учитывая что здесь и по более поздним версиям информация, то Вы как то пометьте что статические переменные могут быть в трейте не только в методах трейта, но и сами по себе свойствами трейта.
    I want to make flexible module that contains different functionality and I want to combine this but using the same methods:
    <?php
    class Brain {
      public function ask($q) {
        // Trate the question 
        return 'Some response';
      }
    }
    trait BrainLogger {
      public function ask($q) {
        log('Received a question: ' . $q);
        return parent::ask();
      }
    }
    trait BrainMad {
      public function ask($q) {
        if(rand(0,1) == 1) {
            return parent::ask($q);    
        } else {
            return 'I don\'t wanna talk with you.';
        }
      }
    }
    ?>
    In some part of my application I want to use different combinations of that functionality so if I want one brain instance:
    <?php 
    $brain = new Brain;
    $brain->ask('what we gonna do tonight?');
    ?>
    If I want log all received questions:
    <?php
    class NiceBrain extends Brain {
      use BrainLog;
    }
    $brain = new Brain;
    $brain->ask('what we gonna do tonight?'); // Will log
    ?>
    If I want to extend more I need to make a cascade of classes:
    <?php
    class NiceBrain extends Brain {
      use BrainLog;
    }
    class CrazyBrain extends NiceBrain {
      use BrainMad;
    }
    $brain = new CrazyBrain;
    $brain->ask('What we gonna do tonight?');
    // Will output 'I don't wanna talk with you.' or normal response
    // but logging only in case BrainMad has a good mood
    ?>
    Because using of multiple traits will call only one selected in section "use Trait;".
    A (somewhat) practical example of trait usage.
    Without traits:
    <?php
    class Controller {
     /* Controller-specific methods defined here. */
    }
    class AdminController extends Controller {
     /* Controller-specific methods inherited from Controller. */
     /* Admin-specific methods defined here. */
    }
    class CrudController extends Controller {
     /* Controller-specific methods inherited from Controller. */
     /* CRUD-specific methods defined here. */
    }
    class AdminCrudController extends CrudController {
     /* Controller-specific methods inherited from Controller. */
     /* CRUD-specific methods inherited from CrudController. */
     /* (!!!) Admin-specific methods copied and pasted from AdminController. */
    }
    ?>
    With traits:
    <?php
    class Controller {
     /* Controller-specific methods defined here. */
    }
    class AdminController extends Controller {
     /* Controller-specific methods inherited from Controller. */
     /* Admin-specific methods defined here. */
    }
    trait CrudControllerTrait {
     /* CRUD-specific methods defined here. */
    }
    class AdminCrudController extends AdminController {
     use CrudControllerTrait;
     /* Controller-specific methods inherited from Controller. */
     /* Admin-specific methods inherited from AdminController. */
     /* CRUD-specific methods defined by CrudControllerTrait. */
    }
    ?>
    
    Traits are useful for strategies, when you want the same data to be handled (filtered, sorted, etc) differently.
    For example, you have a list of products that you want to filter out based on some criteria (brands, specs, whatever), or sorted by different means (price, label, whatever). You can create a sorting trait that contains different functions for different sorting types (numeric, string, date, etc). You can then use this trait not only in your product class (as given in the example), but also in other classes that need similar strategies (to apply a numeric sort to some data, etc).
    <?php
    trait SortStrategy {
      private $sort_field = null;
      private function string_asc($item1, $item2) {
        return strnatcmp($item1[$this->sort_field], $item2[$this->sort_field]);
      }
      private function string_desc($item1, $item2) {
        return strnatcmp($item2[$this->sort_field], $item1[$this->sort_field]);
      }
      private function num_asc($item1, $item2) {
        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
        return ($item1[$this->sort_field] < $item2[$this->sort_field] ? -1 : 1 );
      }
      private function num_desc($item1, $item2) {
        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
        return ($item1[$this->sort_field] > $item2[$this->sort_field] ? -1 : 1 );
      }
      private function date_asc($item1, $item2) {
        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
        if ($date1 == $date2) return 0;
        return ($date1 < $date2 ? -1 : 1 );
      }
      private function date_desc($item1, $item2) {
        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
        if ($date1 == $date2) return 0;
        return ($date1 > $date2 ? -1 : 1 );
      }
    }
    class Product {
      public $data = array();
      
      use SortStrategy;
      
      public function get() {
        // do something to get the data, for this ex. I just included an array
        $this->data = array(
          101222 => array('label' => 'Awesome product', 'price' => 10.50, 'date_added' => '2012-02-01'),
          101232 => array('label' => 'Not so awesome product', 'price' => 5.20, 'date_added' => '2012-03-20'),
          101241 => array('label' => 'Pretty neat product', 'price' => 9.65, 'date_added' => '2012-04-15'),
          101256 => array('label' => 'Freakishly cool product', 'price' => 12.55, 'date_added' => '2012-01-11'),
          101219 => array('label' => 'Meh product', 'price' => 3.69, 'date_added' => '2012-06-11'),
        );
      }
      
      public function sort_by($by = 'price', $type = 'asc') {
        if (!preg_match('/^(asc|desc)$/', $type)) $type = 'asc';
        switch ($by) {
          case 'name':
            $this->sort_field = 'label';
            uasort($this->data, array('Product', 'string_'.$type));
          break;
          case 'date':
            $this->sort_field = 'date_added';
            uasort($this->data, array('Product', 'date_'.$type));
          break;
          default:
            $this->sort_field = 'price';
            uasort($this->data, array('Product', 'num_'.$type));
        }
      }
    }
    $product = new Product();
    $product->get();
    $product->sort_by('name');
    echo '<pre>'.print_r($product->data, true).'</pre>';
    ?>
    
    Using AS on a __construct method (and maybe other magic methods) is really, really bad. The problem is that is doesn't throw any errors, at least in 5.4.0. It just sporadically resets the connection. And when I say "sporadically," I mean that arbitrary changes in the preceding code can cause the browser connection to reset or not reset *consistently*, so that subsequent page refreshes will continue to hang, crash, or display perfectly in the same fashion as the first load of the page after a change in the preceding code, but the slightest change in the code can change this state. (I believe it is related to precise memory usage.)
    I've spent a good part of the day chasing down this one, and weeping every time commenting or even moving a completely arbitrary section of code would cause the connection to reset. It was just by luck that I decided to comment the
    "__construct as primitiveObjectConstruct"
    line and then the crashes went away entirely.
    My parent trait constructor was very simple, so my fix this time was to copy the functionality into the child __construct. I'm not sure how I'll approach a more complicated parent trait constructor.
    Here is an example how to work with visiblity and conflicts.
    <?php
    trait A
    {
      private function smallTalk()
      {
        echo 'a';
      }
      private function bigTalk()
      {
        echo 'A';
      }
    }
    trait B
    {
      private function smallTalk()
      {
        echo 'b';
      }
      private function bigTalk()
      {
        echo 'B';
      }
    }
    trait C
    {
      public function smallTalk()
      {
        echo 'c';
      }
      public function bigTalk()
      {
        echo 'C';
      }
    }
    class Talker
    {
      use A, B, C {
        //visibility for methods that will be involved in conflict resolution
        B::smallTalk as public;
        A::bigTalk as public;
        //conflict resolution
        B::smallTalk insteadof A, C;
        A::bigTalk insteadof B, C;
        //aliases with visibility change
        B::bigTalk as public Btalk;
        A::smallTalk as public asmalltalk;
        
        //aliases only, methods already defined as public
        C::bigTalk as Ctalk;
        C::smallTalk as cmallstalk;
      }
    }
    (new Talker)->bigTalk();//A
    (new Talker)->Btalk();//B
    (new Talker)->Ctalk();//C
    (new Talker)->asmalltalk();//a
    (new Talker)->smallTalk();//b
    (new Talker)->cmallstalk();//c
    /*
    DocBlocks pertaining to the class or trait will NOT be carried over when applying the trait.
    Results trying a couple variations on classes with and without DocBlocks that use a trait with a DocBlock
    */
    <?php
    /**
     * @Entity
     */
    trait Foo
    {
      protected $foo;
    }
    /**
     * @HasLifecycleCallbacks
     */
    class Bar
    {
      use \Foo;
      
      protected $bar;
    }
    class MoreBar
    {
      use \Foo;
      
      protected $moreBar;
    }
    $w = new \ReflectionClass('\Bar');
    echo $w->getName() . ":\r\n";
    echo $w->getDocComment() . "\r\n\r\n";
    $x = new \ReflectionClass('\MoreBar');
    echo $x->getName() . ":\r\n";
    echo $x->getDocComment() . "\r\n\r\n";
    $barObj = new \Bar();
    $y = new \ReflectionClass($barObj);
    echo $y->getName() . ":\r\n";
    echo $y->getDocComment() . "\r\n\r\n";
    foreach($y->getTraits() as $traitObj) {
      echo $y->getName() . " ";
      echo $traitObj->getName() . ":\r\n";
      echo $traitObj->getDocComment() . "\r\n";
    }
    $moreBarObj = new \MoreBar();
    $z = new \ReflectionClass($moreBarObj);
    echo $z->getName() . " ";
    echo $z->getDocComment() . "\r\n\r\n";
    foreach($z->getTraits() as $traitObj) {
      echo $z->getName() . " ";
      echo $traitObj->getName() . ":\r\n";
      echo $traitObj->getDocComment() . "\r\n";
    }
    Visibility in traits is not shared between trait users.
    <?php
    trait T {
      protected $secret = 1;
    }
    class X {
      use T;
      public function peek ( Y $y ) {
        echo $y->secret;
      }
    }
    class Y {
      use T;
    }
    (new X)->peek(new Y); // Fatal: Cannot access protected property Y::$secret
    ?>
    This is as expected when thinking of traits as language assisted copy-paste.
    A note to 'Beispiel #9 Statische Variablen'. A trait can also have a static property:
    trait Counter {
      static $trvar=1;
      public static function stfunc() {
        echo "Hello world!"
      }
    }
    class C1 {
      use Counter;
    }
    print "\nTRVAR: " . C1::$trvar . "\n";  //prints 1
    $obj = new C1();
    C1::stfunc();  //prints Hello world!
    $obj->stfunc();  //prints Hello world!
    A static property (trvar) can only be accessed using the classname (C1).
    But a static function (stfunc) can be accessed using the classname or the instance ($obj).
    I think it's obvious to notice that using 'use' followed by the traits name must be seen as just copying/pasting lines of code into the place where they are used.
    If you want to resolve name conflicts and also change the visibility of a trait method, you'll need to declare both in the same line:
    trait testTrait{
      
      public function test(){
        echo 'trait test';
      }
      
    }
    class myClass{
      
      use testTrait {
        testTrait::test as private testTraitF;
      }
      
      public function test(){
        echo 'class test';
        echo '<br/>';
        $this->testTraitF();
      }
      
    }
    $obj = new myClass(); 
    $obj->test(); //prints both 'trait test' and 'class test'
    $obj->testTraitF(); //The method is not accessible (Fatal error: Call to private method myClass::testTraitF() )
    It's possible to define abstract function in a trait as static and implement non-static version of the function , and it will works. Like this
    trait B
    {
      public function smallTalk()
      {
        echo 'b';
      }
      public function bigTalk()
      {
        echo 'B';
      }
      abstract public function talk();
    }
    class traitTest
    {
      use B;
      public static function talk()
      {
        echo 111;
      }
    }
    Also, it's possible to define abstract non static , and implement static version. 
    Can't understand , is this a bug or it's a feature :)
    Like shown in other examples it is possible to define a constructors and destructors in traits:
    <? php
      trait T {
        public $prop = null;
        public function __construct($prop) {
          echo "Constructor called\n";
          $this->prop = $prop;
        }
        public function __destruct() {
          echo "Destructor called\n";
        }
      }
      class A {
        use T;
      }
      $a = new A("Hello World\n"); // Constructor called
      
      echo $a->prop; // Hello World
      // Destructor called
    ?>
    It is also possible to use trait methods as constructor/destructor using them with the alias '__construct'/'__destruct':
    <?php
      trait T {
        public $prop = null;
        public function constructor($prop) {
          echo "Constructor called\n";
          $this->prop = $prop;
        }
        public function destructor() {
          echo "Destructor called\n";
        }
      }
      class A {
        use T {
          T::constructor as __construct;
          T::destructor as __destruct;
        }
      }
      $a = new A("Hello World\n"); // Constructor called
      
      echo $a->prop; // Hello World
      // Destructor called  
    ?>
    
    <?php
    trait MyTrait {
      public function sayHello() {
        echo 'Hello World! from MyTrait';
      }
    }
    class MyClass1 {
      use MyTrait;
      public function sayHello() {
        echo 'Hello World from MyClasss!';
      }
    }
     $obj=new MyClass1();
    $obj->sayHello(); //Hello World from MyClasss!⏎
    ?>
    
    https://3v4l.org/mFuQE
    1. no deprecate if same-class-named method get from trait
    2. replace same-named method ba to aa in C
    trait ATrait {
      public function a(){
        return 'Aa';
      }
    }
    trait BTrait {
      public function a(){
        return 'Ba';
      }
    }
    class C {
      use ATrait{
        a as aa;
      }
      use BTrait{
        a as ba;
      }
      
      public function a() {
        return static::aa() . static::ba();
      }
    }
    $o = new C;
    echo $o->a(), "\n";
    class D {
      use ATrait{
        ATrait::a as aa;
      }
      use BTrait{
        BTrait::a as ba;
      }
      
      public function a() {
        return static::aa() . static::ba();
      }
    }
    $o = new D;
    echo $o->a(), "\n";
    class E {
      use ATrait{
        ATrait::a as aa;
        ATrait::a insteadof BTrait;
      }
      use BTrait{
        BTrait::a as ba;
      }
      
      public function e() {
        return static::aa() . static::ba();
      }
    }
    $o = new E;
    echo $o->e(), "\n";
    class F {
      use ATrait{
        a as aa;
      }
      use BTrait{
        a as ba;
      }
      
      public function f() {
        return static::aa() . static::ba();
      }
    }
    $o = new F;
    echo $o->f(), "\n";
    AaAa 
    AaBa 
    Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; E has a deprecated constructor in /in/mFuQE on line 48 
    AaBa 
    Fatal error: Trait method a has not been applied, because there are collisions with other trait methods on F in /in/mFuQE on line 65
    Trait can not have the same name as class because it will show: Fatal error: Cannot redeclare class

    上篇:对象接口

    下篇:匿名类