Progettazione Web - PHP
Cerca per parola chiave 0/0

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 giranel browser, sul dispositivo dell'utentesul server, prima che la risposta parta
che cosa vede l'utenteil codice sorgente, per interosolo il risultato: l'HTML prodotto
a che cosa può accederela pagina, le API del browserdatabase, file del server, altri servizi, segreti come le password del database
a che cosa serveinterattività, controlli immediati, aggiornamenti parzialidati 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

  1. Dal pannello di controllo di XAMPP si avviano Apache (e MySQL, quando servirà il database).
  2. I file del sito vanno nella cartella htdocs: C:\xampp\htdocs su Windows, /Applications/XAMPP/xamppfiles/htdocs su macOS, /opt/lampp/htdocs su Linux. Conviene una sottocartella per progetto.
  3. Le pagine si aprono dal server: http://localhost/progetto/pagina.php. Aprendo il file con un doppio clic l'indirizzo sarebbe file:///…: Apache non viene coinvolto, il PHP non viene eseguito e il browser mostra il codice, o niente.
  4. http://localhost/phpmyadmin apre phpMyAdmin, l'interfaccia web per gestire i database.
  5. Le impostazioni di PHP stanno nel file php.ini; gli errori di Apache e PHP finiscono nel file error.log della cartella dei log. Dopo aver modificato php.ini va riavviato Apache.

Che cosa succede a una richiesta .php

Browser riceve e mostra solo HTML server Apache .php? lo passa al modulo PHP PHP compila ed esegue saluto.php database, file GET /saluto.php HTML output
Il codice PHP non lascia mai il server: al browser arriva soltanto ciò che il programma ha scritto in uscita.

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 <?php e ?>; 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 (ECHO funziona), 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.
  • printf stampa secondo un formato e sprintf restituisce la stringa: %s stringa, %d intero, %.2f decimale con due cifre, %5d larghezza minima 5, %05d riempito di zeri, %b binario, %x esadecimale.

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 anche null).
  • 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 variabile static conserva il valore tra una chiamata e l'altra.
  • Esistono funzioni anonime (function () use ($x) { … }, che importano le variabili esterne con use) 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

FunzioneChe 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, rsortordina per valore e rinumera le chiavi
asort, arsort / ksort, krsortordina 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_unshiftaggiungere 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, mintrasformare, 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 da null;
  • 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;
  • trim per togliere gli spazi, is_numeric, ctype_digit, filter_var($email, FILTER_VALIDATE_EMAIL), preg_match('/regex/', $s) per il formato;
  • in_array o array_key_exists per verificare che un valore sia tra quelli ammessi, come le opzioni di un select;
  • 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.

ChiaveEsempio
REQUEST_METHODGET, POST
SERVER_NAME, HTTP_HOSTlocalhost, www.esempio.it
SCRIPT_NAME, PHP_SELF/progetto/ordine.php
QUERY_STRINGnome=Anna&corso=ing-inf
REMOTE_ADDRl'indirizzo IP del client (o del proxy davanti a lui)
HTTP_USER_AGENTil browser dichiarato dal client
HTTP_REFERERla pagina da cui arriva l'utente, se il browser la comunica
DOCUMENT_ROOTla 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

FunzioneUso
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, fcloselettura e scrittura riga per riga, per i file grandi
file_exists, is_readable, unlinkcontrollare, 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 class contiene 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 new e i membri si raggiungono con la freccia -> (senza $ davanti al nome della proprietà);
  • i membri static appartengono alla classe e si raggiungono con ::: self::$conteggio dall'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 abstract non si può istanziare: fa da modello, e i suoi metodi abstract vanno scritti dalle sottoclassi;
  • un'interface elenca solo metodi da implementare; una classe può implementarne quante vuole (implements A, B);
  • instanceof verifica 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:

LivelloEsempioEffetto
Parse erroruna parentesi o un punto e virgola mancantelo script non parte proprio, nemmeno le righe giuste
Fatal errorchiamare un metodo su null, una funzione inesistentelo script si interrompe in quel punto
Warninguna variabile non definita, un file da includere che mancamessaggio, ma lo script continua
Notice, Deprecatedfunzioni che spariranno nelle versioni futuresolo 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_ALL tutti, 0 nessuno;
  • 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_errors ed error_log: se scrivere i messaggi in un file di log, e in quale. In produzione è il posto dove andarli a leggere. Con la funzione error_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 ||.

Verifica