iteratorAggregateに新しい要素を書くことができませんか?
質問
私はそれを考えて正しいですか? IteratorAggregate
アレイのようなもののみを提供します 読む オブジェクトへのアクセス?アレイとしてオブジェクトに書き込む必要がある場合は、使用する必要があります Iterator
?
新しい要素を追加しようとするときに致命的なエラーを引き起こす反復gregateオブジェクトのデモンストレーションは次のとおりです。
<?
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
解決
サンプルクラスを考えると、それが価値があることを考えると、 ArrayObject
(これは実装されています IteratorAggregate
と ArrayAccess
私たちが望むように)。
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
他のヒント
わかりました、今、私はあなたが何を意味するのかをよりよく理解したので、ここで説明してみましょう。
イテレーターの実装(どちらかを介して Iterator
また IteratorAggregate
インターフェイス)は、反復時に機能を追加します。それが何を意味するのか、それは使用する能力にのみ影響しますか foreach
. 。オブジェクトの他の使用を変更したり変更したりしません。どちらも、イテレーターへの「書き込み」を直接サポートしていません。私は直接言います。なぜなら、あなたはまだ反復中にベースオブジェクトに書き込むことができるが、foreachの内側ではないからです。
それはそれを意味します:
foreach ($it as $value) {
$it->foo = $value;
}
正常に動作します(元のオブジェクトに書き込むので)。
その間
foreach ($it as &$value) {
$value = 'bar';
}
Iteratorを介して書き込もうとしているため、おそらく機能しない可能性があります(これは直接サポートされていません。実行できますが、コアデザインではありません)。
理由を見るために、それで舞台裏で何が起こっているのか見てみましょう 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オブジェクトと同様に、イテレーターオブジェクトの場合はまったく同じです。
さて、間に違いがあります Iterator
と IteratorAggregate
. 。振り返ってください while
同等性、そしてあなたは違いを見ることができるかもしれません。私たちがこれをしたとします:
foreach ($objFoo as $value) {
$objFoo->_array = array();
print $value . ' - ';
}
何が起こるのでしょうか?で Iterator
, 、最初の値のみを印刷します。これは、反復ごとにイテレーターがオブジェクト上で直接動作するためです。また、オブジェクトが反復するもの(内部配列)を変更したため、反復が変わります。
今、if $objFoo
です IteratorAggregate
, 、反復開始時に存在したすべての値を印刷します。それはあなたが返品したときにあなたが配列のコピーを作ったからです new ArrayIterator($this->_array);
. 。したがって、オブジェクト全体を反復開始時と同じように繰り返し続けることができます。
その重要な違いに注意してください。の各反復 Iterator
その時点でオブジェクトの状態に依存します。の各反復 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
参照により非推奨があります, 、だからそれをする必要はありません...
したがって、あなたの特定の問題については、ここにそれを回避する1つの方法があります。私は単に実装しました 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
)それは元のオブジェクト状態に依存します。しかし、それは通常行われる方法ではありません