Caricamento di file in PHP
Scopri come gestire il caricamento di file in PHP con $_FILES e move_uploaded_file(), con validazione sicura e configurazione php.ini.
Il caricamento di file è un requisito comune nello sviluppo web. Che si tratti di un'immagine del profilo, di un documento PDF o di un'importazione CSV, consentire agli utenti di inviare file al server è una funzionalità fondamentale della maggior parte delle applicazioni. In PHP questo viene gestito dalla funzione move_uploaded_file() insieme all'array superglobale $_FILES.
Questo capitolo tratta l'intero flusso di caricamento: configurare il form HTML, leggere i metadati del file caricato da $_FILES, validarlo in modo sicuro e spostarlo nella destinazione finale. Vengono esaminati anche i codici di errore del caricamento e le impostazioni di configurazione PHP che controllano i limiti di upload.
Come funziona il caricamento di file in PHP
Il caricamento di un file avviene in tre fasi:
- Il browser invia il file in una richiesta POST
multipart/form-data. - PHP riceve il file, lo scrive in una posizione temporanea sul disco, quindi espone i dettagli nell'array
$_FILES. - Lo script valida il file e lo sposta dalla posizione temporanea a una permanente con
move_uploaded_file().
Il file temporaneo viene eliminato automaticamente al termine della richiesta se non viene spostato, quindi è necessario elaborarlo durante la stessa richiesta.
Il superglobale $_FILES
Quando viene caricato un file, le informazioni su di esso vengono memorizzate nell'array superglobale $_FILES. Per un campo del form denominato userfile, l'array contiene le seguenti chiavi:
$_FILES['userfile']['name']- Il nome originale del file caricato.$_FILES['userfile']['type']- Il tipo MIME del file caricato.$_FILES['userfile']['size']- La dimensione del file caricato in byte.$_FILES['userfile']['tmp_name']- La posizione temporanea del file caricato sul server.$_FILES['userfile']['error']- Un codice di errore che indica se si è verificato un problema durante il caricamento del file (vedi i codici di errore di seguito).
Passo 1: Il form HTML
L'attributo enctype del form deve essere impostato su multipart/form-data, e il metodo deve essere POST. Senza multipart/form-data, il browser invia solo il nome del file, non il suo contenuto, e $_FILES sarà vuoto.
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="userfile">
<input type="submit" value="Upload">
</form>Passo 2: Validare il caricamento
Non fidarsi mai di un file caricato. Prima di spostarlo, verificare tre cose: che il caricamento sia avvenuto con successo, che la dimensione rientri nel limite consentito e che il file sia davvero del tipo atteso.
Controllare il codice di errore
Ispezionare sempre prima $_FILES['userfile']['error']. PHP definisce costanti con nome per i valori possibili:
| Costante | Valore | Significato |
|---|---|---|
UPLOAD_ERR_OK | 0 | Nessun errore, il file è stato caricato correttamente. |
UPLOAD_ERR_INI_SIZE | 1 | Il file supera upload_max_filesize in php.ini. |
UPLOAD_ERR_FORM_SIZE | 2 | Il file supera il campo MAX_FILE_SIZE del form. |
UPLOAD_ERR_PARTIAL | 3 | Il file è stato caricato solo parzialmente. |
UPLOAD_ERR_NO_FILE | 4 | Nessun file è stato caricato. |
UPLOAD_ERR_NO_TMP_DIR | 6 | Manca una cartella temporanea. |
UPLOAD_ERR_CANT_WRITE | 7 | Impossibile scrivere il file su disco. |
Validare tipo e dimensione in modo sicuro
Non affidarsi a $_FILES['userfile']['type']. Quel valore è fornito dal browser e può essere facilmente falsificato da un attaccante. Invece, rilevare il tipo MIME reale dal contenuto del file con l'estensione finfo, e imporre un limite di dimensione autonomamente:
$file = $_FILES['userfile'];
// 1. Did the upload succeed?
if ($file['error'] !== UPLOAD_ERR_OK) {
exit("Upload failed with error code " . $file['error']);
}
// 2. Enforce a maximum size (2 MB here).
$maxBytes = 2 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
exit("File is too large.");
}
// 3. Detect the real MIME type, not the client-supplied one.
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
];
if (!isset($allowed[$mime])) {
exit("Only JPEG, PNG, and GIF images are allowed.");
}Passo 3: Spostare il file nella destinazione finale
Una volta validato il file, spostarlo con move_uploaded_file(). Accetta due argomenti: il percorso temporaneo ($_FILES['userfile']['tmp_name']) e il percorso di destinazione. Usare questa funzione invece di copy() o rename() è importante — verifica che il file sia stato un autentico upload HTTP, il che impedisce a un attaccante di ingannare lo script facendogli spostare un file arbitrario del server.
Generare il nome del file finale autonomamente invece di fidarsi del nome originale. Questo evita attacchi di directory traversal (un nome come ../../config.php) e la sovrascrittura di file esistenti:
$targetDir = "uploads/";
// Build a safe, unique file name; never trust the client's name.
$extension = $allowed[$mime];
$safeName = bin2hex(random_bytes(8)) . "." . $extension;
$targetFile = $targetDir . $safeName;
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
echo "The file was uploaded as " . $safeName;
} else {
echo "There was an error saving the file.";
}Per maggiori dettagli su questa funzione e sulla sua funzione complementare is_uploaded_file(), consultare i capitoli di riferimento move_uploaded_file() e is_uploaded_file().
Configurazione che influisce sui caricamenti
Alcune direttive di php.ini limitano silenziosamente ciò che lo script può ricevere. Se i caricamenti di file di grandi dimensioni falliscono anche quando il codice è corretto, verificare queste impostazioni:
file_uploads— deve essereOnaffinché i caricamenti funzionino.upload_max_filesize— la dimensione massima del singolo file che PHP accetta (default2M).post_max_size— la dimensione massima totale del corpo POST; deve essere maggiore diupload_max_filesize.max_file_uploads— il numero massimo di file in una singola richiesta.
Conclusione
Il caricamento di file in PHP è una parte fondamentale dello sviluppo web. Con l'array superglobale $_FILES e la funzione move_uploaded_file(), è possibile gestire i caricamenti in poche righe. La parte difficile è farlo in modo sicuro: controllare sempre il codice di errore del caricamento, imporre un limite di dimensione, rilevare il tipo MIME reale con finfo invece di fidarsi di $_FILES[...]['type'], e generare un nome file proprio in modo che il client non possa mai controllare dove il file viene salvato. Per elaborare il file salvato in seguito, consultare gestione dei file PHP e validazione dei form.