Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】SplSubject::notify()メソッドの使い方

notifyメソッドの使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

notifyメソッドは、SplSubjectインターフェースを実装するクラスにおいて、そのオブジェクトの状態が変化した際に、登録されている全てのオブザーバーに通知を行うメソッドです。このメソッドは、GoFデザインパターンのObserverパターンにおけるSubject(被監視者)の役割を果たすオブジェクトが、自身を監視しているObserver(監視者)へ状態の変更を伝えるために利用されます。

具体的には、SplSubjectを実装したクラスのインスタンスで何らかの重要なイベントが発生したり、保持しているデータが更新されたりしたときに、開発者が明示的にnotifyメソッドを呼び出します。notifyメソッドが実行されると、事前にこのSubjectにattachメソッドで登録されていた全てのSplObserverオブジェクトのupdateメソッドが自動的に順次呼び出されます。これにより、Subjectは自身の状態変化をObserverに一斉に伝え、Observerはそれを受けて適切な処理を実行することができます。

この仕組みは、SubjectとObserverの間を疎結合に保ちながら、イベント発生時の連携を可能にします。ObserverはSubjectの内部状態を直接監視することなく、通知を介して必要な情報を受け取ることができるため、システムの柔軟性や拡張性が向上します。主にGUIイベント処理、データベースの状態変更通知、または複数のコンポーネントが同一のオブジェクトの状態に依存するような設計において、その真価を発揮します。

構文(syntax)

1abstract public function notify(): void;

引数(parameters)

引数なし

引数はありません

戻り値(return)

戻り値なし

戻り値はありません

サンプルコード

PHP SplSubject notifyでLINE通知する

1<?php
2
3declare(strict_types=1);
4
5/**
6 * 監視される側(Subject)を表現するクラスです。
7 * SplSubjectインターフェースを実装し、Observer(監視者)の登録・解除・通知を行います。
8 * 状態が変更された際に、登録されているすべてのObserverにnotify()メソッドを通じて通知します。
9 */
10class EventMonitor implements SplSubject
11{
12    /**
13     * @var SplObserver[] 登録されているObserverのリスト
14     */
15    private array $observers = [];
16
17    /**
18     * @var string 現在の監視対象の状態
19     */
20    private string $currentState = '初期状態';
21
22    /**
23     * Observerを登録します。
24     *
25     * @param SplObserver $observer 登録するObserverインスタンス
26     */
27    public function attach(SplObserver $observer): void
28    {
29        $this->observers[] = $observer;
30        echo "[EventMonitor] Observerが登録されました。\n";
31    }
32
33    /**
34     * Observerの登録を解除します。
35     *
36     * @param SplObserver $observer 解除するObserverインスタンス
37     */
38    public function detach(SplObserver $observer): void
39    {
40        foreach ($this->observers as $key => $obs) {
41            if ($obs === $observer) {
42                unset($this->observers[$key]);
43                echo "[EventMonitor] Observerが解除されました。\n";
44                return;
45            }
46        }
47    }
48
49    /**
50     * 登録されているすべてのObserverに通知します。
51     * SplSubject::notify() の要件通り、引数なし、戻り値なしです。
52     * Observerのupdate()メソッドを呼び出し、自身のインスタンスを渡します。
53     */
54    public function notify(): void
55    {
56        echo "[EventMonitor] 全てのObserverに通知しています...\n";
57        foreach ($this->observers as $observer) {
58            $observer->update($this);
59        }
60    }
61
62    /**
63     * 監視対象の状態を変更し、Observerに通知します。
64     * このメソッド内でnotify()が呼び出されることで、状態変化がObserverに伝わります。
65     *
66     * @param string $newState 新しい状態
67     */
68    public function changeState(string $newState): void
69    {
70        $this->currentState = $newState;
71        echo "[EventMonitor] 状態が「" . $this->currentState . "」に変更されました。\n";
72        $this->notify(); // 状態変更後に通知を発行
73    }
74
75    /**
76     * 現在の状態を取得します。
77     * Observerが通知を受け取った際に、Subjectのどの状態変化かを判断するために利用されます。
78     *
79     * @return string 現在の状態
80     */
81    public function getCurrentState(): string
82    {
83        return $this->currentState;
84    }
85}
86
87/**
88 * 監視する側(Observer)を表現し、LINE通知を模倣するクラスです。
89 * SplObserverインターフェースを実装し、Subjectからの通知を受け取ります。
90 */
91class LineNotifierObserver implements SplObserver
92{
93    /**
94     * Subjectからの通知を受け取った際に実行されるメソッドです。
95     * このメソッド内で、LINEに通知を送る処理を模倣します。
96     *
97     * @param SplSubject $subject 通知を発行したSubjectインスタンス
98     */
99    public function update(SplSubject $subject): void
100    {
101        // 実際のLINE通知API呼び出しの代わりに、メッセージを出力します。
102        // ここにLINE Notify APIを呼び出すHTTPリクエストなどのコードを記述します。
103        // 例:
104        // $message = "システムイベント発生: " . $subject->getCurrentState();
105        // sendLineNotification($message); // 独自のLINE通知関数を呼び出す
106        
107        if ($subject instanceof EventMonitor) {
108            echo "[LINE Notify] 通知を受信しました!システム状態が「" . $subject->getCurrentState() . "」になりました。\n";
109        } else {
110            echo "[LINE Notify] 不明なSubjectからの通知です。\n";
111        }
112    }
113}
114
115// --- ここからサンプルコードの実行部分 ---
116
117// イベントを監視するSubjectのインスタンスを作成
118$monitor = new EventMonitor();
119
120// LINE通知を行うObserverのインスタンスを作成
121$lineNotifier = new LineNotifierObserver();
122
123// ObserverをSubjectに登録
124$monitor->attach($lineNotifier);
125
126echo "\n--- システムの状態を変化させます ---\n";
127
128// Subjectの状態を変更すると、登録されたObserverに自動的に通知されます。
129$monitor->changeState("サーバー起動");
130
131echo "\n--- さらに状態を変化させます ---\n";
132
133$monitor->changeState("処理実行中");
134
135echo "\n--- 異常が発生したと仮定します ---\n";
136
137$monitor->changeState("エラー発生!要確認");
138
139// Observerの登録解除も可能です
140// $monitor->detach($lineNotifier);
141// echo "\n--- Observerを解除しました ---\n";
142// $monitor->changeState("復旧"); // この状態変化ではLINE通知は行われません

SplSubject::notify()メソッドは、PHPの標準機能として提供されるObserver(監視者)パターンの一部で、監視される側(Subject)が自身の状態変化を監視者たちに通知するために利用されます。このメソッドは引数を一切取らず、また特定の値を返すこともありません。

サンプルコードでは、システムイベントを監視するEventMonitorクラスがSplSubjectインターフェースを実装し、notify()メソッドを実装しています。EventMonitorの状態がchangeState()メソッドによって「サーバー起動」や「エラー発生」といった新しい状態に変わるたびに、内部でnotify()メソッドが呼び出されます。

notify()が実行されると、EventMonitorに事前に登録されているすべての監視者(Observer)に対し、それぞれのupdate()メソッドが自動的に実行されます。今回の例では、LINE通知を模倣するLineNotifierObserverが監視者として登録されており、update()メソッドを通じて「システム状態が『〇〇』になりました」といったメッセージが出力されます。

このように、notify()メソッドを使うことで、システムの状態変化に応じて、LINE通知のような外部サービスへの連携処理を柔軟にトリガーできる仕組みを構築できます。

SplSubject::notifyメソッドは、監視対象(Subject)の状態が変化した際に、登録されている全ての監視者(Observer)にその変化を伝える役割を担います。このメソッドは引数も戻り値も持たず、インターフェースの規約を厳守する必要があります。実際に通知を受け取り、具体的な処理を行うのはObserver側のupdateメソッドです。サンプルコードのLineNotifierObserverはLINE通知を模倣していますが、実際にLINEへメッセージを送る場合は、updateメソッド内にLINE Notify APIを呼び出すHTTPリクエストなどの実装が必要です。その際、APIキーの安全な管理や、ネットワーク障害などに対するエラーハンドリングも適切に考慮してください。この設計は、システムの特定イベント発生時に複数の処理を柔軟に連携させたい場合に有効です。

PHPでPostgreSQLのLISTEN/NOTIFYをSplSubject::notify()で処理する

1<?php
2
3/**
4 * プログラミング言語リファレンス情報: SplSubject::notify()
5 * キーワード: php postgresql listen notify
6 *
7 * このサンプルコードは、PostgreSQLのLISTEN/NOTIFY機能を使用して
8 * データベースからの通知をPHPアプリケーションで受け取り、
9 * その通知をアプリケーション内部のオブザーバーパターン(SplSubject/SplObserver)で処理する方法を示します。
10 *
11 * 想定されるシナリオ:
12 * 1. PHPアプリケーションがPostgreSQLデータベースに接続し、特定のチャネルを「LISTEN」する。
13 * 2. データベース側で何らかのイベント(例: 新しいデータが挿入されたトリガーなど)が発生し、
14 *    「NOTIFY」コマンドでそのチャネルに通知を送る。
15 * 3. PHPアプリケーションがその通知を受け取り、アプリケーション内部で登録されたオブザーバー(監視者)に
16 *    そのイベントをさらに「通知」する(SplSubject::notify() の役割)。
17 *
18 * 動作にはPostgreSQLデータベースと、PHPのpg_connectなどのPostgreSQL拡張が必要です。
19 * 以下の環境変数を設定するか、直接コード内の接続情報 (`$dsn`) を実際のデータベース情報に変更してください。
20 * PG_HOST, PG_PORT, PG_DBNAME, PG_USER, PG_PASSWORD
21 *
22 * PostgreSQL側でのNOTIFYコマンドの発行例:
23 * SELECT pg_notify('my_channel', '{"event": "データ更新", "id": 123}');
24 *
25 */
26
27// -------------------------------------------------------------------------
28// 1. オブザーバー(監視者)の定義
29//    => Subjectからの通知を受け取り、具体的な処理を実行する役割
30// -------------------------------------------------------------------------
31// SplObserverインターフェースを実装することで、Subjectからの通知を受け取れるようになります。
32class MyPostgresObserver implements SplObserver
33{
34    private string $name;
35
36    public function __construct(string $name)
37    {
38        $this->name = $name;
39    }
40
41    /**
42     * Subjectから通知を受け取った際に実行されるメソッドです。
43     * SplSubjectインスタンスを通じて、通知元の情報を取得できます。
44     *
45     * @param SplSubject $subject 通知を発行したSubjectのインスタンス
46     */
47    public function update(SplSubject $subject): void
48    {
49        // $subjectがPostgresEventSubjectのインスタンスであることを前提とする
50        if ($subject instanceof PostgresEventSubject) {
51            $notificationData = $subject->getLastNotificationData();
52            echo sprintf(
53                "[%s] Observer '%s' received notification: %s\n",
54                date('Y-m-d H:i:s'),
55                $this->name,
56                json_encode($notificationData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
57            );
58            // ここで通知されたデータ ($notificationData) を使用して、
59            // ログ記録、キャッシュクリア、他のシステムへの連携などの具体的な処理を行います。
60        } else {
61            echo sprintf(
62                "[%s] Observer '%s' received a notification from an unknown subject.\n",
63                date('Y-m-d H:i:s'),
64                $this->name
65            );
66        }
67    }
68}
69
70// -------------------------------------------------------------------------
71// 2. サブジェクト(被監視者・通知元)の定義
72//    => Observerを登録・解除し、状態変化があった際にObserverに通知する役割
73// -------------------------------------------------------------------------
74// SplSubjectインターフェースを実装することで、Observerを登録・解除し、通知を発行できます。
75class PostgresEventSubject implements SplSubject
76{
77    /** @var SplObserver[] 登録されているオブザーバーのリスト */
78    private array $observers = [];
79
80    /** @var array|null 最後に受け取ったPostgreSQLからの通知データ */
81    private ?array $lastNotificationData = null;
82
83    /**
84     * オブザーバーを登録します。
85     *
86     * @param SplObserver $observer 登録するオブザーバーのインスタンス
87     */
88    public function attach(SplObserver $observer): void
89    {
90        $hash = spl_object_hash($observer);
91        if (!isset($this->observers[$hash])) {
92            $this->observers[$hash] = $observer;
93            echo "Observer attached.\n";
94        }
95    }
96
97    /**
98     * オブザーバーの登録を解除します。
99     *
100     * @param SplObserver $observer 解除するオブザーバーのインスタンス
101     */
102    public function detach(SplObserver $observer): void
103    {
104        $hash = spl_object_hash($observer);
105        if (isset($this->observers[$hash])) {
106            unset($this->observers[$hash]);
107            echo "Observer detached.\n";
108        }
109    }
110
111    /**
112     * 登録されている全てのオブザーバーに通知します。
113     * これがリファレンス情報である `SplSubject::notify()` メソッドの実装です。
114     * オブザーバーパターンにおける「通知」の核心となる部分です。
115     */
116    public function notify(): void
117    {
118        echo "Notifying observers...\n";
119        foreach ($this->observers as $observer) {
120            // 各オブザーバーの update() メソッドを呼び出し、自分自身 (Subject) を渡します。
121            $observer->update($this);
122        }
123    }
124
125    /**
126     * PostgreSQLから受け取った最新の通知データを設定します。
127     * このデータは、notify() メソッドを通じてオブザーバーに間接的に渡されます。
128     *
129     * @param array $data PostgreSQLのNOTIFYコマンドのペイロード
130     */
131    public function setLastNotificationData(array $data): void
132    {
133        $this->lastNotificationData = $data;
134    }
135
136    /**
137     * 最新の通知データを取得します。
138     * オブザーバーは自身の update() メソッド内でこのデータを参照します。
139     *
140     * @return array|null 最新の通知データ、またはデータがない場合はnull
141     */
142    public function getLastNotificationData(): ?array
143    {
144        return $this->lastNotificationData;
145    }
146}
147
148// -------------------------------------------------------------------------
149// 3. メイン処理:PostgreSQLとの連携とオブザーバーパターンの利用
150// -------------------------------------------------------------------------
151
152// PostgreSQL接続設定を環境変数から読み込むか、直接設定します。
153$host = getenv('PG_HOST') ?: 'localhost';
154$port = getenv('PG_PORT') ?: '5432';
155$dbname = getenv('PG_DBNAME') ?: 'testdb';    // 実際のデータベース名に変更してください
156$user = getenv('PG_USER') ?: 'testuser';      // 実際のユーザー名に変更してください
157$password = getenv('PG_PASSWORD') ?: 'testpass'; // 実際のパスワードに変更してください
158$channel = 'my_channel';                     // PostgreSQLでLISTENするチャンネル名
159
160$dsn = sprintf("host=%s port=%s dbname=%s user=%s password=%s",
161    $host, $port, $dbname, $user, $password);
162
163// データベースへの接続を試みる
164$connection = pg_connect($dsn);
165
166if (!$connection) {
167    die("エラー: PostgreSQLデータベースに接続できませんでした。接続設定を確認してください。\n");
168}
169
170echo "PostgreSQLデータベースに接続しました。\n";
171
172// LISTENコマンドを発行
173// これにより、指定されたチャンネルでのNOTIFYイベントをリッスンします。
174// pg_query() で SQL を実行します。
175$listenResult = pg_query($connection, "LISTEN {$channel}");
176if (!$listenResult) {
177    die("エラー: チャンネル '{$channel}' のリッスンを開始できませんでした。\n");
178}
179echo "チャンネル '{$channel}' で通知を待機しています...\n";
180
181// サブジェクトとオブザーバーを初期化
182$subject = new PostgresEventSubject();
183$observer1 = new MyPostgresObserver('LoggerObserver');
184$observer2 = new MyPostgresObserver('CacheManagerObserver');
185
186// オブザーバーをサブジェクトに登録
187$subject->attach($observer1);
188$subject->attach($observer2);
189
190echo "\n--- PostgreSQLからの通知を待っています ---\n";
191echo "PostgreSQLクライアントで以下のコマンドを実行してテストできます:\n";
192echo "  SELECT pg_notify('{$channel}', '{\"message\": \"hello from db\", \"timestamp\": \"" . date('Y-m-d H:i:s') . "\"}');\n";
193echo "  SELECT pg_notify('{$channel}', '{\"event_type\": \"user_registered\", \"user_id\": 1, \"username\": \"Alice\"}');\n";
194echo "----------------------------------------\n";
195
196// 無限ループでPostgreSQLからの通知をポーリングします。
197// pg_get_notify() は、通知があるまで処理をブロックします。
198while (true) {
199    // pg_get_notify() は、キューに通知がある場合にそれを返します。
200    // 通知がない場合は、新しい通知が来るまでプログラムの実行をブロックします。
201    $notification = pg_get_notify($connection);
202
203    if ($notification) {
204        echo "\n>>> PostgreSQL通知を受け取りました! <<< \n";
205        
206        // 通知のペイロード(データ部分)をJSONとしてパースを試みます。
207        $payload = json_decode($notification['payload'], true);
208
209        if (json_last_error() === JSON_ERROR_NONE) {
210            // 正常にJSONとしてパースできた場合
211            $subject->setLastNotificationData($payload);
212        } else {
213            // JSONでない、またはパースエラーの場合、生のペイロードをデータとして扱う
214            $subject->setLastNotificationData(['raw_payload' => $notification['payload']]);
215            echo "警告: 通知ペイロードが有効なJSONではありませんでした。生のデータで処理します。\n";
216        }
217
218        // サブジェクトのnotify()メソッドを呼び出し、登録されている全てのオブザーバーに通知を転送します。
219        // これが `SplSubject::notify()` の具体的な利用例です。
220        $subject->notify();
221        echo "----------------------------------------\n";
222        // exit(0); // デバッグやテスト目的で、1回通知を受け取ったら終了したい場合はコメント解除
223    }
224
225    // pg_get_notify() はブロッキング関数なので、通常は追加のスリープは不要です。
226    // 通知が来ない間は、この関数がOSレベルで待機します。
227}
228
229// このサンプルコードは無限ループのため、通常はこの行には到達しませんが、
230// 実際のアプリケーションでは、プログラム終了時にデータベース接続を閉じることが重要です。
231pg_close($connection);
232
233?>

このサンプルコードは、PHPの標準機能であるSplSubjectインターフェースが提供するnotify()メソッドの利用方法を、PostgreSQLデータベースのLISTEN/NOTIFY機能との連携を通じて解説しています。

SplSubject::notify()メソッドは、デザインパターンの一つであるオブザーバーパターンにおいて中心的な役割を果たすメソッドです。これは、SplSubjectを実装したオブジェクト(被監視者)の状態に変化があった際、自身に登録されているすべてのオブザーバー(監視者)に対してその変化を通知するために呼び出されます。このメソッドは引数を一切受け取らず、また、戻り値もありません。その主な機能は、登録された各オブザーバーのupdate()メソッドを順次実行し、通知元であるSplSubject自身のインスタンスを引数として渡すことにあります。

具体的には、このコードではPostgreSQLデータベースから特定のチャネルへの通知を受け取ると、その通知内容がPostgresEventSubjectクラス(SplSubjectを実装)に設定されます。その後、$subject->notify()が呼び出されることで、PostgresEventSubjectに事前に登録された複数のMyPostgresObserverインスタンスのupdate()メソッドが実行されます。これにより、各オブザーバーはデータベースからのイベントに応じた独自の処理(例: ログ記録、キャッシュ更新)を実行し、外部からの通知をアプリケーション内部で柔軟に処理できる仕組みを実現しています。

このサンプルコードは、PostgreSQLからの通知をPHPのオブザーバーパターンで処理する、常駐プロセス(デーモン)としての利用を想定しています。データベース接続情報は、本番環境では環境変数などで安全に管理し、直接コードに書き込まないようご注意ください。SplSubject::notify()は、PostgreSQLから受け取ったデータを元に、登録された全てのオブザーバー(監視者)へ一斉に通知を転送し、具体的な処理を促す中心的な役割を担います。通知ペイロードはJSON形式を期待していますが、形式が異なる場合の堅牢なエラーハンドリングも重要です。Webリクエストの都度実行する通常のPHP処理とは異なり、while (true)による無限ループで継続的に動作する点に特にご留意ください。

関連コンテンツ