配列+プロパティ両方のアクセスができるクラスを作ってみました
..
オブジェクトをSmartyにバインドしたとき、
{$hoge->bar}
と書くのが面倒に感じたり、Smartyに怒られてから「あれ、これオブジェクトだったっけ、それとも配列だったっけ?」とソースコードを読み返したりしたことはありませんか?
そんなものぐさなPHPerの方のために、配列のようにも、オブジェクトのようにもアクセスできるクラスを作ってみました。
<?php
class DualObject implements Iterator, ArrayAccess
{
private $__data;
private $__field_list;
/*
* コンストラクタ
*/
public function __construct()
{
$this->__data = array();
// __で始まるフィールドは除外
$this->__field_list = array();
$vars = get_object_vars($this);
foreach( $vars as $key => $value ){
if ( strpos($key,'__')===FALSE ){
$this->__field_list[] = $key;
}
}
}
/*
* ArrayAccessインタフェース:offsetGetの実装
*/
public function offsetGet($offset)
{
if ( $offset instanceof Object ){
$offset = $offset->s();
}
if ( property_exists($this, $offset) ) {
return $this->$offset;
}
return isset($this->__data[$offset]) ? $this->__data[$offset] : NULL;
}
/*
* ArrayAccessインタフェース:offsetSetの実装
*/
public function offsetSet($offset, $value)
{
if ( $offset instanceof Object ){
$offset = $offset->s();
}
if ( property_exists( $this, $offset ) ){
$this->$offset = $value;
}
else {
$this->__data[$offset] = $value;
if ( FALSE === array_search( $offset, $this->__field_list ) ){
$this->__field_list[] = $offset;
}
}
}
/*
* ArrayAccessインタフェース:offsetExistsの実装
*/
public function offsetExists($offset)
{
if ( $offset instanceof String ){
$offset = $offset->s();
}
return FALSE !== array_search( $offset, $this->__field_list );
}
/*
* ArrayAccessインタフェース:offsetUnsetの実装
*/
public function offsetUnset($offset)
{
if ( $offset instanceof Object ){
$offset = $offset->s();
}
if ( property_exists( $this, $offset ) ){
unset($this->$offset);
}
if ( isset($this->__data[$offset]) ){
unset($this->__data[$offset]);
}
$pos = array_search( $offset, $this->__field_list );
if ( FALSE !== $pos ){
unset($this->__field_list[$pos]);
}
}
/*
* Iteratorインタフェース:rewidの実装
*/
public function rewind()
{
reset($this->__field_list);
}
/*
* Iteratorインタフェース:currentの実装
*/
public function current()
{
$offset = current($this->__field_list);
return $this->offsetGet($offset);
}
/*
* Iteratorインタフェース:keyの実装
*/
public function key()
{
return current($this->__field_list);
}
/*
* Iteratorインタフェース:nextの実装
*/
public function next()
{
return next($this->__field_list);
}
/*
* Iteratorインタフェース:validの実装
*/
public function valid()
{
$offset = current($this->__field_list);
return $this->offsetExists($offset);
}
/*
* プロパティアクセス(取得)
*/
public function __get( $name )
{
return $this->offsetGet($name);
}
/*
* プロパティアクセス(設定)
*/
public function __set( $name, $value )
{
$this->offsetSet($name, $value);
}
/*
* dump
*/
public function dump()
{
print "--------------[ dump start ]--------------" . nl2br(PHP_EOL);
foreach( $this as $key => $value ){
print "[$key]$value" . nl2br(PHP_EOL);
}
print "--------------[ dump end ]--------------" . nl2br(PHP_EOL);
}
}
使い方はこんな感じ。
<?php
require_once 'dual_object.php';
class MyObject extends DualObject
{
public $foo;
}
$o = new MyObject();
$o->dump();
// 通常のpublicフィールドアクセス
$o->foo = 'Boo!';
// 配列でもアクセスできる
$o['foo'] = 'Bazz!';
// 定義していなくてもアクセスできる
$o['bar'] = 'Woot!';
$o->dump();
$o->bar = 'Mzzzz';
$o->dump();
// 読み取りもできる
echo 'o:foo=' . $o['foo'] . nl2br(PHP_EOL);
echo 'o:foo=' . $o->foo . nl2br(PHP_EOL);
echo 'o:bar=' . $o['bar'] . nl2br(PHP_EOL);
echo 'o:bar=' . $o->bar . nl2br(PHP_EOL);
$o->dump();
出力結果。
--------------[ dump start ]-------------- [foo] --------------[ dump end ]-------------- --------------[ dump start ]-------------- [foo]Bazz! [bar]Woot! --------------[ dump end ]-------------- --------------[ dump start ]-------------- [foo]Bazz! [bar]Mzzzz --------------[ dump end ]-------------- o:foo=Bazz! o:foo=Bazz! o:bar=Mzzzz o:bar=Mzzzz --------------[ dump start ]-------------- [foo]Bazz! [bar]Mzzzz --------------[ dump end ]--------------
DualObjectを継承して使うとPHP側でプロパティアクセスし、Smarty側では配列としてアクセスするといった使い方ができます。
ちなみにIteratorも実装しているので、Dual#dump()を見ても分かるように、
$obj = new MyObject();
$obj->foo = 'bar';
...
foreach( $obj as $key => $value ){
...
}
のように記述してオブジェクトのプロパティと値を列挙することができます。