PHPのクラスと連想配列を相互変換する事を考えた。PHPのオブジェクをを連想配列にしたいと思った。
クラスと、配列のアクセスの違い
クラス変数の場合
$obj->user->email->domain
連想配列の場合
$obj["user"]["email"]["domain"]
となる。どっちが読みやすいか、どっちを使うべき?とか不毛な議論に巻き込まれたくないので、両方を使えるようにしたい。
get_object_vars の利用
可視変数がすべてペアでとれる。
<?php class Foo{ public $a= 1; public $b= 2; private $x = 0; } $foo = new Foo(); get_object_vars($foo);
array(1) { ["a"]=> int(1) ["b"]=> int(2) }
これで出来る気がするけど、変数をコピーするタイミングが制御できない。
コピーしてからオブジェクト変わったらどうする
コピーしてからオブジェクトに変更があったら面倒ですよね。
そうか、オブジェクトを配列としてアクセスできるようにしたいと思います。
SPLで ArrayのInterfaceを実装することにした。
ArrayObjectを実装するか?
http://jp.php.net/manual/en/class.arrayobject.php
ただのIteratorであれば
http://jp.php.net/manual/en/class.emptyiterator.php
ArrayIteratorを実装するか
http://jp.php.net/manual/en/class.arrayiterator.php
ArrayAccessを実装するか?
http://jp.php.net/manual/en/class.arrayaccess.php
などの方法があげられる。
ArrayAccessを使うのが一番楽だった。
ArrayAccessを実装して、phpのオブジェクトを配列っぽく見せるのが面白そうだった。
<?php ArrayAccess { /* Methods */ abstract public boolean offsetExists ( mixed $offset ) abstract public mixed offsetGet ( mixed $offset ) abstract public void offsetSet ( mixed $offset , mixed $value ) abstract public void offsetUnset ( mixed $offset ) }
ArrayAccessを implemtns したクラスは配列とおなじになる。
実用例:PDOで取り出せるテーブルクラスに配列機能つけた。
<?pphp /** * DBのレコード配列に、機能を追加するクラス */ class TableRecord implements ArrayAccess //クラスだけど配列として扱えるように { public $fields; public $field_names; public $updated_fields=array(); public $table_class = null; public function __construct($assoc_array=null, & $table_class_instance=null){ if($assoc_array && is_array($assoc_array)==false){throw new Exception("引数には連想配列を入れる。");} $this->fields = is_array($assoc_array)? $assoc_array:array(); $this->table_class = ($table_class_instance)? $table_class_instance :null; } public function offsetExists ( $k ){ return isset($this->fields[$k]); } public function offsetGet ( $k ){ if( $k && isset($this->fields[$k]) ){ return $this->fields[$k]; }else{ return null; } } public function offsetSet ( $k,$v ){ if( $k==null || !in_array($k, $this->field_names ) ) { throw new Exception("保存できるのはテーブルカラム名だけです"); } if( isset($this->fields[$k]) && $this->fields[$k] == $v ){ return; //変更なしは何もしない。 } if( array_key_exists($k,$this->fields ) ) { array_push($this->updated_fields,$k);//更新の時は、更新であることをメモする。 } $this->fields[$k] = $v; } public function offsetUnset($k){ unset($this->fields[$k]); } public function to_array(){ return array_diff($this->fields, array("")); } public function dirty(){ return sizeof($this->updated_fields) > 0; } public function to_array_only_updated_filed(){ if(!$this->dirty()){return false;} $a = $this->updated_fields; $a[] = "id"; $a = array_flip($a); foreach($a as $k=>$v){ $a[$k] = $this->fields[$k]; } return $a; } public function table_class_name(){ return str_replace("Record","",get_class($this->table_class)); } public function save(){ $table = $this->table_class; if($this->dirty()){ return $table->update($this); }else{ $id= $table->insert($this); if(!$this["id"]){ $obj = $table->find($id); $assoc_array = $obj->fields; $this->fields = $assoc_array; } return $id; } } public function delete($force=false){ if(!$this["id"]){return;} $this->table_class->delete($this["id"],$force); unset($this->fields); } }