ArrayObjectによる配列演算子のオーバーロード風

PHP5やり始めたばっかでまだ右も左もわからずに右往左往してます。

色々調べてたら、PHP5になってSPL(Standard PHP Library)ってのが導入されたということを知った。へー。

そのSPLの中にArrayObjectという物がありまして、オブジェクトを配列っぽく扱えるようにしたものなのですよ。今日はコレを取り上げてみたいと思います。

とりあえず使用例。

<?php

$foo = new ArrayObject;

$foo['foo'] = 100;
$foo['foo-bar'] = 200;

print_r((array)$foo);
Array
(
    [foo] => 100
    [foo-bar] => 200
)

おお、面白い。オブジェクトなのに配列のように扱えています。

そして更に面白いのがオブジェクトのプロパティに設定した値と同期させることもできるところです。

以下の例を見てください。

<?php

$foo = new ArrayObject(array(),ArrayObject::ARRAY_AS_PROPS);

$foo->foo = 9999;      // プロパティfooに設定
$foo['foo-bar'] = 200; // 配列形式でfoo-barに設定

print_r((array)$foo);
Array
(
    [foo] => 9999
    [foo-bar] => 200
)

これは凄いですね。

ある変数に対して、プロパティ呼び出しでも配列形式でもどちらの方法でもアクセスできるというのはかなり便利です。

記号等を含まないデータ名の場合はプロパティ呼び出しで値を設定し、含んでしまうようなデータ名の場合は配列形式で値を設定するという感じでしょうか。

<?php

// 普段はプロパティでアクセスする
$stash->foo = 1;
$stash->bar = 22;
$stash->baz = 44;
$stash->hoge = 55;

// どうしても記号を含む場合は配列でアクセスする
$stash['foo-bar'] = 200;
$stash['baz::hoge'] = 300;

僕の場合、オブジェクトのプロパティを連想配列代わりとして使うことがちょくちょくあったりするんですよね。やっぱ括弧やシングルクォートすら書くのが面倒臭いので->だけでアクセスできると凄い楽なんです。当然、記号が必要になりそうなケースでは初めから連想配列を使いますが。

このArrayObjectはPerlCatalystのstashの様な使い方に向いてるんじゃないかなぁと思います。

個人的に非の打ち所が無いArrayObjectですが、こうなるとやっぱり気になるのは速度面でどうなのか?ってことですよね。

というわけでArrayObjectとstdClassと配列でベンチマークをとってみました。

<?php

require_once "Benchmark/Iterate.php";

function array1 () {
    $foo = new ArrayObject(array(),ArrayObject::ARRAY_AS_PROPS);
    foreach(range(0,10000) as $i ) {
        $p = "aaa$i";
        // プロパティ、配列2パターン試しておく
        $foo[$p] = 10;
        $foo->$p += 10;
    }
    $array = (array)$foo;
}

function array2 () {
    $foo = new stdClass;
    foreach(range(0,10000) as $i ) {
        $p = "aaa$i";
        $foo->$p = 10;
        $foo->$p += 10;
    }
    $array = (array)$foo;
}

function array3 () {
    $foo = array();
    foreach(range(0,10000) as $i ) {
        $p = "aaa$i";
        $foo[$p] = 10;
        $foo[$p] += 10;
    }
    $array = (array)$foo;
}

$counter =  10;
$bench   =& new Benchmark_Iterate;
$bench->run($counter,'array1'); $result1 = $bench->get();
$bench->run($counter,'array2'); $result2 = $bench->get();
$bench->run($counter,'array3'); $result3 = $bench->get();

print('arrayobject -> '.$result1['mean'] . "\n");
print('stdclass    -> '.$result2['mean'] . "\n");
print('array       -> '.$result3['mean'] . "\n");
arrayobject -> 0.032885
stdclass    -> 0.033031
array       -> 0.031132


え、ちょ、ま。

殆ど同じじゃないっすか!

あれれ、ベンチの書き方間違ってるのかな・・・。あまりPHPでベンチ書かないからよくわからない。

しかし、もし本当にこれだけ早いのならコレマジで使わない手は無いですね。便利すぎる。