それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

var_exportの奥底を見る

var_exportと__set_state()の使い方がよくわからない。

マニュアル読んでもわからない。
細かく調べてみた

var_exportはPHPソースを返すので便利

var_dumpと違い、var_exportはソースコードを返してくれる。

<?php
#testA.php
$a = array("a","b","c");
$str = var_export($a,true);
echo $str;

これ実行するとソースコードが得られる。

>php testA.php
array (
  0 => 'a',
  1 => 'b',
  2 => 'c',
)
>

これは、便利だ。evalを組み合わせると配列をStringにして持ちまわせる。

Evalをするテスト

<?php
#testB.php
$a = array("a","b","c");
$str = var_export($a,true);
eval('$_='.$str).";";# ';'をつけておく
var_dump($_);

実行。

>php testB.php
array(3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
}
>

データベースの問い合わせ結果につかえる。データベースの問い合わせ結果が通常は連想配列なので、配列で渡さずともソースコードで持てる。多次元配列をデータベースに格納することにも応用できる。

オブジェクトのvar_exportは癖がある

じゃあクラスもSerializeせずに、var_exportでソースにしたいよね。
でもこれがなかなか苦労するんだ。

var_exportでオブジェクトをソースにすると、プロパティーだけが保存される

オブジェクトのVar_Export

<?php
#testC.php
class A {
 var $test1 ="1";
 public $test2 ="2";
 private $test3 ="3";
 function T(){return;}
};
$a = new A();
$str = var_export($a,true);
echo $str;

実行結果

>php testC.php
A::__set_state(array(
   'test1' => '1',
   'test2' => '2',
   'test3' => '3',
))
>

__set_stateってなんじゃ??

PHP5.1.0で追加されたMagicMethodだ。
マニュアルをよむとvar_export専用らしい

もとのオブジェクトに戻せるの??

たとえば、Classをこんな風にすると、もとにもどせる。
__set_stateはFactoryメソッドだと考えると理解が進む

__set_stateの利用例

<?php
#testD.php
class A {
 var $test1 ="1";
 public $test2 ="2";
 private $test3 ="3";
 function T(){return;}
 public static function __set_state($props){
  $a = new A();
  foreach($props as $key=>$val){
   //__setでなく可変変数でセットするのが楽
   $a->$key = $val;
  }
  return $a;
 }
};
$a = new A();
$str = var_export($a,true);
eval('$_='.$str.";");
echo var_dump($_);

実行結果

>php testD.php
object(A)#2 (3) {
  ["test1"]=>
  string(1) "1"
  ["test2"]=>
  string(1) "2"
  ["test3:private"]=>
  string(1) "3"
}
>

おおお、もどったよ!

__set_state()書くのがめんどくさくないか??


__set_stateを書かなければエラーで落ちる。どんなクラスでも上記の__set_stateをデフォルト実装していてほしいところ。__set_stateを自由にかけますよ!ってのがPHPの売り文句なんだろうか。

というわけで、var_exportを元に戻すfunctionを書いてみた

var_exportの逆を行うfunction

<?php
#var_exportを元に戻す関数
function un_var_export($str){
 $_buff = explode ( ":" , $str, 2 );
 $class_name = $_buff[0];
 $vars_str = explode("\n",$str);
 array_shift($vars_str);
 array_pop($vars_str);
 $func = create_function( null,'
  $arr = array('.implode($vars_str, "\n").');
     $obj = new '.$class_name.'();
     foreach( $arr as $key=>$val ){
   $obj->$key = $val;
     }
  return $obj;
 ' );
 $obj = $func();
 return $obj;
}

動作テスト

<?php
#testE.php
class A {
 var $test1 ="1";
 public $test2 ="2";
 private $test3 ="3";
 function T(){return;}
};
$a = new A();
$str = var_export($a,true).";"
echo var_dump(un_var_export($str));

エラーになった

>php testE.php

Fatal error: Cannot access private property A::$test3 in testE.php(72)
: runtime-created function on line 7

privateな変数にはアクセスできないって。いいアイディアだと思ったが無理なようだ。

改良して再度挑戦してみる

マニュアルを読んでみるとphp_classkitというモジュールに出会う。

改良したun_var_export関数

<?php
#var_exportを元に戻す関数
function un_var_export($str){
 @dl ( "php_classkit" );
 $_buff = explode ( ":" , $str, 2 );
 $class_name = $_buff[0];
 $obj = new $class_name();
 $vars_str = explode("\n",$str);
 array_shift($vars_str);
 array_pop($vars_str);
 classkit_method_add( $class_name, "__set_state", '$props','
  foreach($props as $key=>$val){
   $this->$key = $val;
  }');
 return $obj;
}

動作テスト

<?php
#testF.php
class A {
 var $test1 ="1";
 public $test2 ="2";
 private $test3 ="3";
 function T(){return;}
};
$a = new A();
$str = var_export($a,true).";";# ';'をつけておく
echo var_dump(un_var_export($str));

出来た!!

>php testF.php

object(A)#2 (3) {
  ["test1"]=>
  string(1) "1"
  ["test2"]=>
  string(1) "2"
  ["test3:private"]=>
  string(1) "3"
}

classkitモジュールって実はすごく便利なんじゃ・・・

PHPに追加される遅延評価と名前空間

遅延評価や名前空間があればこのへんがもう少し柔軟に出来そう。
でもReflection関連のドキュメントを読んでいるとPHPJava化してスクリプト言語っぽさを失ってしまってる。

でもclasskitでさらにスクリプトっぽくなるのかもね。