【PHP入門】継承(extends)、トレイト(trait)を使いこなそう!

PHPではクラスを継承したり、トレイトを使用することで、自クラス以外のメンバを引き継ぐことできます。

この記事では、クラスを継承するextendsやコードを再利用するtraitについて、以下の内容で解説していきます。

  • extendsの使い方
  • traitの使い方
  • extendsとtraitの優先順位
  • traitにおける衝突の解決方法

今回はそんな継承(extends)、トレイト(trait)について、わかりやすく解説します!

目次

クラスを継承する

ここでは、extendsを使用してクラスを継承する方法を紹介します。

extendsとは

クラスのメンバを他のクラスで継承したい場合にextendsを使用します。extendsを使用すればクラスを拡張することが可能で、サブクラスは親クラスからメソッドの内容を引き継ぐことができます。

extendsを使用した継承は、以下のように記述します。

書き方:

class クラス名 extends 継承元クラス名 {
  処理
}

extendsはクラスを宣言する時に、クラス名の後に「extends 継承元クラス名」で継承元である親クラスを指定します。このようにクラスを定義すると、そのクラスは継承元のクラスのメンバを引き継ぐことができます。

しかし、親クラスでprivate修飾子による権限が指定されている場合は、そのメンバを引き継ぐことはできませんので注意しましょう。

extendsの使い方

extendsを使用し、クラスを継承するサンプルです。

サンプルプログラム:

<?php

class ParentClass{

	public function workItem1($str){
		echo $str.'ParentClass <br>';
	}

	public function workItem2(){
		echo 'Processing of workItem2 of ParentClass. <br>';
	}

}

class ChildClass extends ParentClass{

	public function workItem1($str){
		echo $str.'ChildClass <br>';
	}

}

//インスタンスを生成
$parent = new ParentClass();
$child = new ChildClass();

//メソッドの呼出し
$parent->workItem1('Processing of ');
$child->workItem1('Processing of ');

$parent->workItem2();
$child->workItem2();

?>

実行結果:

Processing of ParentClass 
Processing of ChildClass 
Processing of workItem2 of ParentClass. 
Processing of workItem2 of ParentClass. 

サンプルでは親クラスであるParentClassと、extendsで親クラスを継承した子クラスであるChildClassを定義しています。実行結果のとおり、クラスの継承を行えば子クラスであるChildClassのインスタンスから親クラスのメソッドにアクセスすることができます。

また、ChildClassのインスタンスから親と子クラスの両方に存在するメソッドであるworkItem1にアクセスした場合、子クラスのメソッドが優先されます。

継承を使用した活用法

継承を活用すれば、親クラスの処理内容を子クラスで引き継いで処理を継続することが可能です。

以下は、親クラスの2つのメソッドで配列のキーと値をそれぞれ生成し、子クラスで親クラスを継承して配列を結合するサンプルです。

サンプルプログラム:

<?php

class FruitsClass{

	public function fruitsMethod(){
		//キー用の配列を生成する
		$fruits = ['apple', 'orange', 'melon'];
		return $fruits;
	}

	public function valueMethod(){
		//値用の配列を生成する
		$value = ["100円", "250円", "300円"];
		return $value;
	}
}

//FruitsClassを継承する
class CombClass extends FruitsClass{

	public function combMethod($array1, $array2){

		//2つの配列を結合する
		$fruits_array = array_combine($array1, $array2);
		print_r($fruits_array);
	}
}

//インスタンスを生成
$comb = new CombClass();

//メソッドの呼出し
$value_array1 = $comb->fruitsMethod();
$value_array2 = $comb->valueMethod();
$comb->combMethod($value_array1, $value_array2);

?>

実行結果:

Array
(
    [apple] => 100円
    [orange] => 250円
    [melon] => 300円
)

サンプルでは、親クラスであるFruitsClassの2つのメソッドでキー用の配列と値用の配列を生成しています。次に子クラスであるCombClassのメソッドで、親クラスで生成した2つの配列をarray_combine関数を使用して結合しています。

extendsによる継承を行えば、このように親クラスの処理を引き継げるので、各処理ごとにわかりやすく記述することができます。

サンプルで使用したarray_combine関数については、以下の記事で詳しく解説しています!

トレイトでコードを再利用する

ここでは、traitを使用してコードを再利用する方法を紹介します。

traitとは

PHPのバージョン5.4.0以降では「trait(トレイト)」と呼ばれるコードを再利用するための機能が導入されました。トレイトは継承することなくメンバを他のクラスで使用することができます。

PHPではクラスを多重継承することができませんでしたが、traitにより多重継承と同様なことができるようになりました。

トレイトは以下のように記述します。

書き方:

trait トレイト名1{
	処理
}

trait トレイト名2{
	処理
}

class クラス名{
	use トレイト名1, トレイト名2
}

トレイトは「trait トレイト名」で宣言します。トレイトにはメソッドやプロパティ、staticメンバを定義できます。

トレイトを利用するクラスはuseキーワードでトレイト名を指定します。一つのクラスから複数のトレイトを使用する場合は、トレイト名をカンマ区切りで指定します。

トレイトの使い方

トレイトを使用してコードを再利用するサンプルです。

サンプルプログラム:

<?php

trait Trait1{
	function workItem1(){
		echo 'Processing of Trait1.<br>';
	}
}

trait Trait2{
	function workItem2(){
		echo 'Processing of Trait2.<br>';
	}
}

class ChildClass{
	//useで使用するトレイトを指定する
	use Trait1, Trait2;

	function workItem3(){
		echo 'Processing of ChildClass.<br>';
	}

}

//インスタンスの生成
$child = new ChildClass();

//メソッドの呼出し
$child->workItem1();
$child->workItem2();
$child->workItem3();

?>

実行結果:

Processing of Trait1.
Processing of Trait2.
Processing of ChildClass.

サンプルでは、トレイトであるTrait1とTrait2を定義し、それらを利用するクラスであるChildClassを定義しています。ChildClassでuseキーワードで使用するトレイトを指定しておけば、extendsと同様にトレイトのメンバを引き継ぐことができます。

実行結果のとおり、ChildClassクラスのインスタンスから2つのトレイトのメンバが参照できていることがわかりますね!

オーバーライドの優先順位

あるクラスが継承とトレイトを同時に使い、親クラスとトレイトに同名のメンバがあった場合、どちらが優先されるのでしょうか。

次のサンプルプログラムで確認してみます。

サンプルプログラム:

<?php
class ParentClass {
	public function workItem() {
		echo 'Processing of ParentClass';
	}
}

trait Trait1 {
	public function workItem() {
		echo 'Processing of Trait1';
	}
}

class ChildClass extends ParentClass {
	use Trait1;
}

$child = new ChildClass();
$child->workItem();
?>

実行結果:

Processing of Trait1

クラスChildClassは、ParentClassを継承し、Trait1を利用します。

ParentClassにもTrait1にもworkItemメソッドが存在しますが、ChildClassのworkItemメソッドを実行するとTrait1の「Processing of Trait」が表示されます。継承したメンバよりトレイトで追加したメンバの方が優先されます。

衝突の解決方法

あるクラスが複数のトレイトを利用し、それらのトレイトに同名のメンバがあった場合はどうなるでしょうか?

次のサンプルプログラムで確認してみます。

サンプルプログラム:

<?php
trait Trait1 {
	public function workItem() {
		echo 'Processing of Trait1';
	}
}

trait Trait2 {
	public function workItem() {
		echo 'Processing of Trait2';
	}
}

class ChildClass {
	use Trait1, Trait2;
}

$child = new ChildClass();
$child->workItem();
?>

クラスChildClassはTrait1とTrait2を利用しますが、どちらのトレイトにもworkItemメソッドが存在します。

このプログラムを実行すると、以下のようなエラーが発生します。

実行結果(ログ):

PHP Fatal error:  Trait method workItem has not been applied, because there are collisions with other trait methods on ChildClass in  .....

このような名前の衝突を解決するには、insteadof演算子を使ってどちらを使用するか定義しておく必要があります。

「use トレイト名1, トレイト名2」の後をブロックで囲み、そのブロック内に以下のように記述します。

書き方:

使用するトレイト名::メソッド名 insteadof 使用されないトレイト名

Trait2のworkItemメソッドが使用されるように修正してみます。

サンプルプログラム修正:

<?php
trait Trait1 {
	public function workItem() {
		echo 'Processing of Trait1';
	}
}

trait Trait2 {
	public function workItem() {
		echo 'Processing of Trait2';
	}
}

class ChildClass {
	use Trait1, Trait2 {
		Trait2::workItem insteadof Trait1;
	}
}

$child = new ChildClass();
$child->workItem();
?>

実行結果:

Processing of Trait2

「Trait2::workItem insteadof Trait1;」でTrait2のworkItemメソッドが使用され、「Processing of Trait2」が表示されます。

まとめ

今回は継承(extends)、トレイト(trait)について解説しました。継承やトレイトを使いこなすことで、同じコードを書かず開発効率が上がり、わかりやすいコーディングができるようになります。

継承(extends)、トレイト(trait)について忘れてしまったら、この記事を思い出してくださいね。

この記事を書いた人

侍エンジニア塾は「人生を変えるプログラミング学習」をコンセンプトに、過去多くのフリーランスエンジニアを輩出したプログラミングスクールです。侍テック編集部では技術系コンテンツを中心に有用な情報を発信していきます。

目次