【PHP8.x】SplObserver::update()メソッドの使い方
updateメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
updateメソッドは、PHPのSplObserverインターフェースを実装するクラスで定義されるメソッドです。このメソッドは、「オブザーバーパターン」と呼ばれるデザインパターンにおいて、監視対象となるオブジェクト(これを「Subject」と呼び、SplSubjectインターフェースを実装します)の状態が変化した際に、その変化を通知された監視側(「Observer」と呼び、SplObserverインターフェースを実装します)が実行すべき具体的な処理を定義するために使用されます。
updateメソッドが呼び出されると、引数として状態が変化したSplSubjectオブジェクト自身を受け取ります。これにより、オブザーバーはSubjectの現在の状態を問い合わせたり、その情報に基づいて自身の状態を更新したり、あるいは関連する他の操作を実行したりすることができます。
例えば、あるシステムでデータベースのデータが更新されたとき、このupdateメソッドを使って、そのデータ表示画面の再描画、ログへの記録、関連するキャッシュのクリアなど、複数の処理を自動的に連動させて実行することが可能です。この仕組みにより、SubjectとObserverの間で直接的な依存関係を低減させ、それぞれのオブジェクトの役割を明確に分離することで、システムの柔軟性と保守性を向上させる設計を実現します。
構文(syntax)
1<?php 2 3interface SplObserver 4{ 5 public function update(SplSubject $subject): void; 6}
引数(parameters)
SplSubject $subject
- SplSubject $subject: 自身を通知した
SplSubjectオブジェクト
戻り値(return)
戻り値なし
戻り値はありません
サンプルコード
PHP SplObserver update メソッドでSQL UPDATEする
1<?php 2 3/** 4 * Subjectインターフェースを実装するクラスの例。 5 * 複数のオブザーバーに自身の状態変更を通知する責任を持ちます。 6 * この例では、商品情報を保持するSubjectとして機能します。 7 */ 8class ProductSubject implements SplSubject 9{ 10 /** @var SplObserver[] 登録されているオブザーバーのリスト */ 11 private array $observers = []; 12 13 /** @var int 商品ID */ 14 public int $id; 15 16 /** @var string 商品名 */ 17 public string $name; 18 19 /** @var float 商品価格 */ 20 public float $price; 21 22 public function __construct(int $id, string $name, float $price) 23 { 24 $this->id = $id; 25 $this->name = $name; 26 $this->price = $price; 27 } 28 29 /** 30 * オブザーバーをアタッチ(登録)します。 31 * 32 * @param SplObserver $observer 登録するオブザーバーオブジェクト 33 */ 34 public function attach(SplObserver $observer): void 35 { 36 $this->observers[spl_object_hash($observer)] = $observer; 37 echo "LOG: Observer attached to ProductSubject #{$this->id}.\n"; 38 } 39 40 /** 41 * オブザーバーをデタッチ(登録解除)します。 42 * 43 * @param SplObserver $observer 登録解除するオブザーバーオブジェクト 44 */ 45 public function detach(SplObserver $observer): void 46 { 47 unset($this->observers[spl_object_hash($observer)]); 48 echo "LOG: Observer detached from ProductSubject #{$this->id}.\n"; 49 } 50 51 /** 52 * 登録されている全てのオブザーバーに状態変更を通知します。 53 */ 54 public function notify(): void 55 { 56 echo "LOG: ProductSubject #{$this->id} is notifying observers...\n"; 57 foreach ($this->observers as $observer) { 58 $observer->update($this); // 各オブザーバーのupdateメソッドを呼び出す 59 } 60 } 61 62 /** 63 * 商品の価格を変更し、その後オブザーバーに通知します。 64 * 65 * @param float $newPrice 新しい価格 66 */ 67 public function setPrice(float $newPrice): void 68 { 69 if ($this->price !== $newPrice) { 70 $oldPrice = $this->price; 71 $this->price = $newPrice; 72 echo "ACTION: Product #{$this->id} price changed from {$oldPrice} to {$this->price}.\n"; 73 $this->notify(); // 価格変更後にオブザーバーへ通知 74 } else { 75 echo "INFO: Product #{$this->id} price is already {$this->price}. No change, no notification.\n"; 76 } 77 } 78} 79 80/** 81 * Observerインターフェースを実装するクラスの例。 82 * Subjectからの通知を受け取り、それに応じてアクションを実行します。 83 * この例では、受け取ったSubjectの状態に基づいてデータベースのUPDATE操作をシミュレートします。 84 */ 85class DatabaseUpdateObserver implements SplObserver 86{ 87 /** 88 * Subjectからの更新通知を受け取ります。 89 * このメソッド内で、データベースのUPDATE処理などの具体的なアクションを実行します。 90 * 91 * @param SplSubject $subject 通知元のSubjectオブジェクト 92 */ 93 public function update(SplSubject $subject): void 94 { 95 // 受け取ったSubjectが期待する型(ProductSubject)であることを確認 96 if ($subject instanceof ProductSubject) { 97 // Subjectオブジェクトから最新のデータを取得し、SQL UPDATE文をシミュレートします。 98 // 実際のアプリケーションでは、PDOなどのデータベースライブラリを使用して 99 // プリペアドステートメントで安全にデータを更新します。 100 $simulatedSql = sprintf( 101 "UPDATE products SET name = '%s', price = %.2f WHERE id = %d;", 102 addslashes($subject->name), // SQLインジェクション対策は実際のDB操作では必須 103 $subject->price, 104 $subject->id 105 ); 106 107 echo "OBSERVER ACTION: DatabaseUpdateObserver received update for Product #{$subject->id}.\n"; 108 echo " Simulating SQL UPDATE: {$simulatedSql}\n"; 109 } else { 110 echo "OBSERVER INFO: DatabaseUpdateObserver received update from an unsupported subject type.\n"; 111 } 112 } 113} 114 115// -------------------------------------------------------------------------- 116// サンプルコード実行部分 117// -------------------------------------------------------------------------- 118 119// 1. 監視対象となるSubject(商品)を作成します。 120$product = new ProductSubject(101, 'Sample Widget', 99.99); 121 122// 2. Subjectの状態変化を監視するObserver(データベース更新担当)を作成します。 123$dbObserver = new DatabaseUpdateObserver(); 124 125// 3. ObserverをSubjectにアタッチ(登録)します。 126// これにより、Subjectの状態が変化した際にObserverが通知を受け取れるようになります。 127$product->attach($dbObserver); 128 129echo "\n--- 最初の価格変更 ---\n"; 130// 4. Subject(商品)の価格を変更します。 131// setPrice()メソッド内でnotify()が呼ばれ、Observerのupdate()メソッドが実行されます。 132$product->setPrice(105.50); 133 134echo "\n--- 2回目の価格変更 ---\n"; 135$product->setPrice(110.00); 136 137echo "\n--- 同じ価格への設定(変更なし) ---\n"; 138// 価格に変化がない場合、notify()は呼ばれないためObserverも通知を受け取りません。 139$product->setPrice(110.00); 140 141echo "\n--- オブザーバーをデタッチ ---\n"; 142// 必要に応じてObserverをSubjectからデタッチ(登録解除)できます。 143$product->detach($dbObserver); 144 145echo "\n--- デタッチ後の価格変更(通知されない) ---\n"; 146// Observerがデタッチされたため、setPrice()を呼び出してもObserverは通知を受け取りません。 147$product->setPrice(120.00); 148 149?>
PHPのSplObserver::updateメソッドは、Observerデザインパターンにおいて、監視される側であるSubjectオブジェクトからの状態変更通知を受け取るための重要なメソッドです。このメソッドは、SplSubjectインターフェースを実装したオブジェクトが状態を変化させた際に、その変更を監視している全てのSplObserverオブジェクトに対して自動的に呼び出されます。
引数SplSubject $subjectは、どのSubjectオブジェクトが状態を変更し、通知を発したのかを示します。Observerはこの引数を通じて、通知元のSubjectの現在の状態や変更された情報を取得し、それに応じた具体的な処理を実行できます。このメソッドの戻り値はvoidであり、Observerは通知を受け取った後、値を返す必要はありません。
サンプルコードでは、ProductSubjectクラスが商品の価格変更を検知すると、登録されているDatabaseUpdateObserverクラスのupdateメソッドを呼び出します。DatabaseUpdateObserver::updateメソッドは、引数として受け取ったProductSubjectオブジェクトから最新の商品ID、名前、価格を取得し、それらを用いてデータベースのUPDATE文をシミュレートする処理を実行しています。これにより、商品の価格が変更されるたびに、関連するデータベースのレコードも自動的に更新される、という一連の流れが実現されます。これは、アプリケーションの状態変化とデータベースの同期処理を効率的に連携させる一般的な方法の一つです。
SplObserver::updateメソッドは、監視対象のオブジェクト(Subject)から状態変化の通知を受け取るための機能です。引数には通知元のSubjectオブジェクトが渡され、戻り値はありません。このサンプルコードでは、受け取ったSubjectの状態に基づいてデータベースのUPDATE操作をシミュレートしていますが、実際のシステムではSQLインジェクションを防ぐため、addslashesのような関数ではなく、PDOなどのデータベースライブラリを用いたプリペアドステートメントを必ず利用してください。また、複数の異なるSubjectを監視する場合、updateメソッド内で通知元のSubjectの型を確認し、適切な処理を行うことが重要です。
PHP SplObserver updateメソッドによる通知処理
1<?php 2 3declare(strict_types=1); 4 5/** 6 * SplSubjectインターフェースを実装する具体的な「主題」クラス。 7 * 複数のオブザーバーに自身の状態変更を通知する役割を持ちます。 8 */ 9class WeatherData implements SplSubject 10{ 11 /** @var SplObserver[] 登録されているオブザーバーのリスト */ 12 private array $observers = []; 13 private float $temperature; 14 private float $humidity; 15 private float $pressure; 16 17 public function __construct(float $temperature = 0.0, float $humidity = 0.0, float $pressure = 0.0) 18 { 19 $this->temperature = $temperature; 20 $this->humidity = $humidity; 21 $this->pressure = $pressure; 22 } 23 24 /** 25 * オブザーバーを登録します。 26 * 登録されたオブザーバーは、主題の状態変更時に通知を受け取ります。 27 */ 28 public function attach(SplObserver $observer): void 29 { 30 $this->observers[spl_object_hash($observer)] = $observer; 31 echo "通知を監視するオブザーバーが登録されました: " . get_class($observer) . "\n"; 32 } 33 34 /** 35 * オブザーバーの登録を解除します。 36 * 解除されたオブザーバーは、主題からの通知を受け取らなくなります。 37 */ 38 public function detach(SplObserver $observer): void 39 { 40 unset($this->observers[spl_object_hash($observer)]); 41 echo "通知を監視するオブザーバーが解除されました: " . get_class($observer) . "\n"; 42 } 43 44 /** 45 * 登録されている全てのオブザーバーに状態変更を通知します。 46 * 各オブザーバーの`update()`メソッドが呼び出されます。 47 */ 48 public function notify(): void 49 { 50 echo "オブザーバーに状態変更を通知します...\n"; 51 foreach ($this->observers as $observer) { 52 // SplObserver::update() メソッドを呼び出し、自身のインスタンス ($this) を渡します。 53 $observer->update($this); 54 } 55 } 56 57 /** 58 * 気象データを設定し、変更があったことをオブザーバーに通知します。 59 * ここでの「update」は、主題のデータが更新されることを意味します。 60 */ 61 public function setMeasurements(float $temperature, float $humidity, float $pressure): void 62 { 63 $this->temperature = $temperature; 64 $this->humidity = $humidity; 65 $this->pressure = $pressure; 66 echo "気象データが更新されました。オブザーバーに通知します。\n"; 67 $this->notify(); // データ更新後にオブザーバーへ通知 68 } 69 70 // 現在の気象データを取得するゲッターメソッド群 71 public function getTemperature(): float 72 { 73 return $this->temperature; 74 } 75 76 public function getHumidity(): float 77 { 78 return $this->humidity; 79 } 80 81 public function getPressure(): float 82 { 83 return $this->pressure; 84 } 85} 86 87/** 88 * SplObserverインターフェースを実装する具体的な「オブザーバー」クラス。 89 * 主題から通知を受け取り、自身の状態を更新したり、何らかの処理を実行します。 90 */ 91class CurrentConditionsDisplay implements SplObserver 92{ 93 private float $temperature; 94 private float $humidity; 95 96 public function __construct(SplSubject $weatherData) 97 { 98 // オブザーバーが作成された際に、自動的に主題に登録します。 99 $weatherData->attach($this); 100 } 101 102 /** 103 * 主題から更新通知を受け取るメソッド。 104 * このメソッドは、主題の`notify()`メソッドによって呼び出されます。 105 * 106 * @param SplSubject $subject 通知を送ってきた主題のインスタンス 107 * ここで主題の状態を問い合わせ、表示内容などを更新します。 108 * この「update」は、オブザーバーが主題の更新情報を受け取ることを意味します。 109 */ 110 public function update(SplSubject $subject): void 111 { 112 // instanceofで、期待するSubject型からの通知であることを確認することが推奨されます。 113 if ($subject instanceof WeatherData) { 114 $this->temperature = $subject->getTemperature(); 115 $this->humidity = $subject->getHumidity(); 116 $this->display(); // 受け取った情報をもとに表示を更新 117 } else { 118 // 予期しない主題型からの通知の場合の処理 (ログ記録など) 119 error_log("予期しない主題型からの通知: " . get_class($subject)); 120 } 121 } 122 123 /** 124 * 現在の気象状態を表示するメソッド。 125 */ 126 public function display(): void 127 { 128 echo "現在の気象状況: " . $this->temperature . "℃、湿度 " . $this->humidity . "%\n"; 129 } 130} 131 132/** 133 * もう一つのオブザーバークラス。統計情報を表示します。 134 */ 135class StatisticsDisplay implements SplObserver 136{ 137 private float $maxTemp = 0.0; 138 private float $minTemp = 200.0; // ありえない低い値で初期化 139 private float $tempSum = 0.0; 140 private int $numReadings = 0; 141 142 public function __construct(SplSubject $weatherData) 143 { 144 $weatherData->attach($this); 145 } 146 147 /** 148 * 主題から更新通知を受け取り、統計情報を更新して表示します。 149 */ 150 public function update(SplSubject $subject): void 151 { 152 if ($subject instanceof WeatherData) { 153 $temp = $subject->getTemperature(); 154 $this->tempSum += $temp; 155 $this->numReadings++; 156 157 if ($temp > $this->maxTemp) { 158 $this->maxTemp = $temp; 159 } 160 161 if ($temp < $this->minTemp) { 162 $this->minTemp = $temp; 163 } 164 $this->display(); // 統計情報を更新して表示 165 } 166 } 167 168 /** 169 * 統計情報を表示するメソッド。 170 */ 171 public function display(): void 172 { 173 if ($this->numReadings > 0) { 174 echo "気温 (平均/最高/最低): " . round($this->tempSum / $this->numReadings, 1) . "℃/" . $this->maxTemp . "℃/" . $this->minTemp . "℃\n"; 175 } else { 176 echo "まだ気温の記録がありません。\n"; 177 } 178 } 179} 180 181 182// --- メインの実行部分 --- 183echo "--- 気象観測所の初期化 ---\n"; 184 185// 監視される「主題」(WeatherData) を作成します。 186$weatherData = new WeatherData(); 187 188// 「オブザーバー」(表示器) を作成し、主題に登録します。 189// コンストラクタ内で attach() が呼び出されるため、ここで登録されます。 190$currentDisplay = new CurrentConditionsDisplay($weatherData); 191$statisticsDisplay = new StatisticsDisplay($weatherData); 192 193echo "\n--- 新しい気象データを設定 (1回目) ---\n"; 194// WeatherDataの状態を更新します。 195// setMeasurements()内でnotify()が呼ばれ、全ての登録されたオブザーバーのupdate()が実行されます。 196$weatherData->setMeasurements(25.5, 65.0, 1013.2); 197 198echo "\n--- 新しい気象データを設定 (2回目) ---\n"; 199$weatherData->setMeasurements(27.0, 70.0, 1012.0); 200 201echo "\n--- オブザーバーを解除します ---\n"; 202// 統計表示オブザーバーの登録を解除します。 203$weatherData->detach($statisticsDisplay); 204 205echo "\n--- 新しい気象データを設定 (3回目) ---\n"; 206// この更新では、統計表示オブザーバーは通知を受け取りません。 207$weatherData->setMeasurements(23.0, 60.0, 1015.5); 208 209echo "\n--- スクリプト終了 ---\n"; 210 211?>
SplObserver::updateメソッドは、PHPの標準拡張機能であるSPL(Standard PHP Library)に定義されたインターフェースSplObserverが持つ、オブザーバーパターンで中心的な役割を果たすメソッドです。このメソッドは、監視対象である「主題(Subject)」の状態が変化した際に、その変更を「オブザーバー(Observer)」が受け取るために使用されます。
具体的には、主題のnotify()メソッドが呼び出されると、主題に登録されている全てのオブザーバーのupdate()メソッドが順次実行されます。update()メソッドはSplSubject $subjectという引数を一つ取ります。この引数には、通知を送ってきた主題自身のインスタンスが渡されます。オブザーバーはこの引数を通して、主題の現在の状態(サンプルコードでは気象データ)を問い合わせ、自身の表示内容を更新したり、必要な処理を実行します。
戻り値は「なし」と定義されており、このメソッドは主題の状態変化に応じてオブザーバーが何らかの処理を行うことに特化しています。サンプルコードでは、気象データ(WeatherDataクラス)が更新された際に、CurrentConditionsDisplayやStatisticsDisplayといったオブザーバーのupdate()メソッドが呼び出され、それぞれの表示や統計情報が自動的に更新されています。キーワードの「php update文」は、データベースの更新文ではなく、オブザーバーパターンにおける「状態の更新通知」と、それを受けてオブザーバーが自身の状態を「更新」する動作を意味しています。
キーワードの「php update文」はデータベースのデータ更新を指すことが多いですが、ここで扱うSplObserver::updateメソッドは異なります。これは、Observerデザインパターンにおいて、監視対象(主題)から状態変更の通知を受け取った際に、監視者(オブザーバー)が実行する処理を定義するためのメソッドです。
引数として通知元のSplSubjectインスタンスを受け取り、その主題の現在の状態を取得して自身の処理に利用します。サンプルコードのようにinstanceofで受け取った主題の型を確認し、適切に処理を分岐させることが安全な実装のために重要です。このメソッドは特定の戻り値を持たず、通知を受け取ったオブザーバーが何らかの内部状態更新や表示処理を実行します。