Strutture di controllo
Scope delle variabili
Prima di incominciare a parlare di strutture di controllo voglio chiarire una piccola cosa riguardante le variabili, quello che in gergo è chiamato scope. Questo fantomatico scope delle variabili altri non è che il contesto in cui la variabile è visibile al programmatore che può quindi leggere e modificare il suo contenuto. In generale le variabili hanno un solo scope che si espande rendendola visibile in tutto lo script e file inclusi. Sono un' eccezione le funzioni. Le variabili definite all'esterno di una funzione non sono viste al suo interno e tutte le variabili definite al suo interno muoiono quando questa termina. Esistono modi più o meno avanzati per modificare il comportamento del PHP nei confronti delle variabili ma ne parleremo più avanti.
<?php
/* definisce la variabile $k */
$k = 1000.27;
echo $k; // stampa il valore di $k
function stampa_k()
{
echo $k; // non stampa niente.
$z = 9;
echo $z; // stampa $z
}
stampa_k();
?>
In C era possibile tramite le parentesi graffe creare nuovi blocchi di istruzioni con scope diverso, ridefinire nuovamente una variabile e utilizzarla senza che quella precedente venisse modificata. Questo giochino in PHP non è possibile visto che lo scope viene propagato anche dentro a blocchi più interni e varie strutture di controllo.
<?php
$a = '1';
{
echo $a; // stampa 1
{
$a = 7; // ora $a vale 7
}
}
echo $a; // stampa 7
?>
Include e include_once
Include e include_once sono due funzioni molto utili che vi permettono di dividere in più file il vostro codice. Il comportamento di questa funzione è molto simile alla direttiva del precompilatore C #include. Il file indicato tra parentesi è incluso ed eventualmente valutato. Tutto le variabili, funzioni e oggetti contenuti nel file diventeranno automaticamente disponibili dalla riga in cui la funzione è chiamata. Se il file non viene trovato il PHP genera in automatico un warning e continua con l'esecuzione della pagina. Ogni volta che la funzione viene chiamata il PHP entra in quella che è chiamata la modalità HTML quindi tutto il codice viene interpretato come HTML e stampato direttamente nella pagina HTML per poi ritornare in modalità PHP alla fine del file. Se volete che il codice venga eseguito dovrete ricordavi di racchiudere il codice tra i tag di apertura e chiusura. Ricordate che se usate la include all'interno di una funzione tutto il suo contenuto sarà disponibile solo all'interno della funzione e verrà cancellato alla morte della stessa.
<?php
### File variabile-a.php
$a = 10;
?>
<?php
### File variabile-b.php
$b = 99;
?>
<?php
### File funzioni.php
echo $a; // non stampa niente
include('variabile-a.php');
echo $a; // viene stampato 10
function funzione_A()
{
include('variabile-b.php');
echo $b; // stampa 99;
}
funzione_A();
echo $b; // non stampa niente
?>
Il funzionamento di include_once, come suggerisce il nome stesso, è simile a quello della include con la sola differenza che il file viene incluso soltanto una volta. Questa funzione è particolarmente utile quando si vuole limitare l'inclusione di un file per evitare conflitti con rifinizione di variabili e funzioni all'interno del file.
<?php
### file incluso.php
$x = 12.23;
?>
<?php
### File principale.php
include_once('incluso.php');
echo $x++; // stampa 12.23
include_once('incluso.php');
echo ++$x; // stampa 14.23
?>
Require e require_once
Il funzionamento di require e require_once è del tutto simile alle loro contro parti include. La sola differenza che li distingue è dovuta al fatto che quando il file non viene trovato la require genera un errore terminando l'esecuzione della pagina. È utile usare questa funzione al posto della include quando il codice che risiede nella pagina richiesta è di vitale importanza all'esecuzione dello script, come ad esempio un file in cui sono memorizzate delle password.
<?php
/* richiede un file HTML */
require('file-richiesto.html');
/* genera un' errore perché il file non esiste terminando cosi
l'esecuzione della pagina */
require_once('/percorso/sbagliato\al/file.password');
?>
Per concludere la parte che riguarda include e require volevo spiegare come queste funzioni aprono i vari file. Il parametro in ingresso come abbiamo già visto è il nome del file. Questa stringa può contenere semplicemente il nome del file o altrimenti il nome con un percorso completo oppure relativo della cartella in cui si trova. Quando il percorso non viene definito nella stringa il PHP procede alla ricerca del file in un elenco di directory separate dal simbolo ':' che è memorizzato nella variabile include_path. A volte è utile poter modificare l'ordine oppure aggiungere delle directory a questa variabile in modo da non dover scrivere ogni volta il percorso. Per chi volesse approfondire consiglio di dare un' occhiata alla funzione ini_set(), oppure nelle versioni più recenti set_include_path.
<?php /* aggiunge /lib/scripts all'include path */ set_include_path(get_include_path() . ':' . '/lib/scripts'); ?>
If... Elseif... Else
Eccoci infine arrivati alla struttura di controllo più importante e più usata. Grazie a questa struttura potete selezionare quali frammenti di codice verranno eseguiti. La sintassi della struttura If in PHP è identica a quella del C. If valuta un' espressione che deve ritornare TRUE o FALSE. Se il valore ritornato è TRUE il frammento di codice viene eseguito altrimenti viene ignorato e il programma continua cosi per il suo flusso naturale. Se il risultato dell'espressione data in pasto alla If non appartiene al tipo boolean questo viene convertito seguendo alcune semplici regole. If considera FALSE i seguenti valori:
- Il valore 0 di un integer.
- Il valore 0.0 di un float.
- La stringa vuota. La stringa '0'.
- Un array con zero elementi.
- Un oggetto con zero variabili.
- Il valore speciale NULL.
- Variabili non esistenti.
In generale quindi quando un valore non è tra quelli riportati qui sopra viene considerato come TRUE. Un caso a parte sono i tipi resource che vengono considerati TRUE in ogni caso.
<?php
/* esempio di istruzione if */
if ($a >= 10) $a++;
/* alcuni esempi con variabili di tipo diverso dalla boolean */
$bfloat = 3.14;
$bint = 0;
$bstr = '1';
if ($bfloat) $bfloat++; // questa istruzione viene eseguita
if ($bint) $bint = $bint + 9; // questa istruzione non è eseguita
if ($bstr) { /* queste istruzioni vengono eseguite. */
$bstr = $bint . ' ciao ';
$bstr .= $bfloat;
echo $bstr;
}
?>
Spesso può capitare di dover scegliere tra due alternative, due possibili strade. Ad esempio sommare 4 ad una variabile se questa è minore di 40 altrimenti sommargli 1. Con un solo if ci risulta impossibile ma grazie ad Else potete estendere If e riuscire cosi a risolvere il nostro problema. L'istruzione else esegue infatti un determinato frammento di codice se e solo se l'espressione del If a cui è associata ha dato come risultato FALSE. Da notare come quando ci si trova di fronte a problemi di questo tipo può essere utile usare l'operatore ternario di scelta condizionata.
<?php
/* esempio di if/else */
if ($a < 40){
$a += 4; // finché $a è minore di 40 somma 4
} else {
$a +=1; // quando si supera 40 si somma solo 1
}
/* stesso esempio con l'operatore ternario */
$a += ($a < 40) ? 4 : 1;
?>
Ora che avete risolto il problema delle due alternative è possibile complicarlo un po'. Supponete quindi di avere 4 alternative tra cui scegliere e 4 frammenti di codice diverso da eseguire, uno per ogni alternativa possibile. Utilizzando i soli If ed else non riuscireste a risolvere il problema facilmente. È proprio qui che Elseif vi viene in aiuto. Questo costrutto molto simile ad else esegue un frammento di codice se è solo se quello della if non è stato eseguito inoltre vi permette di imporre nuove condizioni. Notate che non esistono limitazioni in quando a numeri di elseif possono essere associati ad un if. Elseif ed Else possano essere usati contemporaneamente ma l'else deve sempre essere uno solo e l'ultimo della lista.
<?php
/* esempio di if/elsefi/else */
$ris = cos(1) * 10;
if (($ris >= 0) && ($ris < 1)){
echo "tra 0 e 1";
}elseif (($ris >= 1 ) && ($ris < 10)){
echo "tra 1 e 10";
}elseif (($ris >= 10 ) && ($ris < 11)){
echo "tra 10 e 11";
}else{
echo "altri valori";
}
/* quale stringa viene stampata ? */
?>
Quando usate If/elseif ed else fate sempre particolarmente attenzione ai vari simboli di maggiore, minore e le loro contro parti con l'uguale. Spesso si fanno errori come includere lo stesso valore in due alternative differenti. Questo porta ad errori difficili da scovare e comportamenti del codice completamente a caso.
Iterazione
Dal punto di vista puramente teorico si dovrebbe tratatre prima il concetto di ricorsione e poi quello di iterazione, ma la ricorsione è più difficile da comprendere quindi ne parleremo più tardi.
L'iterazione nel PHP viene effettuata tramite strutture come for, foreach e while che vi permettono di ripetere un certo numero di volte un frammento di codice. Ognuna delle strutture che ho citato ha il suoi pregi e difetti e vengono usate in campi diversi. Come già detto il numero di cicli è a discrezione del programmatore e non è detto che debba per forza essere un numero finito. Questo può essere 5, 10 o 0, spesso poi può non interessarvi affatto il numero di cicli e quindi potete andare avanti fino all'infinito. In generale un ciclo è composto da quattro punti. Una condizione iniziale da cui partire. Un invariante o condizione di permanenza nel ciclo che ci indica fino a quando rimanere all'interno del ciclo. Una progressione che nella maggior parte dei casi è una variabile incrementata di 1. In fine ci sono l'istruzione da eseguire.
For
Incominciamo le strutture iterative vedendo per primo il For. Questo costrutto è senza dubbio il più usato perché a differenza di while e foreach è molto più espressivo. Permette infatti di dichiarare una variabile contatore e di inizializzarla, dichiarare l'invariante del ciclo e la progressione. Vediamo subito un esempio per capire come usarla e come è composta.
<?php
/* scrive 100 volte Ciao! */
for ($i = 0; $i < 100; $i++)
{
echo "Ciao!\n";
}
?>
Come vedete nel ciclo qui sopra ci sono tutte e 4 le parti fondamentali. Prima di tutto vine definita la variabile $i che è inizializzata a 0 (condizione iniziale). Dopo il primo punto e virgola viene indicato al For fino a quando deve continuare (invariante), dopo di che si inserisce la progressione che la variabile $i deve avere, in questo caso ad ogni ciclo viene incrementata di uno. In fine tra parentesi graffe c'è il frammento di codice da ripetere.
L'unica parte fondamentale del for è quella compresa tra parentesi graffe. Le altre possono essere omesse a piacere fornendo vari effetti. Ad esempio omettendo l'invariante il ciclo andrà avanti all'infinito senza mai uscire. Potrebbe capitare di dover definire la condizione di partenza fuori dal ciclo in questo caso il primo punto sarebbe superfluo, inoltre la progressione può essere effettuata anche tramite una normale istruzione all'interno del codice che viene ripetuto.
<?php
/* ciclo infinito */
for (;;)
{
echo "ripetimi per sempre.";
}
/* altro esempio di ciclo */
$y = funzione($x);
for (;$y < funzione($x + 10); $x += $delta)
{
funzione_che_fa_qualcosa($x);
}
?>
Do... While
While è senza dubbio il tipo di ciclo più facile da usare che esista in PHP. Di questa struttura ne esistono due varianti. La prima, quella con while e la clausola do, esegue almeno una volta le istruzioni del ciclo. La seconda senza la clausola do può saltare direttamente le istruzioni e andare oltre. Questo perché nel primo caso l'invariante viene controllato ogni volta che si terina il ciclo mentre nel secondo ogni volta che il ciclo comincia. In questo tipo di ciclo si indica sempre e solo la condizione di permanenza lasciando al programmatore tutto il resto.
<?php
/* esempio di ciclo con do.. while */
$x = 75;
do {
echo "la variabile \$x vale: $x";
$x++;
} while ($x < 100);
/* esempio di ciclo con solo while */
while ($x-- > 0)
{
echo "!";
}
?>
Come vedete l'espressione che permette di rimanere nel ciclo viene controllata, come in un if, solo all'inizio o alla fine del ciclo stesso, in questo modo il suo valore può variare anche più volte durante l'esecuzione delle varie istruzioni senza far interrompere il tutto. Questi cicli sono molto usati per fare input output su file oppure per navigare array di cui non si conosce la dimensione come ad esempio il risultato di una query.
Foreach
Foreach è una simpatica struttura di controllo ereditata dal Perl che vi permette di attraversare un' intero array senza preoccuparvi di contatori e condizioni. Ne esistono due versioni che sembrano simili ma sono profondamente diverse. La prima associa ad ogni ciclo il valore dell'array ad una variabile. La seconda associa anche il nome della chiave dell'elemento corrente. Quando si usa foreach si deve fare attenzione perché quando viene usata questa struttura il PHP crea una copia del array e opera su quello. Questo può essere sia un bene che un male. Quando si ha a che fare con array molto grandi si rischia di rallentare molto lo script, però con array di medie dimensioni questo metodo e più veloce degli altri e quindi consigliabile usarlo. Vediamo qualche esempio.
<?php
$array_a = array(1, 2, 3, 4);
$array_b = array('a', 'b', 'c', 'd');
/* Naviga un array stampando i valori contenuti */
foreach ($array_a as $elem)
{
echo "$elem\n";
}
/* Naviga un array stampando i valori contenuti e anche le chiavi dei vari elementi */
foreach ($array_b as $key => $elem)
{
echo "elem vale -> $elem \nKey vale -> $key";
}
?>
Spesso può essere utile usare la seconda forma di foreach che fornisce la chiave per accedere direttamente all'array vero e proprio senza dover usare la copia. Da notare anche che il ciclo foreach inizia sempre dalla prima locazione dell'array e non c'è altro modo e la progressione è sempre 1.
Continue
La struttura di controllo continue è di una semplicità disarmante e non mi soffermerò molto su di essa. Il suo unico scopo è quello di interrompere il ciclo corrente di una struttura iterativa come il for o un while e riprendere dal ciclo successivo. In questo modo potete saltare alcuni elementi del ciclo senza interrompere il ciclo stesso. Ecco un piccolo esempio.
<?php
/* Ciclo che fa qualcosa solo ai numeri primi tra 0 e 100 */
for ($i=0; $i<100; $i++)
{
if (!isprime($i)) continue;
fa_qualcosa_ai_numeri_primi($i);
}
?>
Nell'esempio qui sopra un if controlla se la variabile contatore è un numero primo. Se si il ciclo continua normalmente chiamando la funzione mentre per numeri non primi il ciclo viene interrotto e tutto riprende con il numero successivo.
Break
La break interrompe il flusso di una struttura di controllo facendola uscire dal suo flusso naturale e facendo continuare lo script per la sua strada. La Break può anche accettare in ingresso un parametro numerico che indica quante strutture deve interrompere. Più precisamente se avessimo 4 for uno dentro l'altro usando break 4 usciremo da tutti e 4 i for. Anche se comodo utilizzare il parametro della break è un' abitudine pericolosa che rende il codice illeggibile e che sconsiglio.
<?php
/* esempio di 3 for in cui sono usate delle istruzioni break */
for ($i=0; $i<10; $i++)
{
for ($j=0; $j<100; $j++)
{
for ($k=0; $k<3; $k++)
{
// se $i $j e $k sono uguali allora salta il ciclo corrente
if (($i==$j) && ($j==$k)) break;
}
if ($j=10) break 2; // salta il ciclo corrente e anche anche quello superiore
}
}
?>
Return
Se la break interrompe delle strutture di controllo la return interrompe funzioni e script. Anche return può accettare un parametro aggiuntivo che viene detto valore di ritorno. Come vedremo più avanti return viene usato dalle funzioni che ritornano dei valori. Non è detto che il parametro aggiuntivo di una return debba per forza essere una costante numerica, è possibile ritornare qualsiasi tipo di dati presente in PHP.
Essendo la include una funzione è possibile eguagliarla ad una variabile quando viene chiamata. Se il file incluso contiene un return al suo interno che con un parametro qualsiasi questo viene messo dentro alla nostra variabile.
<?php
### file inizializza.php
/* esempio di funzione. quando si giunge al termine il return ritorna il controllo
di flusso al ciclo */
function funzione()
{
static $contatore;
return ++$contatore;
}
/* ciclo for che chiama 100 volte la funzione qui sopra */
for ($i = 0; $i < 100; $i++)
{
echo funzione();
}
return 'ciao';
?>
<?php
### file principale.php
$ris = include('inizializza.php');
if ($ris == 'ciao') echo "ok";
?>
Switch
Come abbiamo visto con If e le sue clausole è possibile scegliere tra più alternative, ma quando queste diventano molte può essere scomodo usare ad esempio 20 if e elseif. Provate a pensare a quanto diventerebbe complicato voler visualizzare un output diverso per ogni faccia di un dado. In questo caso la struttura switch diventerà la nostra più cara amica. La switch è una struttura che riceve in ingresso una variabile e manda in esecuzione uno o più frammenti di codice a seconda del valore di questa variabile. vediamo subito un esempio:
<?php
/* genera un numero a caso tra 1 e 6 */
$random = rand(1,6);
/* Stampa uno dei seguenti messaggi a seconda del numero generato */
switch ($random)
{
case 1:
echo 'uno';
break;
case 2:
echo 'pippo';
break;
case 3:
echo 'fiore';
break;
case 4:
echo 'nano';
break;
default:
echo 'elfo';
}
?>
È meglio chiarire subito il funzionamento della switch per evitare errori futuri. La switch incomincia a cercare dall'alto il valore a cui corrisponde la variabile. Una volta incontrato incomincia ad eseguire le varie istruzioni fino a che non viene chiusa la struttura. Se ad esempio il valore della variabile fosse '2' tutti i blocchi inferiori al due sarebbero stati eseguiti compresi quelli per il 3,4 e il caso di default. Quello che ho appena detto però non corrisponde esattamente a quanto succede nell'esempio qui sopra perché al termine di ogni blocco è stato inserito un break che chiude la struttura di controllo evitando l'esecuzione degli altri blocchi.
La clausola di default viene usata al termine della struttura switch per indicare tutti gli altri possibili valori che la variabile può assumere. Si tratta di una clausola obbligatori per switch e va sempre inserita.
Come ultima cosa è bene sapere che le varie clausole di switch in PHP possono essere anche delle stringhe a differenza del C in cui è possibile usare solo valori numerici.
Sintassi alternativa
Come abbiamo già visto più volte PHP vi concede grande libertà in quanto a stile di scrittura del codice. Fino ad ora ho sempre usato negli esempi uno stile del tutto identico a quello del C, racchiudendo tutto il codice tra parentesi graffe. PHP vi mette però a disposizione anche una sintassi alternativa che può come non può piacere. Si deve per prima cosa sostituire la parentesi graffa di apertura con il simbolo di due punti : mentre la parentesi di chiusura con la parola chiave endif; endfor; e cosi via.
<?php /* un classico if ma con sintassi alternativa */ if ($a == 1): $a = $a + 1; $a .= 'Ciao'; endif; /* ecco un esempio di for con una sintassi alternativa */ for ($i = 0; $i < 7; $i++): echo "trallalero!"; endfor; ?>
Non sono un grande appassionato di questo stile e non lo consiglierei a nessuno. Secondo la mia opinione è meno leggibile di quello standard e meno supportato da editor vari rendendo ad altri difficile la modifica e la comprensione di quello che state scrivendo.