get_object_varsでprivate/protectedなプロパティを取りたい

　get_object_varsはオブジェクトのプロパティを連想配列で取得できる便利な関数ですが、残念なことに現在のスコープでアクセスできるプロパティしか取得できません。ですのでテストやデバッグ時には使えません。

そこで、リフレクションを使って無理矢理にprivate/protectedなプロパティへアクセスする関数を作ってみます。

<br>

```php:テストプログラム
<?php
class GrandParentClass
{
	public    $grandparent_public_var = 1;
	protected $grandparent_protected_var = 2;
	private   $grandparent_private_var = 3;

	public    $public_var = 10;
	protected $protected_var = 11;
	private   $private_var = 12;

	public function getObjectVars()
	{
		return get_object_vars($this);
	}
}
class ParentClass extends GrandParentClass
{
	public    $parent_public_var = 4;
	protected $parent_protected_var = 5;
	private   $parent_private_var = 6;

	public    $public_var = 13;
	protected $protected_var = 14;
	private   $private_var = 15;

	public function getObjectVars()
	{
		return array_merge(parent::getObjectVars(), get_object_vars($this) );
	}
}
class ChildClass extends ParentClass
{
	public    $child_public_var = 7;
	protected $child_protected_var = 8;
	private   $child_private_var = 9;

	public    $public_var = 16;
	protected $protected_var = 17;
	private   $private_var = 18;

	public function getObjectVars()
	{
		return array_merge(parent::getObjectVars(), get_object_vars($this) );
	}
}

function get_object_vars_ex( $obj )
{
	$ref = new ReflectionObject( $obj );
	$vars = get_object_vars_me_and_parent( $obj, $ref );
	return $vars;
}

function get_object_vars_me_and_parent( $obj, $ref )
{
	$vars = array();

	$props = $ref->getProperties();
	foreach( $props as $p ){
		$p->setAccessible(true);

		$vars[$p->getName()] = $p->getValue($obj);
	}

	$parent = $ref->getParentClass();
	if ( $parent !== FALSE ){
		$vars_parent = get_object_vars_me_and_parent( $obj, $parent );
		$vars = array_merge( $vars_parent, $vars );
	}

	return $vars;
}

$me = new ChildClass();

// TEST1: get_object_vars
$start = microtime(true);
$result1 = get_object_vars($me);
$elapse1 = round((microtime(true) - $start) * 1000,10);

// TEST2: array cast
$start = microtime(true);
$result2 = (array)($me);
$elapse2 = round((microtime(true) - $start) * 1000,10);

// TEST3: getObjectVars
$start = microtime(true);
$result3 = $me->getObjectVars();
$elapse3 = round((microtime(true) - $start) * 1000,10);

// TEST4: get_object_vars_ex
$start = microtime(true);
$result4 = get_object_vars_ex($me);
$elapse4 = round((microtime(true) - $start) * 1000,10);

echo "------- result -------" . PHP_EOL;
echo "result1:" . print_r($result1,true) . PHP_EOL;
echo "result2:" . print_r($result2,true) . PHP_EOL;
echo "result3:" . print_r($result3,true) . PHP_EOL;
echo "result4:" . print_r($result4,true) . PHP_EOL;

echo "------- time -------" . PHP_EOL;
echo "elapse1: {$elapse1}" . PHP_EOL;
echo "elapse2: {$elapse2}" . PHP_EOL;
echo "elapse3: {$elapse3}" . PHP_EOL;
echo "elapse4: {$elapse4}" . PHP_EOL;
```

```php:実行結果
------- result -------
result1:Array
(
    [child_public_var] => 7
    [public_var] => 16
    [parent_public_var] => 4
    [grandparent_public_var] => 1
)

result2:Array
(
    [child_public_var] => 7
    [ * child_protected_var] => 8
    [ ChildClass child_private_var] => 9
    [public_var] => 16
    [ * protected_var] => 17
    [ ChildClass private_var] => 18
    [parent_public_var] => 4
    [ * parent_protected_var] => 5
    [ ParentClass parent_private_var] => 6
    [ ParentClass private_var] => 15
    [grandparent_public_var] => 1
    [ * grandparent_protected_var] => 2
    [ GrandParentClass grandparent_private_var] => 3
    [ GrandParentClass private_var] => 12
)

result3:Array
(
    [child_public_var] => 7
    [child_protected_var] => 8
    [public_var] => 16
    [protected_var] => 17
    [parent_public_var] => 4
    [parent_protected_var] => 5
    [grandparent_public_var] => 1
    [grandparent_protected_var] => 2
    [grandparent_private_var] => 3
    [private_var] => 18
    [parent_private_var] => 6
    [child_private_var] => 9
)

result4:Array
(
    [grandparent_public_var] => 1
    [grandparent_protected_var] => 2
    [grandparent_private_var] => 3
    [public_var] => 16
    [protected_var] => 17
    [private_var] => 18
    [parent_public_var] => 4
    [parent_protected_var] => 5
    [parent_private_var] => 6
    [child_public_var] => 7
    [child_protected_var] => 8
    [child_private_var] => 9
)

------- time -------
elapse1: 0.0231266022
elapse2: 0.0140666962
elapse3: 0.0741481781
elapse4: 0.3459453583

```

<br>

　リフレクションでプロパティにアクセスする際、リフレクションオブジェクトのgetProperties()メソッドを呼び出して各プロパティのオブジェクトを取得できるのですが、その際にsetAccessible(true)をしてあげないとReflectionExceptionが発生します。

　Arrayへのキャストでもprivate/protectedプロパティは取得できますが、値のキー名がおかしなことになっています。内部でget_object_varsを呼び出しているTEST3は要件を満たしていますが、テスト対象のクラスにメソッドを実装することになるのでテストには不向きでしょう。

　今回作成したリフレクションバージョンのget_object_vars関数(get_object_vars_ex）は要件を満たしていますが、ベンチマークを取るとTEST3の5倍も遅いという結果に。うーん。