Nella lezione precedente di questa guida abbiamo visto il funzionamento delle due variabili superglobali $_GET e $_POST. In questa lezione vedremo, invece, come funziona la variabile superglobale $_FILES la quale viene utilizzata quale vettore per il trasferimento di file dal client verso il server. In altre parole, in questa lezione vedremo come gestire gli upload di files con PHP.
Configurare correttamente le opzioni relative agli upload
Prima di procedebe è bene ricordare al lettore che all’interno del file php.ini sono presenti diverse opzioni che riguardano l’upload di file alle quali è bene prestare attenzione. Se non avete accesso diretto al file php.ini vi ricordo che potete visualizzare le varie impostazioni della vostra installazione di PHP utilizzando la funzione phpinfo().
Di seguito le opzioni interessate:
- file_uploads: se settata su 0 (o "off") l’upload di file è disabilitato (di default è settata su 1 o su "on");
- upload_tmp_dir: è la directory temporanea che PHP utilizzerà per far transitare i file durante l’upload; se non è specificata il sistema utilizzerà una cartella di default;
- upload_max_filesize: indica la dimensione massima di file che è possibile caricare (di default 2M);
- max_file_uploads: numero massimo di file che è possibile caricare simultaneamente.
Oltre a questi ci sono altri parametri che influenzano gli upload di file, cioè:
- post_max_size: indica la dimensione massima dei dati che è possibile inviare attraverso il metodo POST (il suo valore dovrebbe essere maggiore di upload_max_filesize);
- memory_limit: imposta la dimensione massima di memoria che può essere occupata dall’esecuzione di uno script (nelle versioni recenti di PHP è settato a 128M). Per non interferire con gli upload, questo parametro dovrebbe essere maggiore di post_max_size.
Come detto, è bene prestare attenzione a questi parametri in quanto, molto spesso, i problemi o gli errori nelle operazioni di upload mediante PHP sono dovuti ad una loro cattiva configurazione.
Creare il modulo di upload
Per effettuare l’upload di un file abbiamo bisogno, prima di tutto, di predisporre un form HTML che consenta all’utente di selezionare un file dal suo computer e di inviarcelo mediante il metodo POST. Di seguito il codice HTMl del nostro form di upload:
<form enctype="multipart/form-data" action="upload.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="30000">
Invia questo file: <input name="userfile" type="file"></br>
<input type="submit" value="Invia File">
</form>
In merito al codice proposto qui sopra sono necessarie alcune osservazioni:
- abbiamo aggiunto al tag <form> l’attributo enctype con valore "multipart/form-data": questa accortezza è essenziale in quanto una sua eventuale omissione comprometterebbe il funzionamento del sistema di upload!
- abbiamo previsto un campo nascosto MAX_FILE_SIZE: questo campo hidden deve precedere il campo di immissione del file ed è utilizzato per specificare la dimensione massima (espressa in byte) per il file. E’bene precisare che questo campo è facoltativo ed un suo utilizzo, seppur consigliato, non è determinante. Altrettanto opportuno sottolineare come l’utilizzo di questo campo debba considerarsi "insicuro" in quanto il limite ivi espresso potrebbe essere facilmente aggirato (sempre meglio prevedere dei controlli lato server).
- la selezione del file è gestita medinate un input di tipo file il quale crea un pulsante che consente all’utente di selezionare un file locale navigando all’interno del proprio computer.
Il codice PHP che realizza l’upload
Come detto all’inizio diq uesta lezione, PHP ci offre la variabile superglobale $_FILES la quale è un vettore (un array) contenente tutte le informazioni sul file che viene caricato. Facciamo un paio di esempi per capire meglio come funziona la variabile $_FILES:
Per conoscere il nome del file scaricato useremo:
$_FILES['userfile']['name']
Per vedere il mime-type:
$_FILES['userfile']['type']
Per vedere le sue dimensioni:
$_FILES['userfile']['size']
ecc.
Vediamo ora il codice dello script upload.php il quale recuperà il file per poi metterlo nella cartella che noi abbiamo predisposto per ospitare i file uploadati:
<?php
// per prima cosa verifico che il file sia stato effettivamente caricato
if (!isset($_FILES['userfile']) || !is_uploaded_file($_FILES['userfile']['tmp_name'])) {
echo 'Non hai inviato nessun file...';
exit;
}
//percorso della cartella dove mettere i file caricati dagli utenti
$uploaddir = '/var/www/myupload/';
//Recupero il percorso temporaneo del file
$userfile_tmp = $_FILES['userfile']['tmp_name'];
//recupero il nome originale del file caricato
$userfile_name = $_FILES['userfile']['name'];
//copio il file dalla sua posizione temporanea alla mia cartella upload
if (move_uploaded_file($userfile_tmp, $uploaddir . $userfile_name)) {
//Se l'operazione è andata a buon fine...
echo 'File inviato con successo.';
}else{
//Se l'operazione è fallta...
echo 'Upload NON valido!';
}
?>
Lo script come al solito è ampiamente commentato in ogni passaggio e pertanto dovreste aver compreso tutti i passaggi svolti, tuttavia ritengo opportuno alcune precisazioni in merito a due funzioni native di PHP che assumono un ruolo centrale nei processi di upload:
- nella prima linea di codice, dopo aver verificato se la variabile $_FILES è settata, ho utilizzato is_uploaded_file() per verificare se effettivamente è stato trasferito un file;
- con la funzione move_uploaded_file verifico se il file temporaneo è stato spostato correttamente nella cartella di destinazione: nel primo caso la funzione restituisce TRUE, in caso contrario FALSE.
Tecniche per la validazione degli upload
Il codice proposto qui sopra è davvero minimale e, prima di essere messo in produzione, necessiterebbe di alcune implementazioni ulteriori. Vediamo, di seguito, alcuni codici per implementare dei controlli di sicurezza circa i file caricati dagli utenti.
Verificare se il file è stato caricato
Si tratta di un controllo essenziale ed è l’unico che abbiamo introdotto nel nostro esempio:
if (!isset($_FILES['userfile']) || !is_uploaded_file($_FILES['userfile']['tmp_name'])) {
echo 'Non hai inviato nessun file...';
exit;
}
Verificare che il file non sia troppo grande
Un controllo piuttosto comune consiste nel verificare se il file caricato dall’utente non sia superiore ad una dimensione massima:
// limito la dimensione massima a 4MB
if ($_FILES['userfile']['size'] > 4194304) {
echo 'Il file è troppo grande!';
exit;
}
Verificare che l’upload non sovrascriva altro file
Talvalta può essere utile verificare se il file inviato dall’utente mediante il modulo di upload sia già presente all’interno della nostra cartella di destinazione:
$target_file = '/var/www/myupload/' . $_FILES['userfile']['name'];
if (file_exists($target_file)) {
echo 'Il file esiste già';
exit;
}
Verificare l’estensione del file caricato
Un modo semplice per controllare il tipo di file caricato dall’utente consiste nell’effettuare una verifica sulla sua estensione:
$ext_ok = array('doc', 'docx', 'pdf');
$temp = explode('.', $_FILES['userfile']['name']);
$ext = end($temp);
if (!in_array($ext, $ext_ok)) {
echo 'Il file ha un estensione non ammessa!';
exit;
}
la verifica del tipo di file, è bene sottolinearlo, è molto impostante: senza un controllo del genere un attaccante potrebbe caricare sul nostro server dei codici malevoli che potrebbero compromettere la sicurezza del nostro sito web!
Verificare se il file è effettivamente un immagine
Un tipo di controllo molto comune consiste nel verificare se il tipo di file corrisponde a quello atteso. Se, ad esempio, abbiamo predisposto un modulo per il caricamento di immagini è buona norma verificare che il file inviato dall’utente sia effettivamente un file grafico:
$is_img = getimagesize($_FILES['userfile']['tmp_name']);
if (!$is_img) {
echo 'Puoi inviare solo immagini';
exit;
}