2

オブジェクトへのIteratorAggregate配列のような読み取りアクセスのみを提供すると考えるのは正しいですか?object-as-arrayに書き込む必要がある場合は、Iterator?を使用する必要があります。

新しい要素を追加しようとしたときに致命的なエラーを引き起こすIteratorAggregateオブジェクトのデモンストレーションは次のとおりです。

<?

class foo implements IteratorAggregate {

    public $_array = array('foo'=>'bar', 'baz'=>'quux');

    public function getIterator() {
        return new ArrayIterator($this->_array);
    }

} // end class declaration


$objFoo = & new foo();

foreach ( $objFoo as $key => $value ) {
        echo "key: $key, value: $value\n";
}

$objFoo['reeb'] = "roob";

foreach ( $objFoo as $key => $value ) {
        echo "key: $key, value: $value\n";
}

$ php test.php
key: foo, value: bar
key: baz, value: quux

Fatal error: Cannot use object of type foo as array in /var/www/test.php on line 20

Call Stack:
    0.0009      57956   1. {main}() /var/www/test.php:0
4

2 に答える 2

4

さて、私はあなたが何を意味するのかをよりよく理解したので、ここで説明しようと思います。

イテレータを(IteratorまたはIteratorAggregateインターフェイスを介して)実装すると、反復時にのみ機能が追加されます。つまり、を使用する機能にのみ影響しますforeach。オブジェクトの他の使用法を変更または変更することはありません。どちらもイテレータへの「書き込み」を直接サポートしていません。直接言います。繰り返しながらベースオブジェクトに書き込むことはできますが、foreachの内部には書き込むことができないからです。

つまり、次のことを意味します。

foreach ($it as $value) {
    $it->foo = $value;
}

正常に動作します(元のオブジェクトに書き込んでいるため)。

その間

foreach ($it as &$value) {
    $value = 'bar';
}

イテレータを介して書き込もうとしているため、おそらく機能しません(直接サポートされていません。実行できる可能性がありますが、コアデザインではありません)。

その理由を理解するために、その舞台裏で何が起こっているのかを見てみましょうforeach ($obj as $value)。基本的には次のものと同じです。

Iteratorクラスの場合:

$obj->rewind();
while ($obj->valid()) {
    $value = $obj->current();
    // Inside of the loop here
    $obj->next();
}

IteratorAggregateクラスの場合:

$it = $obj->getIterator();
$it->rewind();
while ($it->valid()) {
    $value = $it->current();
    // Inside of the loop here
    $it->next();
}

さて、なぜ書き込みがサポートされていないのかわかりますか? $iterator->current()参照を返しません。したがって、を使用して直接書き込むことはできませんforeach ($it as &$value)

さて、それをしたいのなら、からの参照を返すようにイテレータを変更する必要がありますcurrent()。ただし、これによりインターフェイスが破損します(メソッドのシグネチャが変更されるため)。だからそれは不可能です。つまり、イテレータへの書き込みは不可能です。

ただし、これは反復自体にのみ影響することに注意してください。他のコンテキストや他のマナーからオブジェクトにアクセスすることとは何の関係もありません。 $obj->barイテレータオブジェクトの場合も、非イテレータオブジェクトの場合もまったく同じになります。

さて、との間に違いがIteratorありIteratorAggregateます。同等性を振り返るとwhile、違いがわかる場合があります。これを行ったとしましょう:

foreach ($objFoo as $value) {
    $objFoo->_array = array();
    print $value . ' - ';
}

どうなるでしょうか?を使用するIteratorと、最初の値のみが出力されます。これは、イテレータが反復ごとにオブジェクトを直接操作するためです。また、オブジェクトが反復するもの(内部配列)を変更したため、反復が変更されます。

ここで、$objFooがの場合IteratorAggregate、反復の開始時に存在していたすべての値が出力されます。これは、を返したときに配列のコピーを作成したためですnew ArrayIterator($this->_array);。したがって、反復の開始時と同じように、オブジェクト全体を反復し続けることができます。

重要な違いに注意してください。aの各反復は、Iteratorその時点でのオブジェクトの状態に依存します。aの各反復は、IteratorAggregate反復開始時のオブジェクトの状態に依存します。*それは意味がありますか?

さて、あなたの特定のエラーについて。イテレータとはまったく関係ありません。反復の外部で変数にアクセス(実際に書き込み)しようとしています。したがって、イテレータはまったく含まれません(反復によって結果を確認しようとしていることを除いて)。

オブジェクトを配列()として処理しようとしています$objFoo['reeb'] = 'roob';。さて、通常のオブジェクトの場合、それは不可能です。それを行いたい場合は、ArrayAccessインターフェースを実装する必要があります。そのインターフェースを使用するためにイテレーターを定義する必要はないことに注意してください。配列のような構文を使用して、オブジェクトの特定の要素にアクセスする機能を提供するだけです。

私が配列のように言ったことに注意してください。一般的に配列のように扱うことはできません。 sort関数は機能しません。ただし、オブジェクトをより配列のようにするために実装できるインターフェイスは他にもいくつかあります。1つは、オブジェクト( )で関数を使用できるようにするCountableインターフェイスです。count()count($obj)

これらすべてを1つのクラスに結合するコアの例については、クラスを確認してArrayObjectください。そのリストの各インターフェイスは、コアの特定の機能を使用する機能を提供します。を使用IteratorAggregateする機能を提供しますforeach。はArrayAccess、配列のような構文でオブジェクトにアクセスする機能を提供します。Serializableインターフェースは、特定のマナーのデータを提供しますserializedeserializeインターフェイスではCountable、配列のようにオブジェクトをカウントできます。

したがって、これらのインターフェイスは、オブジェクトのコア機能にはまったく影響しません。$obj->propertyそれでも、通常のオブジェクト(またはなど)に対して実行できることは何でも実行できます$obj->method()。ただし、コア内の特定の場所で配列のようにオブジェクトを使用する機能を提供します。それぞれが厳密な範囲で機能を提供します(つまり、任意の組み合わせで使用できます。配列のようにオブジェクトにアクセスできる必要はありませんcount())。それがここでの真の力です。

ああ、参照によるの戻り値の割り当てnewは非推奨になっているので、それを行う必要はありません...

それで、あなたの特定の問題に関しては、これがそれを回避する一つの方法です。それが機能するように、私は単にArrayAccessあなたのクラスにインターフェースを実装しました$objFoo['reeb'] = 'roob';

class foo implements ArrayAccess, IteratorAggregate {

    public $_array = array('foo'=>'bar', 'baz'=>'quux');

    public function getIterator() {
        return new ArrayIterator($this->_array);
    }

    public function offsetExists($offset) {
        return isset($this->_array[$offset]);
    }

    public function offsetGet($offset) {
        return isset($this->_array[$offset]) ? $this->_array[$offset] : null;
    }

    public function offsetSet($offset, $value) {
        $this->_array[$offset] = $value;
    }

    public function offsetUnset($offset) {
        if (isset($this->_array[$offset]) {
            unset($this->_array[$offset]);
        }
    }
}

次に、既存のコードを試すことができます。

$objFoo = & new foo();

foreach ( $objFoo as $key => $value ) {
    echo "key: $key, value: $value\n";
}

$objFoo['reeb'] = "roob";

foreach ( $objFoo as $key => $value ) {
    echo "key: $key, value: $value\n";
}

そしてそれはうまくいくはずです:

key: foo, value: bar
key: baz, value: quux
key: foo, value: bar
key: baz, value: quux
key: reeb, value: roob

$objFoo->_arrayプロパティを直接変更することで「修正」することもできます(通常のOOPのように)。$objFoo['reeb'] = 'roob';行を。に置き換えるだけ$objFoo->_array['reeb'] = 'roob';です。それは同じことを成し遂げるでしょう…。

  • これがデフォルトの動作であることに注意してください。IteratorAggreagate::getIterator元のオブジェクトの状態に依存する内部イテレータ(によって返されるイテレータ)を一緒にハックすることができます。しかし、それは通常行われている方法ではありません
于 2010-08-27T14:41:47.753 に答える
4

ArrayObjectサンプルクラスを考えると、その価値については、 (実装IteratorAggregateArrayAccess、私たちが望むように)を利用することができたはずです。

class Foo extends ArrayObject
{
    public function __construct()
    {
        parent::__construct(array('foo' => 'bar', 'baz' => 'quux'));
    }
}

$objFoo = new Foo();

foreach ( $objFoo as $key => $value ) {
    echo "$key => $value\n";
}

echo PHP_EOL;

$objFoo['foo'] = 'badger';
$objFoo['baz'] = 'badger';
$objFoo['bar'] = 'badger';

foreach ( $objFoo as $key => $value ) {
    echo "$key => $value\n";
}

予想どおり、出力は次のようになります。

foo => bar
baz => quux

foo => badger
baz => badger
bar => badger
于 2010-08-27T20:50:22.290 に答える