Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 19 di 50
  • livello avanzato
Indice lezioni

Iterazione sugli oggetti in PHP

Perché rendere un oggetto iterabile? Analizziamo le interfacce Iterator e IteratorAggregate che permettono di rendere un oggetto compatibile con il costrutto foreach come se si trattasse di un array.
Perché rendere un oggetto iterabile? Analizziamo le interfacce Iterator e IteratorAggregate che permettono di rendere un oggetto compatibile con il costrutto foreach come se si trattasse di un array.
Link copiato negli appunti

Rendere un oggetto iterabile

Dalla versione 5.0 PHP ha integrato all'interno della propria distribuzione standard la componente Standard PHP Library (SPL) che fornisce una serie di interfacce e classi per risolvere problemi comuni. Questa scelta è nata con la volontà di migliorare il supporto per la programmazione orientata agli oggetti nel linguaggio.

Tra le interfacce disponibili troviamo Iterator e IteratorAggregate che permettono di rendere un oggetto compatibile con il costrutto foreach come se si trattasse di un array mantenendo però l'essenza di oggetto che può integrare la propria logica business. Questa tecnica viene comunemente utilizzata dagli ORM (framework per l'accesso ai database) per eseguire un ciclo foreach su un set di risultati continuando ad aver accesso a metodi specifici.

Il codice mostra come iterare su un array presente in una classe che ha rapporti con diverse istanze di un'altra classe.

<?php
class MyManager
{
    private $objects;
    public function __construct()
    {
        $this->objects = array();
    }
    public function add(MyClass $object)
    {
        $this->objects[] = $object;
    }
    public function getObjects()
    {
        return $this->objects;
    }
}
$manager = new MyManager();
$manager->add(new MyClass(5));
$manager->add(new MyClass(10));
$manager->add(new MyClass(2));
$objects = $manager->getObjects();
foreach ($objects as $object) {
    echo $object->getValue();
}
// Output: 5, 10, 2

Implementare l'interfaccia Iterator

In questo caso però stiamo dando anche accesso in scrittura all'array presente nella classe e in generale si tratta di una soluzione poco elegante, ma possiamo implementare l'interfaccia Iterator aggiungendo 5 metodi (tutti public e senza parametri) alla nostra classe oltre ad una variabile per tenere traccia della posizione attuale durante l'iterazione.

Metodo Descrizione
key() Restituisce l'indice attuale del cursore (cioè la variabile menzionata precedentemente).
current() Restituisce l'oggetto alla posizione attuale del cursore.
next() Sposta il cursore alla posizione successiva e non restituisce niente.
rewind() Sposta il cursore alla posizione iniziale e non restituisce niente.
valid() Restituisce un valore booleano true se la posizione attuale del cursore corrisponde ad un oggetto, altrimenti false. Nel caso in cui venga restituito false il ciclo viene terminato.

Durante un ciclo foreach viene richiamato il metodo rewind() portando il cursore alla posizione iniziale, quindi viene chiamato il metodo valid() per controllare se la posizione attuale è disponibile, in caso positivo vengono chiamati i metodi key() e current() per ottenere l'indice e il valore attuali. A questo punto viene chiamato il metodo next() e il ciclo ricomincia dal metodo valid() fino a che non si ottiene un valore false.

Leggendo attentamente l'ordine delle chiamate ci si rende conto che alla fine di un ciclo foreach il cursore interno è spostato all'ultima posizione e non viene reimpostato sullo stato iniziale. Un esempio concreto di classe che implementa Iterator è il seguente:

<?php
class MyManager implements Iterator
{
    private $objects;
    private $position = 0;
    public function __construct()
    {
        $this->objects = array();
    }
    public function add(MyClass $object)
    {
        $this->objects[] = $object;
    }
    public function key()
    {
        return $this->position;
    }
    public function current()
    {
        return $this->objects[$this->position];
    }
    public function next()
    {
        $this->position += 1;
    }
    public function rewind()
    {
        $this->position = 0;
    }
    public function key()
    {
        return $this->position;
    }
    public function valid()
    {
        return isset($this->objects[$this->position]) || array_key_exists($this->position, $this->objects);
    }
}
$manager = new MyManager();
$manager->add(new MyClass(5));
$manager->add(new MyClass(10));
$manager->add(new MyClass(2));
foreach ($manager as $object) {
    echo $object->getValue();
}
// Output: 5, 10, 2

Semplificare il costrutto con l'interfaccia IteratorAggregate

Benché sia semplice implementare l'interfaccia Iterator, questa caratteristica ha un utilizzo prevalentemente in situazioni standard come l'iterazione su un array interno. Per evitare di reinventare la ruota è possibile sfruttare un'altra interfaccia, IteratorAggregate, insieme alle classi di PHP SPL.

L'interfaccia IteratorAggregate espone un singolo metodo, getIterator(), da richiamare senza parametri che restituisce un oggetto che implementa l'interfaccia Traversable (l'interfaccia che viene estesa da Iterator). Quindi l'esempio precedente si semplifica così:

<?php
class MyManager implements IteratorAggregate
{
    private $objects;
    public function __construct()
    {
        $this->objects = array();
    }
    public function add(MyClass $object)
    {
        $this->objects[] = $object;
    }
    public function getIterator()
    {
        return new ArrayIterator($this->objects);
    }
}
$manager = new MyManager();
$manager->add(new MyClass(5));
$manager->add(new MyClass(10));
$manager->add(new MyClass(2));
foreach ($manager as $object) {
    echo $object->getValue();
}
// Output: 5, 10, 2

Ti consigliamo anche