トレイトの衝突解決
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 53/91。
2つのトレイトが同じ名前のメソッドを定義している場合、PHPは致命的なエラーをスローします。insteadof および as 演算子を使用して、この競合をどのように処理するかをPHPに明示的に指定する必要があります。
<?php
trait FileLogger {
public function log(string $message): void {
echo "File: $message";
}
}
trait DatabaseLogger {
public function log(string $message): void {
echo "DB: $message";
}
}
class Application {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger;
DatabaseLogger::log as logToDatabase;
}
}
$app = new Application();
$app->log("Error occurred");
$app->logToDatabase("Error occurred");
出力:
File: Error occurred
DB: Error occurredinsteadof 演算子は、競合が発生した際にどのトレイトのメソッドを使用するかを選択します。この例では、FileLogger::log が優先され、デフォルトの log() メソッドになります。as 演算子はエイリアスを作成し、別の名前で別のトレイトのメソッドにアクセスできるようにします。
as を使用して、メソッドの可視性を変更することもできます:
<?php
trait Greeting {
public function sayHello(): void {
echo "Hello!";
}
}
class Person {
use Greeting {
sayHello as private;
}
}
これにより、トレイト内では public であっても、Person クラス内では sayHello() が private になります。衝突解決により、トレイトのメソッドをクラスにどのように統合するかをきめ細かく制御できます。
チャレンジ
簡単異なるチャネルでメッセージを送信できる通知システムを構築しましょう。ただし、複数のトレイトが同じメソッド名を提供する場合の競合を解決する必要があります。
コードを4つのファイルに分けて構成します:
EmailNotifier.php—notify(string $message)メソッドを持つEmailNotifierというトレイトを作成します。このメソッドは"Email: [message]"を返します。SmsNotifier.php—notify(string $message)メソッドを持つSmsNotifierというトレイトを作成します。このメソッドは"SMS: [message]"を返します。AlertService.php— 両方のトレイトを使用するAlertServiceクラスを作成します。両方のトレイトにnotify()メソッドがあるため、この競合を解決する必要があります:EmailNotifier::notifyをデフォルトのnotify()メソッドとして使用しますSmsNotifier::notifyメソッドに対してnotifyBySmsというエイリアスを作成します
notifyAll(string $message)メソッドを追加します。main.php— 必要なすべてのファイルをインクルードし、AlertServiceインスタンスを作成します。1つの入力(メッセージ文字列)を受け取ります。次の3行を出力してください:- メッセージを指定して
notify()を呼び出した結果 - メッセージを指定して
notifyBySms()を呼び出した結果 - メッセージを指定して
notifyAll()を呼び出した結果
- メッセージを指定して
これは、名前の競合がある場合に insteadof がどのトレイトのメソッドを優先するかを解決し、as が別の名前で他のトレイトのメソッドへのアクセスを維持する方法を示しています。
チートシート
複数のトレイトが同じ名前のメソッドを定義している場合、PHPでは insteadof および as 演算子を使用した明示的な衝突解決が必要です。
insteadof 演算子は、どのトレイトのメソッドをデフォルトとして使用するかを選択します。
<?php
trait FileLogger {
public function log(string $message): void {
echo "File: $message";
}
}
trait DatabaseLogger {
public function log(string $message): void {
echo "DB: $message";
}
}
class Application {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger;
DatabaseLogger::log as logToDatabase;
}
}
$app = new Application();
$app->log("Error occurred"); // File: Error occurred
$app->logToDatabase("Error occurred"); // DB: Error occurred
as 演算子はトレイトのメソッドにエイリアスを作成し、別の名前でアクセスできるようにします。
また、as を使用してメソッドの可視性を変更することもできます。
<?php
trait Greeting {
public function sayHello(): void {
echo "Hello!";
}
}
class Person {
use Greeting {
sayHello as private;
}
}
自分で試してみよう
<?php
require_once 'AlertService.php';
// 入力を読み込む
$message = trim(fgets(STDIN));
// TODO: AlertServiceのインスタンスを作成する
// TODO: メッセージを指定してnotify()を呼び出した結果を表示する
// TODO: メッセージを指定してnotifyBySms()を呼び出した結果を表示する
// TODO: メッセージを指定してnotifyAll()を呼び出した結果を表示する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。