prefabolic

12345678910111213141516171819202122232425262728
HOME (Archive) > Category : Programming > phpにもC#のプロパティ, rubyのattr系のアレが欲しい
« ふぃずばず | ぼくとコンピュータ »

phpにもC#のプロパティ, rubyのattr系のアレが欲しい

投稿者:kalibora
投稿日時:2007-06-24 - 01:33:39
カテゴリー:Programming - トラックバック(DISALLOWED (TrackBack))-
最近phpをあまり追ってないのでよくわからないんだけども
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'
...
となってしまった。

今日はここで力尽きる。。


Comments

No comments yet

Add Comments

このアイテムは閉鎖されました。このアイテムへのコメントの追加、投票はできません。