phpにもC#のプロパティ, rubyのattr系のアレが欲しい
最近phpをあまり追ってないのでよくわからないんだけども
getter,setterのようなaccessorメソッドはphp5でも地道に書くしかないんだよね?
__get(), __set()は知っているのだけれども
ちょっと違うんだよなあ。
いや出来たとしても記述が冗長になり過ぎる。
やりたいことは
1. フィールドに直接アクセスしているように見える
2. でもメソッドを介しているので呼び出し側が意識しなくても呼び出される側で色々チェックが出来る
3. 明示的にフィールド名を宣言できる。というか宣言していないフィールド名を設定、参照することができなくしたい
4. getHoge(), setHoge()などとだらだら書きたくない
多分できなくはない、でもスマートに書ける気がしない。
簡単に書けるように基底クラスにめんどくさい実装をしてしまえと思って少し書いてみた。
これを実行すると
次はなんらかのドメインロジックを持ったアクセサを定義する場合、
子クラスにdummyのアクセサを実装して
親クラスにattr_[writer|reader]_user_funcなどと言うアクセサを登録するメソッドを実装して
親から子のアクセサをcall_user_funcしてみる。
まずは親クラスの実装(変更箇所のみ)
次に子クラスとメイン部分の実装
今回はfooの値を必ず小文字にしてびっくりマークを付けるように実装してみる
これで実行すると
のように思えるけど、実はこれダメダメで、
例えば以下のように2回fooをセットしてみると
foo2!!
と表示されて欲しいのだけれども、
今日はここで力尽きる。。
getter,setterのようなaccessorメソッドはphp5でも地道に書くしかないんだよね?
__get(), __set()は知っているのだけれども
ちょっと違うんだよなあ。
いや出来たとしても記述が冗長になり過ぎる。
やりたいことは
1. フィールドに直接アクセスしているように見える
2. でもメソッドを介しているので呼び出し側が意識しなくても呼び出される側で色々チェックが出来る
3. 明示的にフィールド名を宣言できる。というか宣言していないフィールド名を設定、参照することができなくしたい
4. getHoge(), setHoge()などとだらだら書きたくない
多分できなくはない、でもスマートに書ける気がしない。
簡単に書けるように基底クラスにめんどくさい実装をしてしまえと思って少し書いてみた。
#!/usr/local/bin/php
<?php
class AttrClass
{
private $attr_reader = array();
private $attr_writer = array();
private $attr = array();
protected function attr_accessor($attributes)
{
$add = $this->_my_flip($attributes);
$this->attr_reader = array_merge($this->attr_reader, $add);
$this->attr_writer = array_merge($this->attr_writer, $add);
}
protected function attr_reader($attributes)
{
$add = $this->_my_flip($attributes);
$this->attr_reader = array_merge($this->attr_reader, $add);
}
protected function attr_writer($attributes)
{
$add = $this->_my_flip($attributes);
$this->attr_writer = array_merge($this->attr_writer, $add);
}
private function _my_flip($mixed)
{
if (! is_array($mixed)) {
$mixed = array($mixed);
}
return array_flip($mixed);
}
private function __set($name, $value)
{
if (array_key_exists($name, $this->attr_writer)) {
$this->attr[$name] = $value;
} else {
throw new Exception("$name is undefined setter method");
}
}
private function __get($name)
{
if (array_key_exists($name, $this->attr_reader)) {
return $this->attr[$name];
} else {
throw new Exception("$name is undefined getter method");
}
}
}
class Klass extends AttrClass
{
function __construct($foo, $bar, $baz)
{
$this->attr_accessor(array('foo', 'bar', 'baz'));
$this->foo = $foo;
$this->bar = $bar;
$this->baz = $baz;
}
}
$kls = new Klass('FOO', 'BAR', 'BAZ');
echo $kls->foo . PHP_EOL;
echo $kls->bar . PHP_EOL;
echo $kls->baz . PHP_EOL;
$kls->bar = 'バー';
$kls->baz = 'バズ';
echo $kls->foo . PHP_EOL;
echo $kls->bar . PHP_EOL;
echo $kls->baz . PHP_EOL;
$kls->hoge = 'HOGE';
?>
メソッド名はrubyのぱくり。これを実行すると
FOO BAR BAZ FOO バー バズ Fatal error: Uncaught exception 'Exception' with message 'hoge is undefined setter method' ...ここまではOK。
次はなんらかのドメインロジックを持ったアクセサを定義する場合、
子クラスにdummyのアクセサを実装して
親クラスにattr_[writer|reader]_user_funcなどと言うアクセサを登録するメソッドを実装して
親から子のアクセサをcall_user_funcしてみる。
まずは親クラスの実装(変更箇所のみ)
protected function attr_reader_user_func($attribute, $class, $func)
{
$this->attr_reader[$attribute] = array($class, $func);
}
protected function attr_writer_user_func($attribute, $class, $func)
{
$this->attr_writer[$attribute] = array($class, $func);
}
...
private function __set($name, $value)
{
if (array_key_exists($name, $this->attr_writer)) {
if (is_callable($this->attr_writer[$name])) {
call_user_func($this->attr_writer[$name], $value);
} else {
$this->attr[$name] = $value;
}
} else {
throw new Exception("$name is undefined setter method");
}
}
private function __get($name)
{
if (array_key_exists($name, $this->attr_reader)) {
if (is_callable($this->attr_reader[$name])) {
return call_user_func($this->attr_reader[$name]);
} else {
return $this->attr[$name];
}
} else {
throw new Exception("$name is undefined getter method");
}
}
次に子クラスとメイン部分の実装
今回はfooの値を必ず小文字にしてびっくりマークを付けるように実装してみる
class Klass extends AttrClass
{
function __construct($foo, $bar, $baz)
{
$this->attr_accessor(array('foo', 'bar', 'baz'));
$this->attr_writer_user_func('foo', $this, 'set_foo');
$this->foo = $foo;
$this->bar = $bar;
$this->baz = $baz;
}
protected function set_foo($value)
{
$this->foo = strtolower($value) . '!!';
}
}
$kls = new Klass('FOO', 'BAR', 'BAZ');
echo $kls->foo . PHP_EOL;
echo $kls->bar . PHP_EOL;
echo $kls->baz . PHP_EOL;
$kls->bar = 'バー';
$kls->baz = 'バズ';
echo $kls->foo . PHP_EOL;
echo $kls->bar . PHP_EOL;
echo $kls->baz . PHP_EOL;
$kls->hoge = 'HOGE';
これで実行すると
foo!! BAR BAZ foo!! バー バズ Fatal error: Uncaught exception 'Exception' with message 'hoge is undefined setter method' ...成功!!!
のように思えるけど、実はこれダメダメで、
例えば以下のように2回fooをセットしてみると
$kls = new Klass('FOO', 'BAR', 'BAZ');
echo $kls->foo . PHP_EOL;
echo $kls->bar . PHP_EOL;
echo $kls->baz . PHP_EOL;
$kls->bar = 'バー';
$kls->baz = 'バズ';
echo $kls->foo . PHP_EOL;
echo $kls->bar . PHP_EOL;
echo $kls->baz . PHP_EOL;
$kls->foo = 'FOO2'; ←ここを追加
echo $kls->foo . PHP_EOL; ←ここを追加
$kls->hoge = 'HOGE';
追加した部分に関してはfoo2!!
と表示されて欲しいのだけれども、
foo!! BAR BAZ foo!! バー バズ FOO2 ←あーあ・・・ Fatal error: Uncaught exception 'Exception' with message 'hoge is undefined setter method' ...となってしまった。
今日はここで力尽きる。。