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つなのでエラーにならないことを期待しました。includeinclude_onceに例えれば、includeではなくinclude_onceに相当する動作を期待したのですが実際にはincludeに相当する動作をします。

この回避方法としてはトレイトB、トレイトCでトレイトAを取り込まないで、クラスDでトレイトABCを取り込むことが考えられます。

但しトレイトB、トレイトCがトレイトAを取り込むことがコード上で明示されないというデメリットがあります。これについてはコメントで注釈しています。

トレイトB、トレイトCをコーディングするとき、トレイトAに関するメンバーをIDEが補完してくれない可能性があります。PHPStormではトレイトをuseしているクラス、この場合はクラスDを通じてAのメンバーを候補に表示してくれます。Eclipseは調査していません。

ダイヤモンド継承の問題はPHP7.3で解消されたものと思われます。PHP8.0でこの問題が発生しないことを確認しています。

サイト内関連リンク