Menu
Coddy logo textTech

トレイトの衝突解決

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 occurred

insteadof 演算子は、競合が発生した際にどのトレイトのメソッドを使用するかを選択します。この例では、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 になります。衝突解決により、トレイトのメソッドをクラスにどのように統合するかをきめ細かく制御できます。

challenge icon

チャレンジ

簡単

異なるチャネルでメッセージを送信できる通知システムを構築しましょう。ただし、複数のトレイトが同じメソッド名を提供する場合の競合を解決する必要があります。

コードを4つのファイルに分けて構成します:

  • EmailNotifier.phpnotify(string $message) メソッドを持つ EmailNotifier というトレイトを作成します。このメソッドは "Email: [message]" を返します。
  • SmsNotifier.phpnotify(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()を呼び出した結果を表示する
?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン