PHP 5.4.0 以降、PHP は traits と呼ばれるコード再利用の方法を実装しました。
Traits は、PHP のような単一継承言語用に用意されたコード再利用メカニズムです。トレイトは、単一継承言語の制約を軽減し、開発者が異なる階層内の独立したクラスでメソッド セットを自由に再利用できるように設計されています。特性とクラス構成のセマンティクスは、複雑さを軽減し、従来の多重継承とミックスインに関連する典型的な問題を回避する方法を定義します。
トレイトはクラスに似ていますが、きめ細かく一貫した方法で機能を組み合わせるように単純に設計されています。トレイトを単独でインスタンス化することはできません。これは、従来の継承に水平機能の組み合わせを追加します。つまり、アプリケーション クラスのメンバーを継承する必要がありません。
例 #1 特性の例
trait ezcReflectionReturnInfo{ function getReturnType(){} function getReturnDescription(){} } class ezcReflectionMethod extends ReflectionMethod{ use ezcReflectionReturnInfo; } class ezcReflectionFunction extends ReflectionFunction{ use ezcReflectionReturnInfo; }
Priority
基本クラスから継承されたメンバーは、特性によって挿入されたメンバーによってオーバーライドされます。優先順位は、現在のクラスのメンバーがトレイトのメソッドをオーバーライドし、トレイトが継承されたメソッドをオーバーライドすることです。
例 #2 優先順位の例
基本クラスから継承されたメンバーは、挿入された SayWorld トレイトの MyHelloWorld メソッドによってオーバーライドされます。その動作は、MyHelloWorld クラスで定義されたメソッドと一致しています。優先順位は、現在のクラスのメソッドが特性メソッドをオーバーライドし、次に特性メソッドが基本クラスのメソッドをオーバーライドすることです:
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!
例 #3 別の例優先順位
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!
複数の特性
use ステートメントにはカンマで区切って複数の特性がリストされており、それらはすべてクラスに挿入できます。
例 #4 複数のトレイトの使用法
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!
競合解決
2つのトレイトが同じ名前のメソッドを挿入する場合、競合が明示的に解決されないと致命的なエラーが発生します。 。
同じクラス内の複数のトレイトの名前の競合を解決するには、 replaceof 演算子を使用して、競合するメソッドのどれを使用するかを明示的に指定する必要があります。
上記のメソッドでは、他のメソッドを除外することのみが可能です。as 演算子は、競合するメソッドの 1 つを別の名前で導入できます。
例 #5 競合の解決
この例では、Talker は特性 A と B を使用します。 A と B は競合するメソッドを持っているため、トレイト B の smallTalk とトレイト A の bigTalk を使用することを定義します。
Aliased_Talker は、as 演算子を使用して、トークを B の bigTalk のエイリアスとして定義します。
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; } } $t = new Talker; $t->smallTalk(); //b $t->bigTalk(); //A $at = new Aliased_Talker; $at->smallTalk(); //b $at->bigTalk(); //A $at->talk(); //B
メソッドのアクセス制御を変更する
as 構文を使用して、メソッドのアクセス制御を調整することもできます。
例 #6 メソッドのアクセス制御を変更する
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;} }
特性から特性を作成する
クラスが特性を使用できるのと同じように、他の特性も特性を使用できます。特性を定義するときに 1 つ以上の特性を使用すると、他の特性の一部またはすべてのメンバーを組み合わせることができます。
例 #7 trait から trait を作成する
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 は抽象メソッドの使用をサポートします。
例 #8 は、抽象メソッドによる強制を表しています
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 の静的メンバー
静的変数は、trait メソッドによって参照できますが、trait によって定義することはできません。ただし、トレイトは、トレイトが使用されるクラスの静的メソッドを定義できます。
例 #9 静的変数
trait Counter{ public function inc(){ static $c = 0; $c = $c + 1; echo "{$c}<br>"; } } class C1{ use Counter; } class C2{ use Counter; } $o = new C1(); $o->inc(); //echo 1 $p = new C2; $p->inc(); //echo 1
例 #10 静的メソッド
trait StaticExample{ public static function doSomething(){ return 'Doing something.'; } } class Example{ use StaticExample; } Example::doSomething();
出力結果: Doing something.
属性
Trait は属性も定義できます。
例 #11 属性の定義
trait PropertiesTrait{ public $x = 1; } class PropertiesExample{ use PropertiesTrait; } $example = new PropertiesExample; $example->x;
特性が属性を定義する場合、クラスは同じ名前の属性を定義できません。定義しない場合は、エラーが生成されます。クラス内のプロパティの定義が特性内の定義と互換性がある (可視性と初期値が同じ) 場合、エラー レベルは E_STRICT であり、そうでない場合は致命的エラーです。
例 #12 対立
trait PropertiesTrait{ public $sname = true; public $different = false; } class PropertiesExample{ use PropertiesTrait; public $sname = true; //Strict Standards public $different = true; //致命错误 }