PHP
Il lato server: sintassi, array, form, file, classi ed errori
Finora tutto il codice girava nel browser. Da qui in avanti scriviamo programmi che girano sul server e producono la pagina che il browser riceverà. Il linguaggio è PHP, il più diffuso per questo compito: lo usano WordPress, Wikipedia e una larga parte del Web. Negli esempi trovi a sinistra i file PHP e a destra ciò che arriva al browser; la scheda HTML ricevuto mostra il codice che il server ha prodotto.
Il lato server
Client e server: chi fa cosa
| Lato client (JavaScript) | Lato server (PHP) | |
|---|---|---|
| dove gira | nel browser, sul dispositivo dell'utente | sul server, prima che la risposta parta |
| che cosa vede l'utente | il codice sorgente, per intero | solo il risultato: l'HTML prodotto |
| a che cosa può accedere | la pagina, le API del browser | database, file del server, altri servizi, segreti come le password del database |
| a che cosa serve | interattività, controlli immediati, aggiornamenti parziali | dati persistenti, utenti, pagamenti, tutto ciò che deve essere sicuro |
Le tecnologie lato server sono molte: PHP, Node.js (JavaScript sul server), Java (servlet, JSP, Spring), Python (Django, Flask), ASP.NET (C#), Ruby on Rails. Tutte lavorano insieme a un web server, il programma che riceve le richieste HTTP, restituisce i file statici (immagini, CSS, JavaScript), passa al linguaggio le richieste dinamiche, registra gli accessi nei log e gestisce HTTPS.
Una combinazione classica è LAMP: Linux come sistema operativo, Apache come web server, MySQL (o il suo gemello MariaDB) come database, PHP come linguaggio. Su Windows e macOS si usa XAMPP, un pacchetto che installa Apache, MariaDB, PHP e Perl in un colpo solo.
Lavorare con XAMPP
- Dal pannello di controllo di XAMPP si avviano Apache (e MySQL, quando servirà il database).
- I file del sito vanno nella cartella htdocs:
C:\xampp\htdocssu Windows,/Applications/XAMPP/xamppfiles/htdocssu macOS,/opt/lampp/htdocssu Linux. Conviene una sottocartella per progetto. - Le pagine si aprono dal server:
http://localhost/progetto/pagina.php. Aprendo il file con un doppio clic l'indirizzo sarebbefile:///…: Apache non viene coinvolto, il PHP non viene eseguito e il browser mostra il codice, o niente. http://localhost/phpmyadminapre phpMyAdmin, l'interfaccia web per gestire i database.- Le impostazioni di PHP stanno nel file
php.ini; gli errori di Apache e PHP finiscono nel fileerror.logdella cartella dei log. Dopo aver modificatophp.iniva riavviato Apache.
Che cosa succede a una richiesta .php
Il cuore di PHP è il motore Zend: a ogni richiesta compila lo script in un codice intermedio (che la cache OPcache conserva per le volte successive) e lo esegue. Tutto ciò che lo script stampa con echo, insieme all'HTML che sta fuori dai tag PHP, diventa il corpo della risposta. Finita la richiesta, le variabili spariscono: ogni richiesta riparte da zero.
Sintassi di base
Tag, istruzioni e commenti
- Il codice PHP sta tra
<?phpe?>; tutto ciò che è fuori viene inviato così com'è. In un file che contiene solo PHP il tag di chiusura si omette, per non inviare per sbaglio spazi o righe vuote finali. <?= espressione ?>è la scorciatoia di<?php echo espressione; ?>, comodissima dentro l'HTML.- Le istruzioni finiscono con il punto e virgola.
- I commenti sono
// …e# …su una riga,/* … */su più righe. - Le parole chiave e i nomi delle funzioni non distinguono maiuscole e minuscole (
ECHOfunziona), le variabili sì.
Variabili, tipi e costanti
Le variabili iniziano con $ e non si dichiarano: nascono alla prima assegnazione. Come JavaScript, PHP è a tipizzazione dinamica e converte i valori quando serve. I tipi principali sono bool, int, float, string, array, object e null. Per ispezionare un valore durante lo sviluppo si usano var_dump (tipo e valore) e print_r (più leggibile per gli array).
Le costanti si definiscono con define('NOME', valore) o con const NOME = valore;, si usano senza $ e non cambiano più. Per convenzione hanno nomi in maiuscolo. PHP ne offre molte già pronte, come PHP_EOL (il fine riga) e le costanti «magiche» __LINE__, __FILE__ e __DIR__ (la cartella dello script, utile per i percorsi).
Confronti. Come in JavaScript, == converte i tipi e === no. Da PHP 8 il confronto tra un numero e una stringa non numerica confronta come stringhe (0 == "a" è falso, mentre in PHP 7 era vero). Nel dubbio, usa ===.
Stringhe: virgolette, concatenazione e printf
- Tra apici singoli la stringa è letterale:
'Ciao $nome\n'resta esattamente così. - Tra virgolette doppie le variabili vengono sostituite dal loro valore (interpolazione) e le sequenze come
\n(a capo) e\t(tabulazione) interpretate. Per espressioni complesse si usano le graffe:"Voto: {$voti['pweb']}". - L'operatore di concatenazione è il punto
., e.=aggiunge in fondo. Il+è solo aritmetico. printfstampa secondo un formato esprintfrestituisce la stringa:%sstringa,%dintero,%.2fdecimale con due cifre,%5dlarghezza minima 5,%05driempito di zeri,%bbinario,%xesadecimale.
Controllo del flusso e funzioni
Condizioni e cicli
Le strutture sono quelle del C: if, elseif, else, switch, while, do … while, for, con break e continue. In più ci sono foreach per gli array, che vedremo tra poco, e da PHP 8 l'espressione match: un switch più rigoroso (confronta con ===), che restituisce un valore e non ha bisogno di break.
Quando il PHP produce HTML conviene la sintassi alternativa: i due punti al posto della graffa aperta e endif, endfor, endforeach, endwhile al posto di quella chiusa. In mezzo a tanto HTML si capisce subito che cosa si sta chiudendo.
include e require
Le parti comuni a tutte le pagine (intestazione, menu, piè di pagina, configurazione) si scrivono una volta sola e si inseriscono dove servono. include 'file.php' e require 'file.php' inseriscono ed eseguono il file in quel punto; la differenza è nel caso in cui il file manchi: include dà un warning e lo script continua, require dà un errore fatale e lo script si ferma. Le varianti include_once e require_once non includono due volte lo stesso file: si usano per i file che definiscono funzioni o classi.
Funzioni
- Si dichiarano con
function; parametri e valore restituito possono avere un tipo (int,float,string,bool,array, il nome di una classe; con?davanti ammettono anchenull). - I parametri possono avere un valore predefinito, e da PHP 8 si possono passare per nome:
area(altezza: 2, base: 10). - Gli argomenti si passano per valore, array compresi: la funzione lavora su una copia. Con
&davanti al parametro si passano per riferimento, e la funzione modifica l'originale. - Dentro una funzione non si vedono le variabili esterne, a differenza di JavaScript. Si possono richiamare con
global, ma è meglio passarle come parametri. Una variabilestaticconserva il valore tra una chiamata e l'altra. - Esistono funzioni anonime (
function () use ($x) { … }, che importano le variabili esterne conuse) e funzioni freccia (fn($x) => …, che le vedono automaticamente).
Gli array
Indicizzati e associativi
In PHP l'array è la struttura dati tuttofare: è una mappa ordinata che associa chiavi (interi o stringhe) a valori di qualsiasi tipo, altri array compresi. Con chiavi numeriche si comporta come una lista, con chiavi stringa come un dizionario. Si scrive con le quadre, […], o con la vecchia sintassi array(…); $a[] = x aggiunge in fondo.
Scorrere gli array con foreach
foreach ($array as $valore) scorre i valori; foreach ($array as $chiave => $valore) anche le chiavi. È il ciclo con cui si trasforma un array in HTML: righe di tabella, voci di menu, opzioni di un select.
Le funzioni per gli array
| Funzione | Che cosa fa |
|---|---|
count($a) | numero di elementi |
isset($a['k']), array_key_exists('k', $a) | la chiave esiste? (isset è falso anche se il valore è null) |
in_array($v, $a), array_search($v, $a) | il valore c'è? / con quale chiave? |
array_keys($a), array_values($a) | solo le chiavi / solo i valori, rinumerati da 0 |
unset($a['k']) | toglie un elemento (le altre chiavi non cambiano) |
sort, rsort | ordina per valore e rinumera le chiavi |
asort, arsort / ksort, krsort | ordina per valore / per chiave, mantenendo le associazioni |
usort($a, $confronto) | ordina con una funzione di confronto; l'operatore <=> restituisce -1, 0 o 1 |
array_push, array_pop, array_shift, array_unshift | aggiungere e togliere in fondo o in testa |
array_merge, array_slice, range(1, 10) | unire, estrarre una parte, creare una sequenza |
array_map, array_filter, array_sum, max, min | trasformare, filtrare, sommare, trovare estremi |
implode($sep, $a), explode($sep, $s) | da array a stringa e viceversa |
shuffle($a), array_rand($a) | mescolare, scegliere a caso |
Form e superglobali
$_GET e $_POST
Quando un form viene inviato, PHP raccoglie i dati in array associativi chiamati superglobali, visibili ovunque nello script: $_GET per i dati arrivati nella query string dell'indirizzo (form con method="get" o semplici link), $_POST per quelli arrivati nel corpo della richiesta (method="post"). La chiave è l'attributo name del campo, il valore è sempre una stringa. Se il nome finisce con [], come per un gruppo di caselle, PHP crea un array.
Ricorda la regola: GET per leggere (ricerche, filtri, pagine che si possono salvare nei preferiti), POST per modificare (iscrizioni, ordini, login), e per i dati che non devono finire nella cronologia.
I dati si possono passare anche con un normale link, costruendo la query string a mano. I valori vanno codificati con urlencode (o tutta la query con http_build_query), perché spazi, & e accenti hanno un significato speciale negli URL:
Controllare i dati ricevuti
Tutto ciò che arriva dal browser va considerato non affidabile: un campo può mancare, essere vuoto, contenere testo dove ci si aspetta un numero, o essere stato costruito apposta per attaccare il sito. La validazione nel browser non basta, perché chiunque può mandare una richiesta senza usare il nostro form. Gli strumenti principali:
isset($_POST['x']): il campo è arrivato ed è diverso danull;empty($x): vero se il valore è assente o «vuoto». Attenzione: per PHP anche la stringa'0'è vuota;$_POST['x'] ?? 'predefinito': il valore, oppure un predefinito se manca, senza avvisi;trimper togliere gli spazi,is_numeric,ctype_digit,filter_var($email, FILTER_VALIDATE_EMAIL),preg_match('/regex/', $s)per il formato;in_arrayoarray_key_existsper verificare che un valore sia tra quelli ammessi, come le opzioni di unselect;htmlspecialchars($s)quando si rimanda un dato nella pagina: trasforma<,>,&e le virgolette in entità, così nessun tag inserito dall'utente viene interpretato.
Uno schema molto comune è il form che invia a se stesso: la stessa pagina mostra il modulo e, se è arrivata una richiesta POST, controlla i dati. Se ci sono errori ripresenta il form con i valori già inseriti e i messaggi accanto ai campi sbagliati; se è tutto a posto salva e reindirizza a un'altra pagina con header('Location: …'), così ricaricando la pagina l'ordine non parte due volte (lo schema Post/Redirect/Get).
$_SERVER e le altre superglobali
$_SERVER contiene informazioni sulla richiesta e sull'ambiente. Le chiavi che iniziano con HTTP_ vengono dalle intestazioni inviate dal browser e quindi possono essere falsificate: vanno bene per le statistiche, non per le decisioni di sicurezza.
| Chiave | Esempio |
|---|---|
REQUEST_METHOD | GET, POST |
SERVER_NAME, HTTP_HOST | localhost, www.esempio.it |
SCRIPT_NAME, PHP_SELF | /progetto/ordine.php |
QUERY_STRING | nome=Anna&corso=ing-inf |
REMOTE_ADDR | l'indirizzo IP del client (o del proxy davanti a lui) |
HTTP_USER_AGENT | il browser dichiarato dal client |
HTTP_REFERER | la pagina da cui arriva l'utente, se il browser la comunica |
DOCUMENT_ROOT | la cartella radice del sito sul server |
Le superglobali complete sono $_GET, $_POST, $_REQUEST (le due precedenti più i cookie: meglio evitarla, non si sa da dove arriva il dato), $_FILES, $_COOKIE, $_SESSION, $_SERVER, $_ENV e $GLOBALS.
File: upload, lettura e scrittura
Caricare file
Per caricare un file servono tre cose nel form: method="post", enctype="multipart/form-data" (senza, arriverebbe solo il nome del file) e un <input type="file">. PHP salva il file in una cartella temporanea e ne mette i dati in $_FILES:
Il form
print_r($_FILES)
name e type li dichiara il browser, quindi non sono affidabili; tmp_name è il file temporaneo, che PHP cancella alla fine della richiesta se non lo si sposta con move_uploaded_file; error vale UPLOAD_ERR_OK (0) se tutto è andato bene, altrimenti indica il problema: UPLOAD_ERR_INI_SIZE (1, supera upload_max_filesize di php.ini), UPLOAD_ERR_FORM_SIZE (2, supera MAX_FILE_SIZE del form), UPLOAD_ERR_PARTIAL (3), UPLOAD_ERR_NO_FILE (4).
I limiti di dimensione si possono porre a tre livelli: il campo nascosto MAX_FILE_SIZE e un controllo JavaScript sono solo cortesie verso l'utente, aggirabili; quelli veri sono upload_max_filesize e post_max_size in php.ini, e il controllo nello script.
Perché tanta prudenza? Se lo script accettasse qualsiasi file con il nome originale, un attaccante potrebbe caricare shell.php nella cartella pubblica e poi aprirlo dal browser: il server eseguirebbe il suo codice. Per questo si controlla il contenuto, si sceglie un'estensione da un elenco chiuso e si genera un nome nuovo.
Leggere e scrivere file di testo
| Funzione | Uso |
|---|---|
file_get_contents($f) | legge tutto il file in una stringa (anche da un URL) |
file_put_contents($f, $testo) | scrive (sovrascrive); con FILE_APPEND aggiunge in fondo, con LOCK_EX blocca il file mentre scrive |
file($f) | legge il file in un array, una riga per elemento |
fopen, fgets, fwrite, feof, fclose | lettura e scrittura riga per riga, per i file grandi |
file_exists, is_readable, unlink | controllare, cancellare |
Un formato semplice e diffuso per i dati tabellari è il CSV: una riga per record, i campi separati da virgole o, nei fogli di calcolo italiani, da punti e virgola. Si legge con file ed explode (o con fgetcsv, che gestisce anche i campi tra virgolette):
Classi e oggetti
Classi, proprietà e metodi
La programmazione a oggetti in PHP somiglia molto a quella di Java:
- una
classcontiene proprietà e metodi, ciascuno con una visibilità:public(da ovunque),protected(dalla classe e dalle sottoclassi),private(solo dalla classe); - il costruttore si chiama
__construct; dentro i metodi l'oggetto corrente è$this; - gli oggetti si creano con
newe i membri si raggiungono con la freccia->(senza$davanti al nome della proprietà); - i membri
staticappartengono alla classe e si raggiungono con:::self::$conteggiodall'interno,Artista::quanti()da fuori. Lo stesso vale per le costanti di classe (const); - da PHP 8 il costruttore può dichiarare e assegnare le proprietà in un colpo solo (promozione), mettendo la visibilità davanti ai parametri.
Senza la promozione, la stessa classe si scrive nel modo tradizionale, che troverai nel codice meno recente:
Ereditarietà, classi astratte e interfacce
class Dipinto extends Opera: la sottoclasse eredita proprietà e metodi e può ridefinirli;parent::richiama la versione della classe madre, costruttore compreso. PHP ammette una sola classe madre;- una classe
abstractnon si può istanziare: fa da modello, e i suoi metodiabstractvanno scritti dalle sottoclassi; - un'
interfaceelenca solo metodi da implementare; una classe può implementarne quante vuole (implements A, B); instanceofverifica se un oggetto è di una certa classe o interfaccia. Trattare oggetti diversi attraverso lo stesso metodo, che ognuno esegue a modo suo, è il polimorfismo.
Gli oggetti vivono una richiesta. A differenza di un programma Java, che resta in memoria, uno script PHP termina a ogni richiesta e i suoi oggetti spariscono. Per ritrovarli alla richiesta successiva bisogna salvarli da qualche parte: in una sessione, in un file, in un database. È il tema del prossimo modulo.
Errori ed eccezioni
Tipi di errore e come mostrarli
Gli errori sono di due famiglie: quelli previsti (un dato sbagliato, il database che non risponde, un file mancante), che il programma deve gestire con garbo, e quelli di programmazione (una variabile mai definita, una funzione scritta male), che vanno corretti. PHP li segnala con livelli di gravità diversi:
| Livello | Esempio | Effetto |
|---|---|---|
| Parse error | una parentesi o un punto e virgola mancante | lo script non parte proprio, nemmeno le righe giuste |
| Fatal error | chiamare un metodo su null, una funzione inesistente | lo script si interrompe in quel punto |
| Warning | una variabile non definita, un file da includere che manca | messaggio, ma lo script continua |
| Notice, Deprecated | funzioni che spariranno nelle versioni future | solo un avviso |
Tre impostazioni decidono che cosa succede ai messaggi. Si scrivono in php.ini oppure, per un singolo script, con ini_set e error_reporting:
error_reporting: quali livelli segnalare.E_ALLtutti,0nessuno;display_errors: se mostrare i messaggi nella pagina. On durante lo sviluppo, per vedere subito i problemi; Off in produzione, perché i messaggi rivelano percorsi, nomi di file e struttura del codice;log_errorsederror_log: se scrivere i messaggi in un file di log, e in quale. In produzione è il posto dove andarli a leggere. Con la funzioneerror_log('messaggio')ci si scrive anche a mano.
try, catch e finally
Gli errori gravi e le situazioni anomale previste dal programmatore si gestiscono con le eccezioni, come in Java e JavaScript: throw lancia un oggetto eccezione, try delimita il codice a rischio, uno o più catch intercettano le eccezioni del tipo indicato, finally viene eseguito in ogni caso. Se nessuno la intercetta, l'eccezione ferma lo script con un Uncaught exception.
Tutto ciò che si può lanciare implementa l'interfaccia Throwable, da cui discendono due rami: Exception, per gli errori dell'applicazione (InvalidArgumentException, RuntimeException, PDOException…), ed Error, per gli errori interni del linguaggio (TypeError, DivisionByZeroError, la chiamata di un metodo su null…). Un catch (Exception $e) non intercetta un Error; catch (Throwable $e) intercetta tutto. I parse error invece non si possono intercettare: il file non è mai partito.
Gestori personalizzati
Un sito vero non deve mai mostrare all'utente un messaggio tecnico. Con set_exception_handler si registra una funzione chiamata per ogni eccezione non intercettata, e con set_error_handler una per i warning e gli avvisi. Il gestore registra nel log tutti i dettagli utili allo sviluppatore (messaggio, file, riga, momento) e mostra all'utente una pagina generica.
and, or e &&, ||. PHP ha due versioni degli operatori logici con precedenze diverse. and e or hanno precedenza più bassa dell'assegnazione: $f = false or true; assegna false a $f e poi calcola l'or a vuoto, mentre $e = false || true; assegna true. Entrambe le coppie sono «a corto circuito»: in false && foo() la funzione non viene chiamata. Usa sempre && e ||.