arrayにobjectでアクセスするラッパー
ここ数ヶ月PHPばかり書いてるのですが、$ary['foo']よりも$obj->fooの方が書きやすいよなぁってのと、未定義のフィールドにアクセスした時にエラーになるようなのが欲しいってのでこんなのを考えてみた。
案1
<?php class Accessor Implements IteratorAggregate { private $ary; function __construct(&$ary) { $this->ary =& $ary; } function __get($name) { if (isset($this->ary[$name])) { return $this->ary[$name]; } throw new Exception(); } function __set($name, $val) { $this->ary[$name] = $val; } function getIterator() { return new ArrayIterator($this->ary); } } $ary= array( 'One' => 1, ); $obj = new Accessor($ary); $ary['two'] = 2; $obj->three = 3; foreach ($obj as $k => $v) { echo "$k = $v \n"; } var_dump($obj); 実行 ---------------- One = 1 two = 2 three = 3 object(Accessor)#1 (1) { ["ary:private"]=> &array(3) { ["One"]=> int(1) ["two"]=> int(2) ["three"]=> int(3) } }
これは既存のarrayにバイントする感じ。元のarrayの参照を持ってるので、元が変更されればオブジェクト側も変更されます。欠点は元arrayが必要なので、new Accessor(array(...));みたいな書き方はできません。
そうじゃなくってインスタンス生成時にコピーしちゃうのならもっと単純。
案2
<?php class Accessor { function __construct($ary) { foreach ($ary as $k => $v) { $this->$k = $v; } } function __get($name) { if (isset($this->$name)) { return $this->name; } throw new Exception(); } function __set($name, $val) { $this->$name = $val; } } $obj = new Accessor(array('One' => 1)); $obj->two = 2; foreach ($obj as $k => $v) { echo "$k = $v \n"; } var_dump($obj); 実行 ---------------- One = 1 two = 2 object(Accessor)#1 (2) { ["One"]=> int(1) ["two"]=> int(2) }
__set()を定義しなければImmutableにできるなぁとかネストした構造の場合どうしようとか。欠点として、identに使えないキーを扱うことができないってのがありますが。