PHP:クラスのプロパティを簡単にImmutable(不変化)


Traitでgetterとsetter兼用の処理を定義

  • 第1引数でプロパティの参照を受け取る。(setterの機能を実装するため)
  • 第2引数で設定値を受け取る。
  • 第2引数の設定値がnullの場合、getterとして振る舞う。
  • 設定値があり、プロパティが設定済み(nullではない)場合、例外発生。<- 不変のため。
immutableTrait.php

trait Immutable
{
    //クラスのプロパティは参照を受け取る。
    protected static function access(&$property, $value)
    {
        //設定値がnullの場合、未設定としてプロパティ値を返却(getterとして振る舞う)
        if (is_null($value)) {
            return $property;
        }
        //プロパティ値が設定済みの場合、変更をさせないため、例外発生。
        if (!is_null($property)) {
            //ImmutableExceptionとか作ってあてるのがいいかも。
            throw new \Exception('already value exists. this object is immutable');
        }
        //プロパティに値を設定
        $property = $value;
        return $value;

    }
}

モデルクラスにTraitを実装

  • traitのメソッド、accessを呼び出す。
  • 第1引数は、プロパティ値
  • 第2引数は、設定値。デフォルトをnullとする。
Model.php

class Model {
   // トレイト実装
   use Immutable;

   private $_name = null;

   //引数には、デフォルト値を必ず設定する(getterとして使うため)
   public function name($value = null) 
   {
      //プロパティを第1引数(参照渡し)
      return self::access($this->_name, $value);
   }

}

使用方法

using.php

$m = new Model();

//setter
$m->name('value1');

//getter
echo $m->name();

//例外発生
$m->name('value2');


問題点

  • プロパティがオブジェクトや配列の場合、取得後に変更ができる。文法的にfreezeができればいいのだが。