コントローラからビューにオブジェクトを渡す場合、場合によってはビュー側で操作されたくないケースもあるかと思います。
そのような際に役立つかもしれないのが 不変(Immutable)オブジェクト。普通はImmutableな別クラスとして定義するのだと思いますが、それだと実装が複雑になってしまうので、変更操作(プロパティへのsetter、または変更メソッド)に対して例外を投げるクラスを作成してみました。
<?php
class Immutable{
private $obj;
private $setters_pattern;
public function __construct( $obj, $setters_pattern = '/set([0-9a-zA-Z])*/' ){
$this->obj = $obj;
$this->setters_pattern = $setters_pattern;
}
private function checkSetters( $name ){
if ( is_string($this->setters_pattern) ){
return preg_match( $this->setters_pattern, $name );
}
else if ( is_array($this->setters_pattern) ){
foreach( $this->setters_pattern as $pattern ){
if ( preg_match( $pattern, $name ) ) return TRUE;
}
return FALSE;
}
return FALSE;
}
public function __get( $key ){
// 内包オブジェクトに対応するプロパティがある場合は取得
if ( property_exists( $this->obj, $key ) ){
return $this->obj->$key;
}
// えっ!?
throw new LogicException('えっ!?');
}
public function __set( $key, $value ){
// $foo->barでプロパティを設定した場合は例外をスロー
throw new Exception( "Immutableオブジェクトのため変更できません:$key" );
}
public function __call($name, $arguments){
// setters_patternにマッチしたメソッドは例外をスロー
if ( $this->checkSetters( $name ) ){
throw new Exception( "Immutableオブジェクトのため変更できません:$name" );
}
// 内包オブジェクトに対応するメソッドがある場合は実行
else if ( method_exists( $this->obj, $name ) ){
return call_user_func_array( array($this->obj,$name), $arguments );
}
// えっ!?
throw new LogicException('えっ!?'.$name);
}
}
// テストコード
class Hoge
{
public $foo = 'apple';
private $bar = 'banana';
/* getterメソッド */
public function getBar() { return $this->bar; }
/* setterメソッド */
public function setBar($new_val) { $this->bar = $new_val; }
/* setterではないがbarを変更するメソッド */
public function changeBar() { $this->bar = 'hoge'; }
}
// 内包されるオブジェクト
$hoge = new Hoge();
// Immutableオブジェクト
$setter_patterns = array(
'/set([0-9a-zA-Z])*/',
'/change([0-9a-zA-Z])*/',
);
$immutable_hoge = new Immutable( $hoge, $setter_patterns );
// プロパティ参照(getter)
print $immutable_hoge->foo . PHP_EOL; // apple
// メソッドで参照
print $immutable_hoge->getBar() . PHP_EOL; // banana
// 変更メソッドを呼び出す(setter)
try{
$immutable_hoge->setBar( 'melon' ); // 例外が発生
}
catch( Exception $e ){
print $e->getMessage() . PHP_EOL;
}
// 変更メソッドを呼び出す
try{
$immutable_hoge->changeBar(); // 例外が発生
}
catch( Exception $e ){
print $e->getMessage() . PHP_EOL;
}
// プロパティアクセスで設定もダメ
try{
$immutable_hoge->foo = 'pear'; // 例外が発生
}
catch( Exception $e ){
print $e->getMessage() . PHP_EOL;
}
// 元オブジェクト経由では普通に変更できる
$hoge->setBar( 'orange' );
print $immutable_hoge->getBar() . PHP_EOL; // orange
実行結果
apple
banana
Immutableオブジェクトのため変更できません:setBar
Immutableオブジェクトのため変更できません:changeBar
Immutableオブジェクトのため変更できません:foo
orange
使い方は、保護したいオブジェクトをコンストラクタに渡し、オプションで変更メソッドのパターンを文字列または配列で渡します。
// 保護したいオブジェクトを作成
$hoge = new Hoge();
// Immutableオブジェクトを作成
$immutable_hoge = new Immutable( $hoge, $setter_patterns );
パターンを省略した場合は"set"で始まるメソッドをすべて変更メソッドとみなします。
$setter_patternsにマッチするメソッドが呼び出されるか、プロパティへのsetが行われた場合、例外をスローします。