[php] Costruire un oggetto iterabile che possa essere percorso con foreach, grazie alle interfacce Iterator, ArrayAccess e Countable

Oggi propongo un semplice esercizio in PHP dove vogliamo costruire un oggetto iterabile che sia accessibile come un vettore e percorribile da foreach.

Immaginiamo di avere due classi Studente e Scuola, quello che vogliamo ottenere è il qualcosa di analogo a questo:

L’output sarà così:

in tutto ci sono 4 studenti
0 -> Squall
1 -> Zell
2 -> Rinoa
3 -> Edea

Anzitutto cominciamo costruendo la classe Studente. Questa è molto semplice e non ha nulla di particolare, la possiamo costruire come vogliamo ed in particolare, ai fini dell’esercizio, la farò con una sola proprietà e due metodi (set e get).

Adesso costruiamo Scuola implementando i metodi Iterator, ArrayAccess, Countable.

Le tre interfacce permettono di implementare diverse proprietà. Affinché l’oggetto costruito con Scuola sia percorribile dal foreach usiamo l’interfaccia Iterator.

L’interfaccia implementa i seguenti metodi astratti:

Si suppone che la nostra classe abbia una proprietà “posizione“, per cui si possa ricavare un valore da questa posizione corrente con current(). Il metodo key() dovrà restituire tale posizione corrente, next() permetterà di andare alla posizione successiva, mentre rewind() consentirà di ripartire dalla posizione iniziale. Infine il metodo valid() verificherà se nella posizione attuale esiste o meno un oggetto.

Cominciamo allora costruendo la classe nel modo seguente:

Per rendere la classe operativa aggiungiamo anche un altro paio di metodi. In particolare:

Con il controllo if( $studente instanceof Studente ) vogliamo assicurarci che l’oggetto aggiunto tramite il metodo add() sia del tipo Studente. Se il tipo non è quello giusto solleviamo un errore del tipo InvalidArgumentException dove avvisiamo l’utente.

Significa che la forma:

Produrrà un errore del tipo Fatal error: Uncaught InvalidArgumentException: l'oggetto deve essere di tipo Studente in iterator.php:92 Stack trace: #0 iterator.php(87): Scuola->erroreOggettoNonValido() #1 iterator.php(98): Scuola->add('Squall') #2 {main} thrown in iterator.php on line 92

L’operazione valida sarà invece:

A questo punto l’oggetto è già iterabile mediante foreach, che può essere chiamato nel modo seguente:

Vogliamo dare la possibilità di aggiungere gli oggetti anche alla maniera di $scuola[] = new Studente("Edea");

Per farlo implementiamo la nostra classe Scuola anche su ArrayAccess. L’interfaccia ArrayAccess richiede i seguenti metodi:

Quindi implementiamoli nella nostra classe Scuola alla maniera seguente:

Anche qui usiamo if( $value instanceof Studente ) per verificare che l’oggetto sia di tipo Studente. Nel metodo offsetGet() utilizziamo il null coalescing operator per restituire null nel caso in cui l’oggetto non esista.

A questo punto l’istruzione $scuola[] = new Studente("Edea"); diventa utilizzabile, mentre $scuola[] = "Edea"; produce il solito errore Fatal error: Uncaught InvalidArgumentException: l'oggetto deve essere di tipo Studente in...

Infine vogliamo poter usare il metodo count($scuola) sul nostro oggetto, ottenendo il conteggio di tutti gli studenti inseriti. Se lo usiamo senza ulteriori implementazioni il risultato sarebbe sempre e comunque 1.

L’interfaccia Countable richiede un unico metodo:

Implementiamolo nel modo seguente:

Probabilmente sarebbe stato più carino implementare un metodo interno alla classe, magari un contatore che tenesse conto delle aggiunte, ma ai fini dell’esercizio ci accontentiamo di chiamare count sul vettore interno degli studenti.

Fatto tutto questo vediamo il risultato finale così ottenuto:

Rispondi

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.