こんにちは。よっしーです(^^)
今日は、PHPのTraits(トレイト)についてご紹介します。
背景
PHP8.2を利用したAPIを開発しているときに Traits を利用したので、そのときの調査内容を備忘としてのこしました。
こちらのサイトを参考にしています。
Traitsについて
Traits(トレイト)は、PHPのような単一継承のプログラム言語でコードを再利用するためのメカニズムです。
Traitsは、PHPなどの単一継承言語において、複数の独立したクラスでメソッドセットを自由に再利用することを可能にするものです。Traitsとクラスの組み合わせのセマンティクスは、複雑さを減少させ、多重継承やMixinsに関連する典型的な問題を回避する方法として定義されています。
Traitはクラスに似ていますが、機能を細かく整理された一貫性のある方法でグループ化することを意図しています。Trait自体を独立してインスタンス化することはできません。Traitは伝統的な継承に追加され、挙動の水平組み合わせを可能にします。つまり、継承を必要とせずにクラスメンバーを適用することができます。
トレイトの例です。
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
この例では、ezcReflectionReturnInfo
という名前のトレイトが定義されています。このトレイトには2つのメソッド、getReturnType()
と getReturnDescription()
が含まれています。
そして、ezcReflectionMethod
クラスと ezcReflectionFunction
クラスはそれぞれ ezcReflectionReturnInfo
トレイトを使用しています。これにより、これらのクラスはトレイト内で定義されたメソッドを継承し、それを利用することができます。
トレイトを使用することで、同じ機能を持つ異なるクラス間でコードを再利用できるようになります。これにより、コードの重複を減少させ、保守性を向上させることができます。
優先順位
上記の説明とコード例では、トレイト間のメソッドの優先順位、複数のトレイトの組み合わせ、およびメソッド名の競合の解決方法について説明されています。
例2: 優先順位の例
この例では、Base
クラスから継承されたメソッドが SayWorld
トレイトによってオーバーライドされています。また、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: 別の優先順位の例
この例では、異なるトレイト HelloWorld
を使用した場合の優先順位の例が示されています。TheWorldIsNotEnough
クラス内で HelloWorld
トレイトとクラス自体で同じ名前のメソッドが競合しています。そのため、TheWorldIsNotEnough
クラスで定義されたメソッドが優先されます。
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!
複数のトレイト
例4: 複数のトレイトの使用
この例では、Hello
トレイトと World
トレイトを MyHelloWorld
クラスで複数のトレイトとして使用しています。これにより、クラスは複数のトレイトからメソッドを継承でき、それらを利用することができます。
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!
名前の競合解決
例5: 名前の競合解決
トレイト内で同じ名前のメソッドを挿入した場合、競合が発生し、競合が明示的に解決されない限り致命的なエラーが発生します。競合解決のために insteadof
演算子を使用して、競合するメソッドのうちどちらを選ぶかを指定する必要があります。
また、as
演算子を使用して、メソッドに別名を追加することもできます。as
演算子はメソッドの名前を変更するわけではなく、他のメソッドにも影響を与えません。
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;
}
}
この例では、Talker
クラスと Aliased_Talker
クラスがトレイト A
と B
を使用しており、トレイト間で競合が発生しています。insteadof
演算子を使用して、どちらのメソッドを優先するかを指定しています。また、Aliased_Talker
クラスでは as
演算子を使用して、B
トレイトの bigTalk
メソッドを talk
という別名で利用しています。
おわりに
今日は、PHPのTraits(トレイト)についてご紹介しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント