PHP5.4以降で『トレイト(trait)』というしくみが導入されたようです。
抽象クラス、インターフェイスときて、トレイトです。
トレイトは、多重継承のできないPHPで、継承することなくクラスにメンバ(メソッドやプロパティ)を追加できるしくみのようです。
抽象クラスやインターフェイスと同じく、トレイト単体では何も出来ないので、必ず何かしらのクラスを必要とします。
例のごとく、他サイト様が素晴らしいまとめをしてくれてます。
⇩ PHPのトレイトについては下記サイトへ
・PHP5.4 の新機能 trait のまとめと実際の利用例 | 株式会社インフィニットループ技術ブログ
・PHP5.4のtrait機能を理解する - Hack Your Design!
・trait時代のライブラリ設計を予想してみる #php5_4: Architect Note
⇩ PHPのトレイトの使い方などについては下記サイトへ
・PHP のトレイトに気をつける - tototoshi の日記
・PHP5.4のtraitを使ったシングルトンパターン実装によるtrait入門 - id:anatooのブログ
・PHP - シングルトンの抽象化クラス&トレイトを作成する - Qiita
そして、PHPマニュアル(PHP: トレイト - Manual )によると
トレイト
PHP 5.4.0 以降では、コードを再利用するための「トレイト」という仕組みが導入されました。
トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。
トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。
トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。
トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。
トレイト自身のインスタンスを作成することはできません。
昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。
つまり、継承しなくてもクラスのメンバーに追加できるようになります。
例1 トレイトの例
<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
優先順位
基底クラスから継承したメンバーよりも、トレイトで追加したメンバーのほうが優先されます。
優先順位は現在のクラスのメンバーが最高で、その次がトレイトのメソッド、 そしてその次にくるのが継承したメソッドとなります。
例2 優先順位の例
基底クラスから継承したメソッドは、MyHelloWorld に SayWorld トレイトから追加されたメソッドで上書きされます。
この挙動は、MyHelloWorld クラスで定義したメソッドでも同じです。
優先順位は現在のクラスのメンバーが最高で、その次がトレイトのメソッド、 そしてその次にくるのが継承したメソッドとなります。
<?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();
?>
上の例の出力は以下となります。
例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!
複数のトレイト
複数のトレイトをひとつのクラスに追加するには、use 文でカンマ区切りで指定します。
例4 複数のトレイトの使用例
<?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();
?>
上の例の出力は以下となります。
衝突の解決
同じ名前のメンバーを含む複数のトレイトを追加するときには、 衝突を明示的に解決しておかないと fatal エラーが発生します。
同一クラス内での複数のトレイト間の名前の衝突を解決するには、insteadof 演算子を使って そのうちのひとつを選ばなければなりません。
この方法はひとつのメソッドだけしか使えませんが、 as 演算子を使うと、 衝突するメソッドのいずれかを別の名前で含めることができます。
例5 衝突の解決
この例では、Talker がトレイト A と B を使います。
A と B には同じ名前のメソッドがあるので、 smallTalk はトレイト B を使って bigTalk はトレイト A を使うように定義します。
<?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;
}
}
?>
メソッドの可視性の変更
as 構文を使うと、 クラス内でのメソッドの可視性も変更することができます。
例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; }
}
?>
トレイトを組み合わせたトレイト
クラスからトレイトを使えるのと同様に、トレイトからもトレイトを使えます。
トレイトの定義の中でトレイトを使うと、 定義したトレイトのメンバーの全体あるいは一部を組み合わせることができます。
例7 トレイトを組み合わせたトレイト
<?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();
?>
上の例の出力は以下となります。
トレイトのメンバーの抽象化
トレイトでは、抽象メソッドを使ってクラスの要件を指定できます。
例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;
}
}
?>
トレイトの静的なメンバー
トレイトでは、静的なメンバーやメソッドを定義できます。
例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(); // 1 と表示
$p = new C2(); $p->inc(); // 1 と表示
?>
例10 静的なメソッド
<?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
プロパティ
トレイトにはプロパティも定義できます。
例11 プロパティの定義
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>
トレイトでプロパティを定義したときは、クラスでは同じ名前のプロパティを定義できません。 定義しようとすると、エラーが発生します。
クラス側での定義がトレイトでの定義と互換性がある (可視性も初期値も同じ) 場合は E_STRICT
、 それ以外の場合は fatal error となります。
例12 衝突の解決
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true; // Strict Standards
public $different = true; // Fatal error
}
?>
トレイトの特徴
- インスタンス化できません。
- キーワードuseでクラスに追加。
- メソッドとプロパティを持てます。
- 静的(static)メンバ(メソッド、プロパティ)を定義できます。
- 抽象(abstract)メソッドを定義できます。
- クラスにはいくつでもトレイトを追加できます。
(※複数トレイトの追加は、use文でコンマで区切り指定します。) - Trait 同士の優先順位は同じなので,同じクラスにメソッド名の重複する2つの Trait を使用した場合,デフォルトではエラーになります。
しかし "insteadof" と "as" 演算子を使用してコンフリクトを解決するように記述すれば,このエラーを回避することができます。 - as構文でメソッドの可視性(アクセス権)を変更できます。
- トレイトの中でトレイトが使えます。
コンフリクト 【 conflict 】
コンフリクトとは、競合、衝突、対立、葛藤、緊張などの意味を持つ英単語。
ITの世界では、複数の同種の何かが同じ資源を同時に利用しようとして競合状態になってしまうことを意味する場合が多い。
コンピュータシステムの中で共存する複数のソフトウェアやハードウェアが、同じ資源(メモリ領域やI/Oポート、名前空間など)を同時に利用しようとして奪い合いになったり、動作が不安定になったりすることをコンフリクトという。
プログラミングなどで、複数のライブラリなどが同じ名前空間やクラス名、変数名などを定義していて、両者を同時に利用できない状態になってしまうことをコンフリクトという。
データベースシステムやファイルシステムなどで、同じレコードやファイルなどに同時に更新要求が発生して正常に処理できない状態のことをコンフリクトという。
トレイトの注意点
インターフェイスを実装したクラスのオブジェクトのインスタンスであるかどうかも調べられる『instanceof演算子(型演算子)』 が使えないようです。
⇩ 詳しくは下記サイトへ
・PHPのtraitはinstanceofで確認できない - Perl日記
⇩ 型演算子については下記