ニュース・ブログ

ニュース・ブログ一覧PHPの記事情報配列に処理を差し込むための ArrayIterator

配列に処理を差し込むための ArrayIterator

今日は風が強いな~、こんにちは SGK です。
今回はちょっとした PHP のテクニックで ArrayIterator の有用性を紹介します。

配列にjsonデータがありました

以下のように配列の各要素にjson文字列を格納したデータがあり、
これをデコードして1行づつ表示したいケースがあるとします。

$aryJson = ['{"title":"あ","val":1}', '{"title":"い","val":2}'];


タイトル:あ、値:1
タイトル:い、値:2
 
まぁ単純に foreach でループしながら json_decode して表示してあげれば良いのですが、
弊社では原則、MVC での View 部分での値の代入を禁止しており、
この場合 json_decode した結果を変数に入れて参照する方法はルール違反となってしまいます。
 
突然「MVC」を出してしまいましたが、今回の流れとしては「json文字列を格納した配列」を
Controller で Model から受け取って、View でそのデータを参照・出力する処理をどうするか、の話をします。
 
※なぜ View での値の代入を禁止? View でのロジックのコーディングを予防するためです。

あらかじめデコード

じゃあどうするか、
次に考えられるのはあらかじめデコードして配列を構築しなおしておけば良い、
Controller とかで、、

foreach( $aryJson as &$val ){
	$val = json_decode( $val, true );
}

ですが、こういうコードを書くときいつも悩ましいのが、変換のループと参照時のループで2回配列を巡回する事を前提としてしまう事です。

ArrayIterator を使う

そういう場合、配列からデータを取り出すときに処理を差し込む方法が考えられます。
それを実現するのが ArrayIterator です。
PHP には ArrayIterator というネイティブクラスが用意されており、
これを拡張する事で配列に関する様々な動作に任意の処理をはさむ事ができます。

今回のようなケースでは以下のような ArrayJsonDecodeIterator を用意する事で解決しました。

class ArrayJsonDecodeIterator extends \ArrayIterator{ 
	public function current(){
		return @json_decode( parent::current(), true );
	}
}

これを MVC 的に利用すると以下のようなコードになります。
 
Controller で配列をラップ

$objJson = new ArrayJsonDecodeIterator( $aryJson );
//何がしかの View に $objJson をセットする処理
$this->set([ 'objJson' => $objJson ]);

 
View で出力

foreach( $objJson as $val ){
	echo "タイトル:$val[title]、値:$val[val]";
}

注釈

※簡単に説明: ArrayJsonDecodeIterator のインスタンス $objJson の要素を参照すると、
ArrayJsonDecodeIterator::current() が実行されその結果が返ってきます。
$objJson[1] を参照すれば [“title”=>”い”,”val”=>2] という配列が返ってきます。
 
※注意したいのはあらかじめ変換して値をセットしておく方法に比べると、
処理速度的にはそんなに変わりないか、ArrayIterator の方が遅い可能性があるし、
参照時のループが2回以上巡回利用される可能性がある場合は再度 json_decode が行われるので絶対遅いことです。
 
※ArrayIterator を実装するクラスのインスタンスは以下のように配列の特長を備え、
かつオブジェクトの振る舞いをすることも可能です。

//↓これは良い
$objJson[] = '{"title":"う","val":3}';
//↓でもこれはダメ、array系関数に渡す系は無理。
array_push($objJson , '{"title":"う","val":3}');

//↓とか
$objJson->flag = true;
//setFlagメソッドとかを追加実装して↓のようにするのが望ましいけど
$object->setFlag( true );

以上

他のメソッドもオーバーライドすればいろいろ面白いことができます。
残念なのは ArrayIterator ではラップした元の配列に直にアクセスできない点で(たぶんない)、
その場合はよりプリミティブな SeekableIterator や Iterator の拡張から書けば可能ですが、記述しなければいけない事が多くなります。
 
他の ArrayIterator のメンバーやイテレータクラスについて詳しくはPHPマニュアルを参照して見てください。
[ArrayIterator クラス]
[定義済みのインターフェイスとクラス]
 
PHP の ArrayIterator についてでした。