PHPのトレイトでダイヤモンド継承

PHPのトレイトを使うと多重継承に相当する処理を記述できることを3種類の帳票の例で紹介しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
trait A
{
public function a(){}
}
trait B
{
use A;
}
trait C
{
use A;
}
class D
{
use B, C;
} //メソッド競合エラー

$d = new D();

トレイトを使い込む内に1つの制限が見えてきました。それはダイヤモンド継承ができないことです(現在ではこの問題は解消されています。恐らくPHP7.3で解消されたものと思われます)。

右のコードで、クラス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は調査していません。

参照