Movatterモバイル変換


[0]ホーム

URL:


update page now
    無名クラス »
    « オブジェクト インターフェイス

    トレイト

    PHP は、コードを再利用するための「トレイト」という仕組みを実装しています。

    トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。

    トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。

    例1 トレイトの例

    <?php

    traitTraitA{
    public function
    sayHello() {
    echo
    'Hello';
    }
    }

    trait
    TraitB{
    public function
    sayWorld() {
    echo
    'World';
    }
    }

    class
    MyHelloWorld
    {
    use
    TraitA,TraitB;// A class can use multiple traits

    public functionsayHelloWorld() {
    $this->sayHello();
    echo
    ' ';
    $this->sayWorld();
    echo
    "!\n";
    }
    }

    $myHelloWorld= newMyHelloWorld();
    $myHelloWorld->sayHelloWorld();

    ?>

    上の例の出力は以下となります。

    Hello World!

    優先順位

    基底クラスから継承したメンバーよりも、トレイトで追加したメンバーのほうが優先されます。 優先順位は現在のクラスのメンバーが最高で、その次がトレイトのメソッド、 そしてその次にくるのが継承したメソッドとなります。

    例2 優先順位の例

    基底クラスから継承したメソッドは、MyHelloWorld に SayWorld トレイトから追加されたメソッドで上書きされます。 この挙動は、MyHelloWorld クラスで定義したメソッドでも同じです。 優先順位は現在のクラスのメンバーが最高で、その次がトレイトのメソッド、 そしてその次にくるのが継承したメソッドとなります。

    <?php
    classBase{
    public function
    sayHello() {
    echo
    'Hello ';
    }
    }

    trait
    SayWorld{
    public function
    sayHello() {
    parent::sayHello();
    echo
    'World!';
    }
    }

    class
    MyHelloWorldextendsBase{
    use
    SayWorld;
    }

    $o= newMyHelloWorld();
    $o->sayHello();
    ?>

    上の例の出力は以下となります。

    Hello World!

    例3 もうひとつの優先順位の例

    <?php
    traitHelloWorld{
    public function
    sayHello() {
    echo
    'Hello World!';
    }
    }

    class
    TheWorldIsNotEnough{
    use
    HelloWorld;
    public function
    sayHello() {
    echo
    'Hello Universe!';
    }
    }

    $o= newTheWorldIsNotEnough();
    $o->sayHello();
    ?>

    上の例の出力は以下となります。

    Hello Universe!

    複数のトレイト

    複数のトレイトをひとつのクラスに追加するには、use 文でカンマ区切りで指定します。

    例4 複数のトレイトの使用例

    <?php
    traitHello{
    public function
    sayHello() {
    echo
    'Hello ';
    }
    }

    trait
    World{
    public function
    sayWorld() {
    echo
    'World';
    }
    }

    class
    MyHelloWorld{
    use
    Hello,World;
    public function
    sayExclamationMark() {
    echo
    '!';
    }
    }

    $o= newMyHelloWorld();
    $o->sayHello();
    $o->sayWorld();
    $o->sayExclamationMark();
    ?>

    上の例の出力は以下となります。

    Hello World!

    衝突の解決

    同じ名前のメンバーを含む複数のトレイトを追加するときには、 衝突を明示的に解決しておかないと fatal エラーが発生します。

    同一クラス内での複数のトレイト間の名前の衝突を解決するには、insteadof 演算子を使って そのうちのひとつを選ばなければなりません。

    この方法はひとつのメソッドだけしか使えませんが、as 演算子を使うと、 メソッドのいずれかにエイリアスを追加できます。as 演算子はメソッドをリネームするわけではないので、 その他のメソッドにも何も影響を及ぼさないことに注意しましょう。

    例5 衝突の解決

    この例では、Talker がトレイト A と B を使います。 A と B には同じ名前のメソッドがあるので、 smallTalk はトレイト B を使って bigTalk はトレイト A を使うように定義します。

    Aliased_Talker は、as 演算子を使って B の bigTalk の実装にtalk というエイリアスを指定して使います。

    <?php
    traitA{
    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::smallTalkinsteadofA;
    A::bigTalkinsteadofB;
    }
    }

    class
    Aliased_Talker{
    use
    A,B{
    B::smallTalkinsteadofA;
    A::bigTalkinsteadofB;
    B::bigTalkastalk;
    }
    }
    ?>

    メソッドのアクセス権の変更

    as 構文を使うと、 クラス内でのメソッドのアクセス権も変更することができます。

    例6 メソッドのアクセス権を変更する

    <?php
    traitHelloWorld{
    public function
    sayHello() {
    echo
    'Hello World!';
    }
    }

    // sayHello のアクセス権を変更します
    classMyClass1{
    use
    HelloWorld{sayHelloas protected; }
    }

    // アクセス権を変更したエイリアスメソッドを作ります
    // sayHello 自体のアクセス権は変わりません
    classMyClass2{
    use
    HelloWorld{sayHelloas privatemyPrivateHello; }
    }
    ?>

    トレイトを組み合わせたトレイト

    クラスからトレイトを使えるのと同様に、トレイトからもトレイトを使えます。 トレイトの定義の中でトレイトを使うと、 定義したトレイトのメンバーの全体あるいは一部を組み合わせることができます。

    例7 トレイトを組み合わせたトレイト

    <?php
    traitHello{
    public function
    sayHello() {
    echo
    'Hello ';
    }
    }

    trait
    World{
    public function
    sayWorld() {
    echo
    'World!';
    }
    }

    trait
    HelloWorld{
    use
    Hello,World;
    }

    class
    MyHelloWorld{
    use
    HelloWorld;
    }

    $o= newMyHelloWorld();
    $o->sayHello();
    $o->sayWorld();
    ?>

    上の例の出力は以下となります。

    Hello World!

    トレイトのメンバーの抽象化

    トレイトでは、抽象メソッドを使ってクラスの要件を指定できます。 アクセス権は public, protected, private をサポートしています。 PHP 8.0.0 より前のバージョンでは、 public と protected な抽象メソッドだけがサポートされていました。

    警告

    PHP 8.0.0 以降では、具象メソッドはシグネチャの互換性に関するルール を満たさなければなりません。 これより前のバージョンでは、シグネチャは異なっていても構いませんでした。

    例8 抽象メソッドによる、要件の明示

    <?php
    traitHello{
    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;
    }
    }
    ?>

    トレイトの static メンバー

    トレイトでは、static 変数、static メソッド、static プロパティを定義できます。

    注意:

    PHP 8.1.0 以降では、トレイトにある static メソッドや、 static プロパティに直接アクセスすることは、 推奨されなくなりました。 これらは、トレイトを使っているクラスからのみアクセスすべきものです。

    例9 static変数

    <?php

    traitCounter
    {
    public function
    inc()
    {
    static
    $c=0;
    $c=$c+1;
    echo
    "$c\n";
    }
    }

    class
    C1
    {
    use
    Counter;
    }

    class
    C2
    {
    use
    Counter;
    }

    $o= newC1();
    $o->inc();
    $p= newC2();
    $p->inc();

    ?>

    上の例の出力は以下となります。

    11

    例10 staticメソッド

    <?php

    traitStaticExample
    {
    public static function
    doSomething()
    {
    return
    'Doing something';
    }
    }

    class
    Example
    {
    use
    StaticExample;
    }

    echo
    Example::doSomething();

    ?>

    上の例の出力は以下となります。

    Doing something

    例11 staticプロパティ

    警告

    PHP 8.3.0 より前のバージョンでは、トレイト中で定義された static プロパティは、 そのトレイトを use している同じ継承階層にある全てのクラスで共有されていました。 PHP 8.3.0 以降では、子クラスが static プロパティを持つトレイトを use している場合、 親クラスで定義された static プロパティとは別物とみなされるようになりました。

    <?php

    traitT
    {
    public static
    $counter=1;
    }

    class
    A
    {
    use
    T;

    public static function
    incrementCounter()
    {
    static::
    $counter++;
    }
    }

    class
    BextendsA
    {
    use
    T;
    }

    A::incrementCounter();

    echo
    A::$counter,"\n";
    echo
    B::$counter,"\n";

    ?>

    上の例の PHP 8.3 での出力は、このようになります。:

    21

    プロパティ

    トレイトにはプロパティも定義できます。

    例12 プロパティの定義

    <?php

    traitPropertiesTrait
    {
    public
    $x=1;
    }

    class
    PropertiesExample
    {
    use
    PropertiesTrait;
    }

    $example= newPropertiesExample();
    $example->x;

    ?>

    トレイトでプロパティを定義したときは、 クラスではそれと互換性 (公開範囲と型とreadonlyの有無、そして初期値が同じ) がない同じ名前のプロパティを定義できません。 互換性がない名前を定義すると、致命的なエラーが発生します。

    例13 衝突の解決

    <?php
    traitPropertiesTrait{
    public
    $same=true;
    public
    $different1=false;
    public
    bool $different2;
    public
    bool $different3;
    }

    class
    PropertiesExample{
    use
    PropertiesTrait;
    public
    $same=true;
    public
    $different1=true;// Fatal error
    publicstring $different2;// Fatal error
    readonly protectedbool $different3;// Fatal error
    }
    ?>

    定数

    PHP 8.2.0 以降では、トレイトでも定数を定義できます。

    例14 定数を定義する

    <?php
    traitConstantsTrait{
    public const
    FLAG_MUTABLE=1;
    final public const
    FLAG_IMMUTABLE=5;
    }

    class
    ConstantsExample{
    use
    ConstantsTrait;
    }

    $example= newConstantsExample;
    echo
    $example::FLAG_MUTABLE;
    ?>

    上の例の出力は以下となります。

    1

    トレイトで定数を定義したときは、 クラスではそれと互換性 (公開範囲と初期値、そして final の有無が同じ) がない同じ名前の定数を定義できません。 互換性がない名前を定義すると、致命的なエラーが発生します。

    例15 衝突の解決

    <?php
    traitConstantsTrait{
    public const
    FLAG_MUTABLE=1;
    final public const
    FLAG_IMMUTABLE=5;
    }

    class
    ConstantsExample{
    use
    ConstantsTrait;
    public const
    FLAG_IMMUTABLE=5;// Fatal error
    }
    ?>

    Final メソッド

    PHP 8.3.0 以降では、as 演算子を使ってfinal をトレイトからインポートしたメソッドに適用できるようになりました。 こうすることで、子クラスでそのメソッドをオーバーライドすることを防止できます。 しかし、トレイトを使うクラスは、未だそのメソッドをオーバーライドできます。

    例16 トレイトからインポートするメソッドをfinal として定義する

    <?php

    traitCommonTrait
    {
    public function
    method()
    {
    echo
    'Hello';
    }
    }

    class
    FinalExampleA
    {
    use
    CommonTrait{
    CommonTrait::methodas final;// The 'final' prevents child classes from overriding the method
    }
    }

    class
    FinalExampleBextendsFinalExampleA
    {
    public function
    method() {}
    }

    ?>

    上の例の出力は、たとえば以下のようになります。

    Fatal error: Cannot override final method FinalExampleA::method() in ...

    Found A Problem?

    Learn How To Improve This PageSubmit a Pull RequestReport a Bug
    add a note

    User Contributed Notes25 notes

    662
    Safak Ozpinar / safakozpinar at gmail
    13 years ago
    Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.Example using parent class:<?phpclassTestClass{    public static$_bar;}classFoo1extendsTestClass{ }classFoo2extendsTestClass{ }Foo1::$_bar='Hello';Foo2::$_bar='World';echoFoo1::$_bar.' '.Foo2::$_bar;// Prints: World World?>Example using trait:<?phptraitTestTrait{    public static$_bar;}classFoo1{    useTestTrait;}classFoo2{    useTestTrait;}Foo1::$_bar='Hello';Foo2::$_bar='World';echoFoo1::$_bar.' '.Foo2::$_bar;// Prints: Hello World?>
    461
    greywire at gmail dot com
    13 years ago
    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.
    256
    Stefan W
    12 years ago
    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):<?phpnamespaceFoo\Bar;useFoo\Test;// means \Foo\Test - the initial \ is optional?>On the other hand, "use" for traits respects the current namespace:<?phpnamespaceFoo\Bar;classSomeClass{    useFoo\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.
    109
    t8 at AT pobox dot com
    13 years ago
    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:<?phptraitMyTrait{  protected functionaccessVar()  {    return$this->var;  }}classTraitUser{  useMyTrait;  private$var='var';  public functiongetVar()  {    return$this->accessVar();  }}$t= newTraitUser();echo$t->getVar();// -> 'var'?>
    103
    chris dot rutledge at gmail dot com
    13 years ago
    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<?phptraitsayWhere{    public functionwhereAmI() {        echo__CLASS__;    }}classHello{    usesayWHere;}classWorld{    usesayWHere;}$a= newHello;$a->whereAmI();//Hello$b= newWorld;$b->whereAmI();//World?>The magic constant __TRAIT__ will giev you the name of the trait
    qeremy (!) gmail
    10 years ago
    Keep in mind; "final" keyword is useless in traits when directly using them, unlike extending classes / abstract classes.<?phptraitFoo{    final public functionhello($s) { print"$s, hello!"; }}classBar{    useFoo;// Overwrite, no errorfinal public functionhello($s) { print"hello,$s!"; }}abstract classFoo{    final public functionhello($s) { print"$s, hello!"; }}classBarextendsFoo{// Fatal error: Cannot override final method Foo::hello() in ..final public functionhello($s) { print"hello,$s!"; }}?>But this way will finalize trait methods as expected;<?phptraitFooTrait{    final public functionhello($s) { print"$s, hello!"; }}abstract classFoo{    useFooTrait;}classBarextendsFoo{// Fatal error: Cannot override final method Foo::hello() in ..final public functionhello($s) { print"hello,$s!"; }}?>
    yeu_ym at yahoo dot com
    6 years ago
    Here is an example how to work with visiblity and conflicts.<?phptraitA{    private functionsmallTalk()    {        echo'a';    }    private functionbigTalk()    {        echo'A';    }}traitB{    private functionsmallTalk()    {        echo'b';    }    private functionbigTalk()    {        echo'B';    }}traitC{    public functionsmallTalk()    {        echo'c';    }    public functionbigTalk()    {        echo'C';    }}classTalker{    useA,B,C{//visibility for methods that will be involved in conflict resolutionB::smallTalkas public;A::bigTalkas public;//conflict resolutionB::smallTalkinsteadofA,C;A::bigTalkinsteadofB,C;//aliases with visibility changeB::bigTalkas publicBtalk;A::smallTalkas publicasmalltalk;//aliases only, methods already defined as publicC::bigTalkasCtalk;C::smallTalkascmallstalk;    }}(newTalker)->bigTalk();//A(newTalker)->Btalk();//B(newTalker)->Ctalk();//C(newTalker)->asmalltalk();//a(newTalker)->smallTalk();//b(newTalker)->cmallstalk();//c
    JustAddingSomeAdditionalUseCase
    2 years ago
    I have not seen this specific use case:"Wanting to preserve action of parent class method, the trait one calling ::parent & also the child class mehod action".// Child class.use SuperTrait {  initialize as initializeOr;}public function initialize(array &$element) {  ...  $this->initializeOr($element);}// Trait.public function initialize(array &$element) {  ...  parent::initialize($element);}// Parent class.public function initialize(array &$element) {  ...}
    canufrank
    9 years ago
    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:<?phpclassTestClass{    public static$_bar;}classFoo1extendsTestClass{ }classFoo2extendsTestClass{ }Foo1::$_bar='Hello';Foo2::$_bar='World';echoFoo1::$_bar.' '.Foo2::$_bar;// Prints: World World?>Example using trait:<?phptraitTestTrait{    public static$_bar;}classFoo1{    useTestTrait;}classFoo2{    useTestTrait;}Foo1::$_bar='Hello';Foo2::$_bar='World';echoFoo1::$_bar.' '.Foo2::$_bar;// Prints: Hello World?>"shows a correct example, simply adding<?phprequire_once('above');classFoo3extendsFoo2{}Foo3::$_bar='news';echoFoo1::$_bar.' '.Foo2::$_bar.' '.Foo3::$_bar;// Prints: Hello news newsI think the best conceptual model of an incorporatedtraitis an advanced insertion of text, or assomeone put it"language assisted copy and paste."IfFoo1andFoo2 were defined with $_bar,you would not expect them to share the instance.Similarly,you would expect Foo3 to share with Foo2, andit does.Viewing this way explains away a lot of  the'quirks'that are observed above withfinal, orsubsequently declaredprivatevars,
    rawsrc
    7 years ago
    About the (Safak Ozpinar / safakozpinar at gmail)'s great note, you can still have the same behavior than inheritance using trait with this approach :<?phptraitTestTrait{    public static$_bar;}classFooBar{    useTestTrait;}classFoo1extendsFooBar{}classFoo2extendsFooBar{}Foo1::$_bar='Hello';Foo2::$_bar='World';echoFoo1::$_bar.' '.Foo2::$_bar;// Prints: World World
    qschuler at neosyne dot com
    11 years ago
    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.<?phptraitA{    public functionsayHello()    {        echo'Hello from A';    }    public functionsayWorld()    {        echo'World from A';    }}traitB{    public functionsayHello()    {        echo'Hello from B';    }    public functionsayWorld()    {        echo'World from B';    }}classTalker{    useA,B{A::sayHelloinsteadofB;A::sayWorldinsteadofB;B::sayWorldinsteadofA;    }}$talker= newTalker();$talker->sayHello();$talker->sayWorld();?>The method sayHello is imported, but the method sayWorld is simply excluded.
    katrinaelaine6 at gmail dot com
    8 years ago
    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';traitTestTrait{    public functiontestMethod() {        global$format;printf($format,__CLASS__,get_class(),get_called_class(),PHP_EOL);    }        public static functiontestStatic() {        global$format;printf($format,__CLASS__,get_class(),get_called_class(),PHP_EOL);    }}traitDuplicateTrait{    public functionduplMethod() {        global$format;printf($format,__CLASS__,get_class(),get_called_class(),PHP_EOL);    }        public static functionduplStatic() {        global$format;printf($format,__CLASS__,get_class(),get_called_class(),PHP_EOL);    }}abstract classAbstractClass{        useDuplicateTrait;        public functionabsMethod() {        global$format;printf($format,__CLASS__,get_class(),get_called_class(),PHP_EOL);    }        public static functionabsStatic() {        global$format;printf($format,__CLASS__,get_class(),get_called_class(),PHP_EOL);    }}classBaseClassextendsAbstractClass{    useTestTrait;}classTestClassextendsBaseClass{ }$t= newTestClass();$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(): TestClassSince 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.
    Edward
    13 years ago
    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.
    marko at newvibrations dot net
    9 years ago
    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:<?phptraitBeer{    protected static$type='Light';    public static functionprinted(){        echo static::$type.PHP_EOL;    }    public static functionsetType($type){        static::$type=$type;    }}classAle{    useBeer;}Beer::setType("Dark");classLager{    useBeer;}Beer::setType("Amber");header("Content-type: text/plain");Beer::printed();// Prints: AmberAle::printed();// Prints: LightLager::printed();// Prints: Dark?>
    balbuf
    9 years ago
    (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.
    ryan at derokorian dot com
    13 years ago
    Simple singleton trait.<?phptraitsingleton{/**     * private construct, generally defined by using class     */    //private function __construct() {}public static functiongetInstance() {        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 */classfoo{    usesingleton;        private function__construct() {$this->name='foo';    }}classbar{    usesingleton;        private function__construct() {$this->name='bar';    }}$foo=foo::getInstance();echo$foo->name;$bar=bar::getInstance();echo$bar->name;
    bscheshirwork at gmail dot com
    8 years ago
    https://3v4l.org/mFuQE1. no deprecate if same-class-named method get from trait2. replace same-named method ba to aa in Ctrait 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
    Carlos Alberto Bertholdo Carucce
    9 years ago
    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() )
    D. Marti
    13 years ago
    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).<?phptraitSortStrategy{    private$sort_field=null;    private functionstring_asc($item1,$item2) {        returnstrnatcmp($item1[$this->sort_field],$item2[$this->sort_field]);    }    private functionstring_desc($item1,$item2) {        returnstrnatcmp($item2[$this->sort_field],$item1[$this->sort_field]);    }    private functionnum_asc($item1,$item2) {        if ($item1[$this->sort_field] ==$item2[$this->sort_field]) return0;        return ($item1[$this->sort_field] <$item2[$this->sort_field] ? -1:1);    }    private functionnum_desc($item1,$item2) {        if ($item1[$this->sort_field] ==$item2[$this->sort_field]) return0;        return ($item1[$this->sort_field] >$item2[$this->sort_field] ? -1:1);    }    private functiondate_asc($item1,$item2) {$date1=intval(str_replace('-','',$item1[$this->sort_field]));$date2=intval(str_replace('-','',$item2[$this->sort_field]));        if ($date1==$date2) return0;        return ($date1<$date2? -1:1);    }    private functiondate_desc($item1,$item2) {$date1=intval(str_replace('-','',$item1[$this->sort_field]));$date2=intval(str_replace('-','',$item2[$this->sort_field]));        if ($date1==$date2) return0;        return ($date1>$date2? -1:1);    }}classProduct{    public$data= array();        useSortStrategy;        public functionget() {// 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 functionsort_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= newProduct();$product->get();$product->sort_by('name');echo'<pre>'.print_r($product->data,true).'</pre>';?>
    Kristof
    11 years ago
    don't forget you can create complex (embedded) traits as well<?phptraitName{// ...}traitAddress{// ...}traitTelephone{// ...}traitContact{  useName,Address,Telephone;}classCustomer{  useContact;}classInvoce{  useContact;}?>
    Oddant
    12 years ago
    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.
    guidobelluomo at gmail dot com
    5 years ago
    If you override a method which was defined by a trait, calling the parent method will also call the trait's override. Therefore if you need to derive from a class which has a trait, you can extend the class without losing the trait's functionality:<?phptraitExampleTrait{    public functionoutput()    {parent::output();        echo"bar<br>";    }}classFoo{    public functionoutput()    {        echo"foo<br>";    }}classFooBarextendsFoo{    useExampleTrait;}classFooBarBazextendsFooBar{    useExampleTrait;    public functionoutput()    {parent::output();        echo"baz";    }}(newFooBarBaz())->output();?>Output:foobarbaz
    cody at codysnider dot com
    8 years ago
    /*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 */traitFoo{    protected$foo;}/** * @HasLifecycleCallbacks */classBar{    use\Foo;        protected$bar;}classMoreBar{    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";}
    84td84 at gmail dot com
    10 years ago
    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).
    artur at webprojektant dot pl
    13 years ago
    Trait can not have the same name as class because it will  show: Fatal error: Cannot redeclare class
    add a note
    To Top
    and to navigate •Enter to select •Esc to close •/ to open
    PressEnter without selection to search using Google

    [8]ページ先頭

    ©2009-2025 Movatter.jp