さて、私はあなたが何を意味するのかをよりよく理解したので、ここで説明しようと思います。
イテレータを(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
インターフェースは、特定のマナーのデータを提供しますserialize
。deserialize
インターフェイスでは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
元のオブジェクトの状態に依存する内部イテレータ(によって返されるイテレータ)を一緒にハックすることができます。しかし、それは通常行われている方法ではありません