PHP SPLの標準Interfaceを真剣に考える
ArrayAccessとかIteratorとか
関連記事:
WSLにDocker代替のPodmanを入れてみる: ポッドマンが倒せない(1)
WSL2上のリモートPodmanにWindowsから接続: ポッドマンが倒せない(2)
WSLサポートしたpodmanV4: ポッドマンが倒せない(3)
Distrodを使ってWSLでsystemdを動かす
------------------- ↓ 前書きはここから ↓-------------------
かなり古くからありながらあまり使われていないSPL。
PDOのようにフレームワークの中核にあるのを除けば、
他は知らない人がほとんどだと思う。
つい先日、設計の不備をカバーするためにIteratorをちゃんと考える機会があった
PHPのforeachが使い勝手も性能的にも良すぎるので、
Iteratorなんているのか?
って思っていたぐらいだったが、
複雑怪奇なループが出たときに対応が難しい。
Iteratorから組めばスマートなコーディングも可能だとわかった。
一つ一つ掘り下げるのは別記事でやるとして、
今回はこういうのがあるよと言う紹介をしてみよう。
ヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪
------------------- ↓ 本題はここから ↓-------------------
SPLインタフェース
Interfaceというとメソッドとかプロパティとかの型だけ決めて、
実装者と利用者を分ける目的で使うと思うが、
PHPのインタフェースにはそれとは別に、
固有機能付与される。
なので目的に合致するものは積極的に使うのがいいだろう。
ArrayAccess(ArrayObject, ArrayIterator)
特殊能力: オブジェクトを配列として扱える
PDOを除けば次点で使われているであろうArrayAccessインタフェース。
直接コードに書かなくてもフレームワークに実装されて間接的に使ってる人がほとんどだと思う。
Laravelを使ってる方は一度Collectionのソースを見て欲しい。
class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
{
}
普通PHPのオブジェクトは配列として扱えない。
例えば空オブジェクトであるstdClassでも
<?php
$obj = new stdClass();
$obj->abc = 'def';
$obj->bcd = 'efg';
echo $obj['abc'];
実行すると
php stdclass.php
PHP Fatal error: Uncaught Error: Cannot use object of type stdClass as array in /home/dozo/stdclass.php:6
オブジェクトだから当たり前だけどFatalで落ちる。
普通のクラス定義でも同じなのだが、
ArrayAccessを使ってみると
<?php
class stdObject implements ArrayAccess
{
protected $items = [];
public function __construct($data = [])
{
$this->items = $data;
}
public function offsetExists($offset){}
public function offsetGet($offset){}
public function offsetSet($offset, $value){}
public function offsetUnset($offset){}
}
$obj = new stdObject([
'abc' => 'def',
'bcd' => 'efg',
]);
echo $obj['abc'];
実行すると
php arrayobject.php
def
('ω') キモイ
offset~~はちゃんと定義するが、
ページが長くなるのでカット。
ArrayObjectやArrayIteratorはその拡張クラス。
正直この二つは使い勝手が悪いので、
ArrayAccessから自身で組む方が良いと思う。
JsonSerializable
特殊能力: JSON変換のタイミングで実行するメソッドを保有
SPLではないがext_jsonに付属するので、
最早標準と言っていいインタフェース
JSONデータを作るときに配列を作ってjson_encodeをする人がほとんどがと思う。
一応オブジェクトもJSON化できるが、
JsonSerializableを使うと、
クラスの内部からかつ各クラス毎に出力内容を制御できる
<?php
class Abc implements JsonSerializable {
public function jsonSerialize()
{
$this->abc = 'def';
$this->bcd = 'efg';
$this->nest = new Def();
return $this;
}
}
class Def implements JsonSerializable {
public function jsonSerialize()
{
return ['cde' => 'def'];
}
}
$obj = new Abc();
echo json_encode($obj);
実行すると
php objToJson.php
{"abc":"def","bcd":"efg","nest":{"cde":"def"}}
正規化したデータベースを反映して出すなど、
結構重宝する
IteratorAggregate(Iterator)
特殊能力: foreachで分解する内容を制御できる
配列やオブジェクトをforeachに掛けると、
最初から最後まで一定の法則でデータを取り出し、
その中で処理を行うわけだが、
1レコード目は省くとか、
中のデータをちょっと変えるとか、
&とかで配列自体を差し替えるとか、
細かい事象が発生する場合がある。
基本的にはべた書きでいいのだが、
あまりにも複雑になりすぎるときは
Iteratorを検討するといい
ただIterator自体は使いにくいので、
IteratorAggregate + Generatorを推したい
<?php
class Abc implements IteratorAggregate{
public function getIterator()
{
$key = 0;
$item = ['abc' => 'def'];
yield $key => $item;
$key++;
$item = ['bcd' => 'efg'];
yield $key => $item;
}
}
foreach (new Abc() as $key => $item) {
echo "$key:".var_export($item, true)."\n";
}
実行すると
php iteratorAggregete.php
0:array (
'abc' => 'def',
)
1:array (
'bcd' => 'efg',
)
------------------- ↓ 後書きはここから ↓-------------------
思ったより長くなってしまった
Discussion