PHPのトレイトを使うと多重継承に相当する処理を記述できることを3種類の帳票の例で紹介しました。
trait A
{
public function a(){}
}
trait B
{
use A;
}
trait C
{
use A;
}
class D
{
use B, C;
} //メソッド競合エラー
$d = new D();
トレイトを使い込む内に1つの制限が見えてきました。それはダイヤモンド継承ができないことです。
右のコードで、クラスDは、トレイトBとトレイトCを取り込んでいます。トレイトB、トレイトC共にトレイトAを取り込んでいます。トレイトAはメソッドaを持っています。この場合、PHPではメソッドaが衝突しているという意味のエラーが発生します。トレイトBとトレイトCのそれぞれにメソッドaがあるとみなすようです。
私は共通のトレイトAの実体は1つなのでエラーにならないことを期待しました。includeとinclude_onceに例えれば、includeではなくinclude_onceに相当する動作を期待したのですが実際にはincludeに相当する動作をします。
この回避方法としてはトレイトB、トレイトCでトレイトAを取り込まないで、クラスDでトレイトA、B、Cを取り込むことが考えられます。
但しトレイトB、トレイトCがトレイトAを取り込むことがコード上で明示されないというデメリットがあります。これについてはコメントで注釈しています。
トレイトB、トレイトCをコーディングするとき、トレイトAに関するメンバーをIDEが補完してくれない可能性があります。PHPStormではトレイトをuseしているクラス、この場合はクラスDを通じてAのメンバーを候補に表示してくれます。Eclipseは調査していません。
ダイヤモンド継承の問題はPHP7.3で解消されたものと思われます。PHP8.0でこの問題が発生しないことを確認しています。