クラスの継承とトレイト(extends、trait)

「extends」を使うと、定義済みのクラスを継承し、拡張することができます。書式は「class クラス名 extends 親クラス名{処理・・・}」です。拡張したクラスには定義を追加することが可能ですが、すでに存在する変数や関数を未定義にすることはできません。また、この書式での多重継承はできません。

__construct()メソッドを使ったクラスの継承

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
class gosenzo{
public function __construct(){
echo "先祖クラスが呼び出されました。";
}
}
class papamama extends gosenzo{
public function __construct(){
echo "親クラスが呼び出されました。";
}
}
class child1 extends papamama{
public function __construct(){
// 祖先クラスの呼び出しがOKなパターン
gosenzo::__construct();
// NGのパターン
// parent::gosenzo();
}
}
class child2 extends papamama{
public function __construct(){
// 親クラスの呼び出しがOKなパターン
parent::__construct();
// NGのパターン
// parent::papamam();
// papamam::__construct();
}
}
echo "new child1→";
new child1();
echo "new child2→";
new child2();
?>
実行結果:new child1→先祖クラスが呼び出されました。new child2→親クラスが呼び出されました。

PHP7で動くクラス継承

PHP7ではクラス名と同じ名前のメソッドを定義したコンストラクタしかないケースではE_DEPRECATEDエラーになります。 __construct()メソッドを実装していればE_DEPRECATEDエラーは出力されません。

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
// 先祖クラス
class senzo{
public function senzo2(){
echo "先祖クラスが呼び出されました。";
}
}
// 親クラス
class oya extends senzo{
public function oya2(){
echo "親クラスが呼び出されました。";
}
}
// 子クラスA
class childA extends oya{
public function childA2(){
// 祖先クラスの呼び出しがOKなパターン
parent::senzo2();
// 下の書式は祖先、親、子においてクラス名と内包するメソッド名が同名であれば使えます。
//senzo::__construct();
}
}
// 子クラスB
class childB extends oya{
public function childB2(){
// 親クラスの呼び出しがOKなパターン
parent::oya2();
// 下の書式は祖先、親、子においてクラス名と内包するメソッド名が同名であれば使えます。
//oya::__construct();
}
}
echo "new childA→";
$instance=new childA;
$instance->childA2();
echo "new childB→";
$instance=new childB;
$instance->childB2();
?>
実行結果:new childA→先祖クラスが呼び出されました。new childB→親クラスが呼び出されました。

PHP4形式のコンストラクタ

以下の書式はPHP7以降で非推奨となっており、エラーを例外に変換してevalにてコードを実行させると「Fatal error: Uncaught ErrorException: Methods with the same name as their class will not be constructors in a future version of PHP;メソッド名・・・省略」と表示されます。つまり、クラスと同じ名前のメソッドを使う書式は、PHPの将来のバージョンで削除される予定のようです。実行結果は、PHP7でエラーの例外変換なしでの出力結果です。

// 先祖クラス
class gosenzosama{
public function gosenzosama(){
echo "先祖クラスが呼び出されました。";
}
}
// 親クラス
class oyago extends gosenzosama{
public function oyago(){
echo "親クラスが呼び出されました。";
}
}
// 子クラスAA
class childAA extends oyago{
public function childAA(){
// 祖先クラスの呼び出しがOKなパターン
parent::gosenzosama();
gosenzosama::__construct();
}
}
// 子クラスBB
class childBB extends oyago{
public function childBB(){
// 親クラスの呼び出しがOKなパターン
parent::oyago();
oyago::__construct();
}
}
echo "new childAA→";
new childAA();
echo "new childBB→";
new childBB();
実行結果:new childAA→先祖クラスが呼び出されました。先祖クラスが呼び出されました。new childBB→親クラスが呼び出されました。親クラスが呼び出されました。

トレイトによる実質的な多重継承

PHPのクラスで多重継承はできません。しかし一つのクラスから複数のトレイトを使うことが可能になっており、実質的には多重継承と同じようなことが実現できます。extendsによるクラス継承は垂直な親子関係にあるコードを再利用しますが、トレイトはクラス間における水平な位置にあるコードを再利用できます。また、トレイトはインスタンス化が出来ないため、抽象メソッドを定義できるようになっています。メソッド名の衝突は、「insteadof」文で指定したり「as」で別名をつけて回避できますが、プロパティ名の衝突は避けられないため、競合に気をつけます。更にconstによる定義はできませんが、スタティックプロパティやスタティックメソッドなどが定義できます。利用にはuse文を使用し、複数必要であるケースではカンマで区切って指定します。書式は、「class sampleClass{use trait1,trait2,trait3;}」といった感じになります。