忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/11/23 06:11 |
PHP1-65:オブジェクトに配列とforeachでアクセスする続き

前回作成した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
<?php
    
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 &lt;'.$debug[0]['line'].'&gt;'
                .' type &lt;'.gettype($b).'&gt;'
            );
            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


2009/04/07 17:22 | Comments(0) | TrackBack() | PHP

トラックバック

トラックバックURL:

コメント

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<PHP1-66:オブジェクトに配列でアクセスするさらに続き | HOME | PHP1-64:Pear::CheckIPでIPアドレスチェック>>
忍者ブログ[PR]