Frage

Am mich richtig, dass IteratorAggregate nur im Denken bietet Array-artige lesen Zugriff auf ein Objekt? Wenn ich auf das Objekt-as-Array schreiben müssen, dann muss ich verwenden Iterator?

Demonstration eines IteratorAggregate Objekt einen fatalen Fehler zu verursachen, wenn ein neues Element hinzuzufügen versuchen folgt:

<?

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
War es hilfreich?

Lösung

Für das, was es wert ist, Ihre Beispielklasse gegeben, die Sie gerade gemacht Nutzung des ArrayObject konnte (die Geräte IteratorAggregate und ArrayAccess wie wir wollen).

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";
}

Mit dem Ausgang Wesen, wie zu erwarten:

foo => bar
baz => quux

foo => badger
baz => badger
bar => badger

Andere Tipps

Ok, jetzt, dass ich noch besser verstehen, was Sie meinen, lassen Sie mich versuchen, hier zu erklären.

einen Iterator Implementierung (entweder über die Iterator oder IteratorAggregate Schnittstelle) nur Funktionen hinzufügen, wenn das Iterieren. Was das bedeutet, ist es wirkt sich nur auf die Fähigkeit zur Nutzung foreach. Dabei spielt es keine andere Nutzung des Objekts ändern oder verändern. Keiner von denen Unterstützung direkt „Schreiben“ an den Iterator. Ich sage direkt, da man immer noch auf das Basisobjekt schreiben kann, während das Iterieren, aber nicht innerhalb der foreach.

Das bedeutet, dass:

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

wird gut funktionieren (da es schriftlich auf das ursprüngliche Objekt).

Während

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

Die meisten werden wahrscheinlich nicht funktionieren, da es zu schreiben durch den Iterator versucht (was nicht direkt unterstützt wird. Es kann sein kann getan werden, aber es ist nicht das Kern-Design).

Um zu sehen, warum schauen wir uns an, was mit diesem foreach ($obj as $value) hinter den Kulissen passiert. Es ist im Grunde identisch mit:

Für Iterator Klassen:

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

Für IteratorAggregate Klassen:

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

Nun, sehen Sie, warum Schreiben nicht unterstützt wird? $iterator->current() kehrt nicht einen Verweis. Sie können also nicht direkt mit foreach ($it as &$value) schreiben.

Nun, wenn man will, das tun, müssen Sie den Iterator zu verändern, einen Verweis von current() zurückzukehren. Dies würde jedoch bricht die Schnittstelle (da sie die Methoden Signatur ändern würden). Das ist also nicht möglich. Das heißt also, dass das Schreiben an die Iterator ist nicht möglich.

Allerdings realize, dass es wirkt sich nur auf die Iteration selbst. Es hat nichts mit dem Zugriff auf das Objekt aus einem anderen Zusammenhang oder in irgendeine anderen Herren zu tun. $obj->bar wird genau das gleiche für ein Iterator-Objekt, wie es für eine nicht Iteratorobjekt ist.

Nun, es kommt ein Unterschied zwischen den Iterator und IteratorAggregate. Schauen Sie sich die while Äquivalenzen zurück, und Sie können in der Lage, den Unterschied zu sehen. Angenommen, wir dies taten:

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

Was würde passieren? Mit einem Iterator wäre es nur der erste Wert drucken. Das ist, weil der Iterator arbeitet direkt auf dem Objekt für jede Iteration. Und da Sie geändert, was das Objekt iteriert auf (das interne Array), die Iteration ändert.

Nun, wenn $objFoo ein IteratorAggregate ist, würde es alle Werte drucken, die zu Beginn der Iteration existierte. Das ist, weil Sie eine Kopie des Arrays aus, wenn Sie die new ArrayIterator($this->_array); zurückgegeben. So Sie iterieren das gesamte Objekt fortsetzen können, wie es zu Beginn der Iteration war.

Beachten Sie, dass wesentlicher Unterschied. Jede Iteration eines Iterator hängt von dem Zustand des Objekts zu diesem Zeitpunkt abhängen. Jede Iteration eines IteratorAggregate hängt von dem Zustand des Objekts zu Beginn der Iteration abhängig sein. * Ist das sinnvoll?

Jetzt, da für Ihre spezifischen Fehler. Es hat nichts mit Iteratoren überhaupt zu tun. Sie versuchen, den Zugriff (Schreiben tatsächlich) eine Variable außerhalb der Iteration. So ist es nicht den Iterator überhaupt nicht beteiligt (mit der Ausnahme, dass Sie die Ergebnisse zu überprüfen, indem das Iterieren sind versuchen).

Sie versuchen, das Objekt als Array ($objFoo['reeb'] = 'roob';) zu behandeln. Nun, für normale Objekte, das ist nicht möglich. Wenn Sie das tun wollen, müssen Sie die ArrayAccess Schnittstelle implementieren . Beachten Sie, dass Sie haben müssen, um einen Iterator nicht definiert, um diese Schnittstelle zu verwenden. Denn es macht nicht ist die Möglichkeit, den Zugriff auf bestimmte Elemente eines Objekts bieten eine Reihe wie Syntax.

Beachten Sie, sagte ich Array wie. Sie können es nicht wie ein Array im Allgemeinen behandeln. sort Funktionen wird nie funktionieren. Allerdings gibt es ein paar andere Schnittstellen,Sie können implementieren wie ein Objekt mehr Array zu machen. Eine davon ist die Countable Schnittstelle , die die Fähigkeit ermöglicht die count() Funktion zu verwenden auf ein Objekt (count($obj)).

Für ein Beispiel im Kern all dieser in eine Klasse Kombination Besuche die ArrayObject Klasse . Jede Schnittstelle in dieser Liste bietet die Möglichkeit, eine bestimmte Funktion des Kerns zu verwenden. Die IteratorAggregate bietet die Möglichkeit, die Verwendung foreach. Die ArrayAccess bietet die Möglichkeit, das Objekt mit einer Array-ähnlicher Syntax zuzugreifen. Die Serializable Schnittstelle bietet die Möglichkeit, serialize und deserialize die Daten in einem bestimmten Manor. Die Countable Schnittstelle ermöglicht zum Zählen des Objekts wie ein Array.

So diese Schnittstellen haben keinen Einfluss auf die Kernfunktionalität des Objekts in irgendeiner Weise. Sie können immer noch mit ihm tun alles, was Sie zu einem normalen Objekt tun könnten (wie $obj->property oder $obj->method()). Was sie tun, ist jedoch die Möglichkeit bieten, das Objekt wie ein Array an bestimmten Stellen im Kern zu verwenden. Jeder bietet die Funktionalität in einem strengen Rahmen (Was bedeutet, dass Sie eine beliebige Kombination von ihnen verwenden können, dass Sie möchten. Sie haben nicht auf das Objekt zuzugreifen wie ein Array in der Lage sein müssen in der Lage sein, es zu count()). Das ist die wahre Macht hier.

Oh, und Zuweisung der Rückgabewert von new durch Referenz deprecated , so gibt es keine Notwendigkeit, dass ...

zu tun

So, wie für Ihr spezielles Problem, hier ist ein Weg, um ihn herum. Ich habe einfach die ArrayAccess Schnittstelle in Ihre Klasse implementiert, so dass $objFoo['reeb'] = 'roob'; arbeiten.

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]);
        }
    }
}

Dann können Sie Ihre vorhandenen Code versuchen:

$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";
}

Und es sollte funktionieren:

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

Sie könnten auch als „fix“ durch die $objFoo->_array Eigenschaft direkt zu ändern (wie normale OOP). Einfach die Linie $objFoo['reeb'] = 'roob'; mit $objFoo->_array['reeb'] = 'roob'; ersetzen. Das wird das Gleiche erreichen ....

  • Beachten Sie, dass dies das Standardverhalten ist. Sie könnten eine innere Iterator (der Iterator zurück von IteratorAggreagate::getIterator) hacken zusammen, die auf dem ursprünglichen Objekte Zustand abhängt. Aber das ist nicht, wie es normalerweise getan
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top