PHP は、コードを再利用するための「トレイト」という仕組みを実装しています。
トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。
トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。
例1 トレイトの例
<?php
traitTraitA{
public functionsayHello() {
echo'Hello';
}
}
traitTraitB{
public functionsayWorld() {
echo'World';
}
}
classMyHelloWorld
{
useTraitA,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 functionsayHello() {
echo'Hello ';
}
}
traitSayWorld{
public functionsayHello() {
parent::sayHello();
echo'World!';
}
}
classMyHelloWorldextendsBase{
useSayWorld;
}
$o= newMyHelloWorld();
$o->sayHello();
?>上の例の出力は以下となります。
Hello World!
例3 もうひとつの優先順位の例
<?php
traitHelloWorld{
public functionsayHello() {
echo'Hello World!';
}
}
classTheWorldIsNotEnough{
useHelloWorld;
public functionsayHello() {
echo'Hello Universe!';
}
}
$o= newTheWorldIsNotEnough();
$o->sayHello();
?>上の例の出力は以下となります。
Hello Universe!
複数のトレイトをひとつのクラスに追加するには、use 文でカンマ区切りで指定します。
例4 複数のトレイトの使用例
<?php
traitHello{
public functionsayHello() {
echo'Hello ';
}
}
traitWorld{
public functionsayWorld() {
echo'World';
}
}
classMyHelloWorld{
useHello,World;
public functionsayExclamationMark() {
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 functionsmallTalk() {
echo'a';
}
public functionbigTalk() {
echo'A';
}
}
traitB{
public functionsmallTalk() {
echo'b';
}
public functionbigTalk() {
echo'B';
}
}
classTalker{
useA,B{
B::smallTalkinsteadofA;
A::bigTalkinsteadofB;
}
}
classAliased_Talker{
useA,B{
B::smallTalkinsteadofA;
A::bigTalkinsteadofB;
B::bigTalkastalk;
}
}
?>as 構文を使うと、 クラス内でのメソッドのアクセス権も変更することができます。
例6 メソッドのアクセス権を変更する
<?php
traitHelloWorld{
public functionsayHello() {
echo'Hello World!';
}
}
// sayHello のアクセス権を変更します
classMyClass1{
useHelloWorld{sayHelloas protected; }
}
// アクセス権を変更したエイリアスメソッドを作ります
// sayHello 自体のアクセス権は変わりません
classMyClass2{
useHelloWorld{sayHelloas privatemyPrivateHello; }
}
?>クラスからトレイトを使えるのと同様に、トレイトからもトレイトを使えます。 トレイトの定義の中でトレイトを使うと、 定義したトレイトのメンバーの全体あるいは一部を組み合わせることができます。
例7 トレイトを組み合わせたトレイト
<?php
traitHello{
public functionsayHello() {
echo'Hello ';
}
}
traitWorld{
public functionsayWorld() {
echo'World!';
}
}
traitHelloWorld{
useHello,World;
}
classMyHelloWorld{
useHelloWorld;
}
$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 functionsayHelloWorld() {
echo'Hello'.$this->getWorld();
}
abstract public functiongetWorld();
}
classMyHelloWorld{
private$world;
useHello;
public functiongetWorld() {
return$this->world;
}
public functionsetWorld($val) {
$this->world=$val;
}
}
?>トレイトでは、static 変数、static メソッド、static プロパティを定義できます。
注意:
PHP 8.1.0 以降では、トレイトにある static メソッドや、 static プロパティに直接アクセスすることは、 推奨されなくなりました。 これらは、トレイトを使っているクラスからのみアクセスすべきものです。
例9 static変数
<?php
traitCounter
{
public functioninc()
{
static$c=0;
$c=$c+1;
echo"$c\n";
}
}
classC1
{
useCounter;
}
classC2
{
useCounter;
}
$o= newC1();
$o->inc();
$p= newC2();
$p->inc();
?>上の例の出力は以下となります。
11
例10 staticメソッド
<?php
traitStaticExample
{
public static functiondoSomething()
{
return'Doing something';
}
}
classExample
{
useStaticExample;
}
echoExample::doSomething();
?>上の例の出力は以下となります。
Doing something
例11 staticプロパティ
PHP 8.3.0 より前のバージョンでは、トレイト中で定義された static プロパティは、 そのトレイトを use している同じ継承階層にある全てのクラスで共有されていました。 PHP 8.3.0 以降では、子クラスが static プロパティを持つトレイトを use している場合、 親クラスで定義された static プロパティとは別物とみなされるようになりました。
<?php
traitT
{
public static$counter=1;
}
classA
{
useT;
public static functionincrementCounter()
{
static::$counter++;
}
}
classBextendsA
{
useT;
}
A::incrementCounter();
echoA::$counter,"\n";
echoB::$counter,"\n";
?>上の例の PHP 8.3 での出力は、このようになります。:
21
トレイトにはプロパティも定義できます。
例12 プロパティの定義
<?php
traitPropertiesTrait
{
public$x=1;
}
classPropertiesExample
{
usePropertiesTrait;
}
$example= newPropertiesExample();
$example->x;
?>トレイトでプロパティを定義したときは、 クラスではそれと互換性 (公開範囲と型とreadonlyの有無、そして初期値が同じ) がない同じ名前のプロパティを定義できません。 互換性がない名前を定義すると、致命的なエラーが発生します。
例13 衝突の解決
<?php
traitPropertiesTrait{
public$same=true;
public$different1=false;
publicbool $different2;
publicbool $different3;
}
classPropertiesExample{
usePropertiesTrait;
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 constFLAG_MUTABLE=1;
final public constFLAG_IMMUTABLE=5;
}
classConstantsExample{
useConstantsTrait;
}
$example= newConstantsExample;
echo$example::FLAG_MUTABLE;
?>上の例の出力は以下となります。
1
トレイトで定数を定義したときは、 クラスではそれと互換性 (公開範囲と初期値、そして final の有無が同じ) がない同じ名前の定数を定義できません。 互換性がない名前を定義すると、致命的なエラーが発生します。
例15 衝突の解決
<?php
traitConstantsTrait{
public constFLAG_MUTABLE=1;
final public constFLAG_IMMUTABLE=5;
}
classConstantsExample{
useConstantsTrait;
public constFLAG_IMMUTABLE=5;// Fatal error
}
?> PHP 8.3.0 以降では、as 演算子を使ってfinal をトレイトからインポートしたメソッドに適用できるようになりました。 こうすることで、子クラスでそのメソッドをオーバーライドすることを防止できます。 しかし、トレイトを使うクラスは、未だそのメソッドをオーバーライドできます。
例16 トレイトからインポートするメソッドをfinal として定義する
<?php
traitCommonTrait
{
public functionmethod()
{
echo'Hello';
}
}
classFinalExampleA
{
useCommonTrait{
CommonTrait::methodas final;// The 'final' prevents child classes from overriding the method
}
}
classFinalExampleBextendsFinalExampleA
{
public functionmethod() {}
}
?>上の例の出力は、たとえば以下のようになります。
Fatal error: Cannot override final method FinalExampleA::method() in ...
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?>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):<?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.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'?>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 traitKeep 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!"; }}?>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();//cI 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) { ...}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,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 WorldNote 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.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.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.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?>(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.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;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 65If 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() )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>';?>don't forget you can create complex (embedded) traits as well<?phptraitName{// ...}traitAddress{// ...}traitTelephone{// ...}traitContact{ useName,Address,Telephone;}classCustomer{ useContact;}classInvoce{ useContact;}?>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 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/*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";}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).Trait can not have the same name as class because it will show: Fatal error: Cannot redeclare class