Come funziona DAVVERO foreach in PHP

di Lorenzo Neri

Come funziona DAVVERO foreach in PHP? Devi sapere che l’iterazione del ciclo foreach in PHP non è proprio lineare. Ne parlo in questo articolo.

Ciao mi chiamo Lorenzo Neri e sono un informatico: realizzo contenuti per aiutare le persone a padroneggiare l’arte del nuovo millennio, ovvero l’informatica!

Diciamolo, il foreach, che sia PHP o un altro linguaggio di programmazione, torna davvero comodo: soprattutto per chi è davvero pigro 😀

Ti sarai accorto però, che in certi casi non fa proprio quello che dovrebbe fare.

Ti faccio un paio di esempi:

$array = array(1, 2, 3, 4, 5);
foreach ($array as $elemento){
   echo $elemento."\n";
   $array[] = $elemento;
}
print_r($array);
/*
   Output nel primo ciclo: 1 2 3 4 5 
   Output dopo il ciclo: 1 2 3 4 5 1 2 3 4 5
*/

Insomma, di modifiche all’array non ne abbiamo fatte direttamente.

Ma lascia che ti mostri un altro esempio di come NON dovrebbe funzionare foreach in PHP:

foreach ($array as $chiave => $elemento) {
   $array[$chiave + 1] = $elemento + 2;
   echo elemento."\n";
} 
print_r($array);
/*
   Output nel primo ciclo: 1 2 3 4 5 
   Output dopo il ciclo: 1 3 4 5 6 7
*/

Anche qua non è ciò che ci aspetteremmo. Di per sé sulla sezione del manuale PHP troviamo questo:

Quando l’esecuzione del foreach parte, il puntatore all’array è automaticamente resettato. Esso “punta” direttamente al primo elemento dell’array

Manuale PHP

Questo ci torna comodo per addentrarci meglio e capire come funziona DAVVERO il ciclo foreach in PHP.

Come funziona l’iterazione MA PER DAVVERO del foreach in PHP?

Partiamo dal presupposto che foreach in PHP supporta l’iterazione di tre elementi:

  • Array
  • Oggetti
  • Oggetti trasversabili sìsì lo so è tecnichesissima questa definizione: ti ho lasciato il link apposta!

Nell’esempio a seguito ti mostrerò come funziona l’iterazione tramite foreach con i vari casi, basandomi sugli oggettiv trasverabili, visto che comunque è il più semplice:

foreach ($it as $k => $v) { /* codice */ }
// Equivalente di quello che ho scritto prima
if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* codice */
}

In sintesi, hai notato che il foreach è nient’altro che “syntax sugar”, ovvero un modo per addolcire la sintassi di quello che è realmente.

Ciò che ho scritto dopo “Equivalente di quello che ho scritto prima” nient’altro è che il foreach scritto per esteso.

E c’è qualcosa in più.

Se esegui l’iterazione per referenza, ad esempio con “foreach ($arr as &$v)” la variabile “$arr” diventa automaticamente una referenza e puoi modificarla durante l’iterazione.

In PHP 5 succede lo stesso, persino se esegui l’iterazione per valore.

L’ultimo caso, riguarda gli oggetti. Essi si comportano come il modello per referenza: questo significa che durante l’iterazione possono essere modificati direttamente.

Da qui, comprendiamo assieme che non è il funzionamento che vorremmo DAVVERO per i foreach in PHP.

Il tuo obiettivo non è quello di modificare gli elementi iterati, al contrario.

Come risolvere davvero ma PER DAVVERO questo problema

La soluzione alla modifica non desiderata degli elementi iterati con i foreach, la vediamo per due step.

In PHP 5 puoi fare così:

// Utilizziamo l'iterazione per referenza, così che siamo SICURI che l'array è
// DAVVERO lo stesso in entrambi i loop e NON una copia
foreach ($array as &$versione1) {
    foreach ($array as &$copia) {
        // codice
    }
}

In questo modo, utilizzando solo un “internal array pointer” (IAP), le modifiche vengono applicate solo ed esclusivamente quando le farai tu intenzionalmente. Questo, perché PHP 5 supporta un solo IAP per array: vedendo la sdoppiatura data dal doppio ciclo eviterai di avere modifiche indesiderate.

In PHP 7 invece, possiamo sfruttare molto più facilmente la duplicazione dell’array, poiché IAP non viene più usato:

$array = [1, 2, 3, 4, 5];
$riferimento = &$array;
foreach ($array as $elemento) {
    var_dump($elemento);
    $array[2] = 0;
}
/* Output precedente: 1, 2, 0, 4, 5 */
/* Nuovo output: 1, 2, 3, 4, 5 */

Continua a scoprire di più con questi articoli!

Lascia un commento

Questo sito potrebbe fare uso di cookie e siccome l'UE mi obbliga a fartelo presente, eccoti il classico banner dove puoi decidere come gestirli. Accetta Leggi di più