Domanda

È corretto pensare che IteratorAggregate fornisce solo array come lettura accesso a un oggetto? Se ho bisogno di scrivere alla matrice oggetto-as-, quindi ho bisogno di usare Iterator?

Dimostrazione di un oggetto IteratorAggregate che causa un errore fatale quando si cerca di aggiungere un nuovo elemento segue:

<?

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
È stato utile?

Soluzione

Per l'uso quello che vale, data la classe del campione, si potrebbe avere appena fatto del ArrayObject (che implementa IteratorAggregate e ArrayAccess come vogliamo).

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

Con l'essere uscita, come previsto:

foo => bar
baz => quux

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

Altri suggerimenti

Ok, ora che ho più capito cosa vuoi dire, vorrei provare a spiegare qui.

L'implementazione di un iteratore (sia attraverso la Iterator o interfaccia IteratorAggregate) solo aggiungere funzionalità quando l'iterazione. Che cosa significa, è essa riguarda solo la capacità di utilizzo foreach. Non cambia o alterare qualsiasi altro uso dell'oggetto. Nessuno dei quali sostenere direttamente "scrittura" per l'iteratore. Io dico direttamente, dal momento che si può ancora scrivere l'oggetto base, mentre l'iterazione, ma non all'interno del foreach.

Ciò significa che:

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

funziona bene (visto che è la scrittura per l'oggetto originale).

Mentre

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

Molto probabilmente non sarà il lavoro in quanto si sta cercando di scrittura attraverso l'iteratore (che non è supportato direttamente. Può essere in grado di fare, ma non è la progettazione di base).

Per capire perché, Diamo un'occhiata a quello che sta succedendo dietro le quinte con che foreach ($obj as $value). E 'fondamentalmente identica a:

Per le classi Iterator:

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

Per le classi IteratorAggregate:

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

Ora, vedete perché la scrittura non è supportata? $iterator->current() non restituisce un riferimento. Così non si può scrivere direttamente tramite foreach ($it as &$value).

Ora, se si voleva fare questo, si avrebbe bisogno di modificare l'iteratore per restituire un riferimento da current(). Tuttavia, sarebbe interrompere l'interfaccia (dato che sarebbe cambiare la firma metodi). Quindi questo non è possibile. Quindi questo significa che la scrittura per il iteratore non è possibile.

Tuttavia, rendersi conto che colpisce solo l'iterazione stessa. Non ha nulla a che fare con l'accesso all'oggetto da qualsiasi altro contesto o in qualsiasi altro maniero. $obj->bar sarà esattamente lo stesso per un oggetto iteratore come lo è per un oggetto non iteratore.

Ora, ci arriva una differenza tra il Iterator e IteratorAggregate. Guardare indietro alla equivalenze while, e si può essere in grado di vedere la differenza. Supponiamo che abbiamo fatto in questo modo:

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

Che cosa accadrebbe? Con una Iterator, sarebbe stampare solo il primo valore. Questo perché l'iteratore opera direttamente sull'oggetto per ogni iterazione. E dal momento che si è modificato quello che i itera oggetto su (la matrice interna), i cambiamenti di iterazione.

Ora, se $objFoo è un IteratorAggregate, sarebbe stampare tutti i valori che esistevano all'inizio di iterazione. Questo perché hai fatto una copia della matrice quando sei tornato il new ArrayIterator($this->_array);. Così si può continuare per scorrere l'intero oggetto come lo era all'inizio di iterazione.

Si noti che differenza chiave. Ciascuna iterazione di un Iterator sarà dipendente dallo stato dell'oggetto in quel punto nel tempo. Ogni iterazione di un IteratorAggregate dipenderà dallo stato dell'oggetto all'inizio di iterazione. * Ha senso?

Ora, come per il vostro errore specifico. Non ha nulla a che fare con iteratori a tutti. Stai cercando di accesso (scrittura in realtà) un esterno variabile della iterazione. Quindi non comporta l'iteratore a tutti (con l'eccezione che si sta cercando di verificare i risultati dalla iterazione).

Si sta cercando di trattare l'oggetto come un array ($objFoo['reeb'] = 'roob';). Ora, per gli oggetti normali, questo non è possibile. Se si vuole fare questo, è necessario implementare il ArrayAccess interfaccia . Si noti che non c'è bisogno di avere un iteratore definito al fine di utilizzare tale interfaccia. Non fa altro che fornisce la possibilità di accesso elementi specifici di un soggetto utilizzando un array come sintassi.

Nota che detta schiera simili. Non si può trattare come un array in generale. funzioni sort non funzioneranno mai. Tuttavia, ci sono alcune altre interfacce cheè possibile implementare per rendere un oggetto più serie come. Uno è il Countable interfaccia che consente la possibilità di utilizzare la funzione count() su un oggetto (count($obj)).

Per un esempio nel nucleo di combinare tutti questi in una classe, controlla la class ArrayObject . Ciascuna interfaccia in quella lista fornisce la possibilità di utilizzare una funzione specifica del nucleo. Il IteratorAggregate offre la possibilità di utilizzare foreach. Il ArrayAccess fornisce la possibilità di accedere l'oggetto con una sintassi matrice simile. L'interfaccia Serializable fornisce la possibilità di serialize e deserialize i dati in una specifica maniero. L'interfaccia consente Countable per contare l'oggetto come un array.

Quindi, queste interfacce non influisce sulla funzionalità di base dell'oggetto in alcun modo. È ancora possibile fare qualsiasi cosa con esso che si potrebbe fare per un oggetto normale (come $obj->property o $obj->method()). Quello che fanno è comunque fornisce la possibilità di utilizzare l'oggetto come un array in certi luoghi nel nucleo. Ognuna di esse rappresenta la funzionalità in un ambito di stretta (nel senso che è possibile utilizzare qualsiasi combinazione di essi che si desidera. Non è necessario essere in grado di accedere all'oggetto come un array di essere in grado di count() esso). Questo è il vero alimentazione.

Oh, e assegnare il valore di ritorno di new per riferimento è deprecato , quindi non c'è bisogno di farlo ...

Quindi, come per il problema specifico, ecco un modo intorno ad esso. Ho semplicemente implementato l'interfaccia ArrayAccess nella vostra classe in modo che $objFoo['reeb'] = 'roob'; funzionerà.

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

Poi, si può provare il codice esistente:

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

E dovrebbe funzionare bene:

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

Si potrebbe anche "fix" che modificando la proprietà $objFoo->_array direttamente (come OOP regolare). Basta sostituire la $objFoo['reeb'] = 'roob'; linea con $objFoo->_array['reeb'] = 'roob';. Che verrà ottenere la stessa cosa ....

  • Si noti che questo è il comportamento predefinito. Si potrebbe incidere insieme un iteratore interno (l'iteratore restituito da IteratorAggreagate::getIterator) che non dipende dallo stato degli oggetti originali. Ma non è come è in genere fatto
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top