前回作成したintArrayは一見配列のように代入取得できますが、foreachで値を取得しようとすると正しく動作しません。
intArrayをvar_dumpしてみると中身はこのようになっています。
object(intArray)#1 (1) {
["arr:private"]=>
array(2) {
[0]=>
int(123)
["abc"]=>
int(456)
}
}
PHPはObjectをforeachにかけると、その中のpublicなメンバ変数に順にアクセスします。
この場合privateしかないので、foreachしても何も返ってこないことになります。
foreachできない配列なんて役に立ちませんので、イテレータを実装してみます。
Iteratorを実装することでforeachができるようになり、ArrayAccessを実装することで配列としてアクセスすることができます。
で、この両者を足してみると配列そのものとなるわけです。
という方針で作ろうと思ってたのですが、連想配列に対してArrayAccess::offsetSet()とIterator::next()あたりを実装するあたりでおそろしく面倒なことになったのでとりあえずキャンセル。
作れないことはないのですが見難きことこの上ないソースになってしまった。
あまりに美しくないのでもっと綺麗な書き方を思いつくまで封印。
というわけでSPLを活用して簡単に実装。
intArray.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
class intArray implements ArrayAccess,IteratorAggregate,Countable{
//配列
private $arr=array();
//コンストラクタ
function __Construct(){}
//値セット ArrayAccess::offsetSet
public function offsetSet($a,$b){
//引数が数値かチェック
if(!is_int($b)){
//数値でなければエラー
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not integer in line <'.$debug[0]['line'].'>'
.' type <'.gettype($b).'>'
);
return false;
}
//挿入
if($a===NULL){
$this->arr[]=$b;
}else{
$this->arr[$a]=$b;
}
return true;
}
//値が存在するか ArrayAccess::offsetExists
public function offsetExists($a){
if($a===NULL){return false;}
return isset($this->arr[$a]);
}
//値を取得 ArrayAccess::offsetGet
public function offsetGet($a){
if($this->offsetExists($a)){
return $this->arr[$a];
}else{
return null;
}
}
//値削除 ArrayAccess::offsetUnset
public function offsetUnset($a){
if($this->offsetExists($a)){
unset($this->arr[$a]);
}
}
//個数カウント Countable::count
public function count(){
return count($this->arr);
}
//イテレータ IteratorAggregate::getIterator
public function getIterator(){
return new ArrayIterator($this->arr);
}
#↓クラスのおわり
}
|
こういう場合のためにArrayIteratorなんてものが出来ていますのでこれを使ってさくっと実装しました。
http://jp.php.net/manual/ja/class.arrayiterator.php
配列を突っ込むとそれに対してイテレータでアクセスすることができるようになります。
IteratorAggregateは、自分でイテレータを実装するかわりに別のクラスに実装を丸投げしてしまうという他人任せのインターフェイスです。
http://jp.php.net/manual/ja/class.iteratoraggregate.php
で、本来ならIteratorAggregate::getIteratorで返すインスタンス内に各イテレータの実装を書かないといけないわけですが、そこに対してArrayIteratorを突っ込めばそこらへんが自動的にできてしまいます。
IteratorAggregateとArrayIteratorを使用することでロジックをほとんどいじらずforeachを実装できてしまいました。
ArrayAccess::offsetSet()なんかは値を返す必要はないのですが、気分的に入れています。
これは大丈夫なのだろうか?
以上でforeachもできるほとんど配列的なクラスintArrayが作成できました。
ただ、これでint型しか格納できない配列が作れて万々歳かと思いきや、やはり実体はObjectなわけで、in_array()やarray_merge()といった配列関数に突っ込むとエラーになってしまいます。
in_array('666',$int);
みたいな検索を行うことが出来ません。残念。
PR
トラックバック
トラックバックURL: