ホームページ制作 オフィスオバタ

PHPで長いクラスを分割記述する方法

動的なサイトを制作する場合、私はPHPでプログラムを組むのですが、少し悩みがあります。
それは、

クラスがとても長くなると見にくい。

ということです。

クラス以外のソースであれば、PHPファイルを分割し適度な長さにします。
ですが、クラスは範囲指定があるため、分割できません。

そのため、クラス内の処理が増えてくると、ソースがどんどん長くなっていきます。
長くなると、とても見にくくなります。

後からメンテナンスすることを考えると、長いソースはメンテがしにくいです。
だから、分割したいのですが、クラスは分割できない。

というジレンマに陥りました。

ところが色々調べてみると、間接的な方法ではありますが、クラスを分割する方法がありました。
備忘録として記録します。

クラスを分割する原理

とはいえどうやればいいの?
クラスそのものは、別ファイルに術できません。
1クラス1ファイルの原則があるためです。

そこで、別のファイルに書いた関数群を読み込むのです。
そして、読み込んだ関数群は、クラスの関数として使用できます。

別のファイルに書いた関数を、クラスの関数として使用できるということは、
事実上クラスファイルの分割になります。

事実、この方法で長すぎるクラスを、ファイル分割することができました。

具体的な記述方法

クラスに読み込む場合、通常の関数記述だけでは読み込みできません。
読み込めるようにする文法があります。
それがこちらです。

分割するファイル側の記述(読み込まれるファイル)

■trait_b.php

<?php

trait b
{

	function trait_sample()
	{
		echo "トレイト側で出力";
	}
}

?>

呼び出す側の記述(クラス側の記述)

■class_a.php

<?php
require_once("trait_b.php");

class a
{
	use b;
}
?>

クラスを呼び出す側の記述

<?php
require_once("class_a.php");		// クラス

$a = new a();
$a->trait_sample();
?>

実行結果

トレイト側で出力

クラスを分割する場合のルール

クラスで関数群を読み込むには、関数群を traitで囲います。
そして、そのtraitに名前を付けます。

上記サンプルだと、
trait b
と記述しているので、
「b」という名前の関数群となります。

クラス側では、2つのルールがあります。
一つは、require_once でファイルを読み込むことです。
外部ファイルなので、これはtraitに限らず必要なことです。

もう一つがuse を記述することです。
上記サンプルだと、
use b;
と記述しています。

これは、trait bをこのクラスで使用するという意味です。

あとは、クラスに記述した関数として呼び出すことができます。
サンプルでは、
$a = new a();
$a->trait_sample();
と記述しています。
class aには関数が一つもありませんが、trait bの関数が呼ばれています。

疑問 traitって何?

このやり方を知った時に、真っ先に「traitって何?」
と思いました。

知らない記述方法です。
調べてみると以下のようなことでした。

つまり、クラス専用の呼び出しパーツです。
なので、クラス以外では呼び出せません。
まさに、分割記述したいケースにぴったりのやり方です。

面倒ならば、クラスにまとめて書くのもよし。
クラスが長くてわかりにくいならば、traitで分割するのもよし。

自分のやり方に合わせて使用すればよいのです。

そのほかの疑問も調べてみました。

traitの使い方はわかりましたが、いくつか疑問がわきました。
それらを調べてみました。

Q. trait側にクラスと同じ名前の関数を作ったらどうなる?

エラーにはなりませんが、呼び出すとクラス側の関数が優先して呼び出されました。

Q. trait側にpraivate等で同じ変数名を作ったらどうなる?

重複エラーになりました。

Q. traitを呼び出している(use指定している)クラスを継承した場合、trait側の関数は呼び出せるか?

以下のようなクラスaを継承したclass c側から呼び出せました。

trait_b.php
<?php
trait b
{
	function trait_sample()
	{
		echo "トレイト側で出力";
	}
}
?>

class_a.php
<?php
require_once("trait_b.php");

class a
{
	use b;
}

class c extends a
{
}
?>

呼び出す側
<?php
require_once("class_a.php");		// クラス

$c = new c();
$c->trait_sample();
?>

Q. 継承しているクラス側でも同じtraitを呼び出したらどうなる?

エラーにはなりませんでした。
ですが、推奨されない使い方のようです。

<?php
require_once("trait_b.php");

class a
{
	use b;
}

class c extends a
{
	use b;
}
?>

Q. 複数のtraitを呼び出す場合、それぞれのtraitに同じ関数名があるとどうなる?

試したら、エラーになりました。
クラスとtraitで同じ関数名はエラーになりませんが、trait同士ならばエラーになります。

trait b
{
	function trait_sample()
	{
		echo "トレイトb側で出力";
	}
}

trait d
{
	function trait_sample()
	{
		echo "トレイトd側で出力";
	}
}

Q. trait側で記述したprivate等の変数は、クラス側で参照できるか?

できました。
ですが、あまり推奨されないようです。
参照する場合は、trait側に以下のような変数を参照する関数を作るのがセオリーということでした。

trait b
{
	private $b = "traitの変数";

	function b_value()
	{
		return $this->b;
	}
}

traitを使う場合にこうしたほうが良いというルール

実際にtraitを使ってみたところ、以下のようにしたほうが良いと思う点がありました。

クラスの利点は、同じ関数名や変数名を使っても、クラス名さえ違っていれば問題ないという点です。
ですが、traitでは重複名称は使わないに越したことはありません。
特に、複数のクラスから呼び出せる以上、単一名称にしておくほうが無難です。

そこで、trait名を頭につければ重複することはなくなります。
なぜならば、trait名は重複すると文法エラー人るためです。

例えば、trait b という名称ならば、
praivate b_value;
function b_disp()
{
}
というように、trait名称をつけておけば、traitの再利用の際に、名称重複エラーを心配しなくてもよくなります。

trait側で作成した変数は、システム的にはクラス側でダイレクト参照できますが、以下の理由で推奨されないということでした。

trait側で、変数名を変えた場合、使用しているクラスすべて修正しなければいけない

traitは汎用的に呼び出されるため、traitは独立性を高めておいたほうがよいということです。
クラスはその辺気にしなくてもよかったのですが、traitでは気おつけなければいけませんでした。

ですが、クラスのファイル分割としてとらえれば、とても利便性が高いと感じました。

モバイルバージョンを終了