PHPでObserverパターンで実装してみた

## はじめに
Rx****をやってみたくなり、基本となる（？）ObserverパターンをPHPにて実装して見た。  
前提条件として、  

* PHP5.xでも動く
* ある程度実用的

とします。

## Observerパターンとは
あちこちのページで書かれているので細かくは省略します。  
状態変化に応じた処理を記述するときに有効らしいです。

## 今回のサンプル
タイトルと内容を入力とするフォームからPOSTされた時に、

* メール送信
* ログ出力

を行う処理を記述するのにObserverパターンで記述してみます。  
(この程度でObserverパターンを使うのもって話はあるが。。。（汗）  
クラス図はこんな感じ  
![class.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125345/d167cdaa-bf42-e974-bb86-6e42e010f81a.png)


ソース構成はこんな感じ  
┣━ classes  
┃　　┣━ ifSubject.php     
┃　　┣━ clsFormdat.php  
┃　　┣━ ifObserver.php  
┃　　┣━ clsLogObserver.php  
┃　　┣━ clsSndMailObserver.php  
┗━ smp1.php 
## 実装
### インターフェイス
まずSubjectのインターフェイスは

```php:ifSubject.php
<?php
/**
 * Subjectインターフェイス
 */
interface ifSubject {
  public function addObserver($obj);      // Observerインスタンス追加
  public function removeObserver($obj);   // Observerインスタンス削除
  public function notify();               // Observerへの通知
}
```

次にObserverインターフェイスは

```php:ifObserver.php 
<?php
/**
 * Observerインターフェイス
 */
interface ifObserver {
  public function execute($obj);   // Subjectからの通知受信
}
```

### Subjectクラス

```php:clsFormdat.php
<?php
require_once(dirname(__FILE__)."/ifSubject.php");

/**
 * フォームデータクラス
 */
class clsFormdat implements ifSubject {
  private $title = ""; /// タイトル
  private $memo = "";  /// 内容

  private $arrObservers = NULL;  /// Observerインスタンス配列

  public function __construct() {
    $this->arrObservers = array();
  }

  /**
   * タイトルと内容のセッター
   */
  public function __set($itm,$val) {
    if ( $itm == "title" ) $this->title = $val;
    if ( $itm == "memo" ) $this->memo = $val;
  }

  /**
   * タイトルと内容のゲッター
   */
  public function __get($itm) {
    if ( $itm == "title" ) return $this->title;
    if ( $itm == "memo" ) return $this->memo;
  }

  /**
   * Observerインスタンス追加
   * @param $obj Observerインスタンス
   */
  public function addObserver($obj) {
    $this->arrObservers[get_class($obj)] = $obj;
  }

  /**
   * Observerインスタンス削除
   * @param $obj Observerインスタンス
   */
  public function removeObserver($obj) {
    unset($this->arrObservers[get_class($obj)]);
  }

  /**
   * Observerへの通知
   */
  public function notify() {
    foreach($this->arrObservers as $obj) {
      $obj->execute($this);
    }
  }
}
```

### Observerクラス

まず、ログ出力クラスは  

```php:clsLogObserver.php
<?php
require_once(dirname(__FILE__)."/ifObserver.php");

/**
 * ログ出力クラス
 */
class clsLogObserver implements ifObserver {
  public function __construct() {

  }

  /**
   * 通知受信
   * @param $obj clsFormdatのインスタンス
   */
  public function execute($obj) {
    $buf  = "title:".$obj->title;
    $buf .= "\n".$obj->memo;
    $fp = fopen("/var/www/Log/app.log","a");
    fwrite($fp,$buf);
    fclose($fp);
  }
}
```

次にメール送信クラス  

```php:clsSndMailObserver.php
<?php
require_once(dirname(__FILE__)."/ifObserver.php");

/**
 * メール送信クラス
 */
class clsSndMailObserver implements ifObserver {
  public function __construct() {

  }

  /**
   * 通知受信
   * @param $obj clsFormdatのインスタンス
   */
  public function execute($obj) {
    mail("tst@cde.jp",$obj->title,$obj->memo,"From: adm@cde.jp");
  }
}
```

## 動かしてみる
次のコードで実際に動かしてみる。

```php:smp1.php
<?php
require_once(dirname(__FILE__)."/classes/clsLogObserver.php");
require_once(dirname(__FILE__)."/classes/clsSndMailObserver.php");
require_once(dirname(__FILE__)."/classes/clsFormdat.php");

$obj = new clsFormdat();

$objLog = new clsLogObserver();
$obj->addObserver($objLog);

$objMail = new clsSndMailObserver();
$obj->addObserver($objMail);

$obj->title = "サンプルタイトル";
$obj->memo  = "サンプルサンプル\nサンプル";
$obj->notify();
```

確かに実装はシンプルになったようだ。