Introduzione
Quando si parla di form in PHP, spesso l’attenzione si concentra sui campi testuali, sulle email o sulle password. In realtà, uno dei casi più delicati e utili da gestire è il caricamento dei file: immagini del profilo, allegati, documenti, curriculum, screenshot, e così via.
La validazione dei file upload è un tema fondamentale perché un file inviato da un form non è mai affidabile “per definizione”. Il server deve controllare tutto: dimensione, tipo, estensione, errori di upload e destinazione finale. Se questi controlli sono assenti o superficiali, si rischiano problemi di sicurezza, file corrotti, overwrite di file esistenti e comportamenti imprevedibili.
In questo tutorial vedremo un esempio pratico e completo di validazione del caricamento file in PHP, con una struttura semplice ma già adatta a progetti reali. L’obiettivo è imparare a costruire un form sicuro e a gestire correttamente il file lato server.
Codice completo
<?php
// upload.php
$errors = [];
$success = ´´;
if ($_SERVER[´REQUEST_METHOD´] === ´POST´) {
// Verifica che il file sia stato inviato correttamente
if (!isset($_FILES[´documento´])) {
$errors[] = ´Nessun file ricevuto.´;
} else {
$file = $_FILES[´documento´];
// Controllo errori PHP dell´upload
if ($file[´error´] !== UPLOAD_ERR_OK) {
$errors[] = ´Errore durante il caricamento del file.´;
} else {
// Limite dimensione: 2 MB
$maxSize = 2 * 1024 * 1024;
if ($file[´size´] > $maxSize) {
$errors[] = ´Il file supera la dimensione massima consentita di 2 MB.´;
}
// Estensioni consentite
$allowedExtensions = [´pdf´, ´jpg´, ´jpeg´, ´png´];
$originalName = $file[´name´];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions, true)) {
$errors[] = ´Formato file non consentito. Usa PDF, JPG o PNG.´;
}
// Controllo MIME reale del file
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file[´tmp_name´]);
$allowedMimeTypes = [
´application/pdf´,
´image/jpeg´,
´image/png´
];
if (!in_array($mimeType, $allowedMimeTypes, true)) {
$errors[] = ´Il tipo MIME del file non è valido.´;
}
// Se non ci sono errori, sposta il file in una cartella sicura
if (empty($errors)) {
$uploadDir = __DIR__ . ´/uploads/´;
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Nome file sicuro e univoco
$safeName = bin2hex(random_bytes(16)) . ´.´ . $extension;
$destination = $uploadDir . $safeName;
if (move_uploaded_file($file[´tmp_name´], $destination)) {
$success = ´File caricato con successo: ´ . htmlspecialchars($safeName);
} else {
$errors[] = ´Impossibile salvare il file caricato.´;
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload file sicuro</title>
</head>
<body>
<h1>Caricamento file con validazione</h1>
<?php if ($success): ?>
<p><strong><?= $success ?></strong></p>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<ul>
<?php foreach ($errors as $error): ?>
<li><?= htmlspecialchars($error) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<form action="" method="post" enctype="multipart/form-data">
<p>
<label for="documento">Seleziona un file:</label><br>
<input type="file" name="documento" id="documento" required>
</p>
<p>
<button type="submit">Carica file</button>
</p>
</form>
</body>
</html> Spiegazione
Il punto di partenza è il form HTML. Per inviare file, il form deve usare method="post" e soprattutto enctype="multipart/form-data". Senza questo attributo, il browser non invierà correttamente il contenuto del file.
1. Controllo della richiesta
Il codice verifica che la richiesta sia stata fatta tramite POST. Questo evita di eseguire la logica di validazione quando la pagina viene semplicemente aperta nel browser.
2. Lettura di $_FILES
I file inviati da un form non si trovano in $_POST, ma in $_FILES. Ogni file contiene informazioni utili:
- name: nome originale del file sul computer dell’utente
- type: tipo MIME dichiarato dal browser, non sempre affidabile
- tmp_name: percorso temporaneo sul server
- error: codice di errore dell’upload
- size: dimensione in byte
3. Gestione degli errori di upload
PHP assegna un codice a ogni possibile problema. Il più importante da controllare è UPLOAD_ERR_OK, che indica che il caricamento è andato a buon fine. Se il valore è diverso, significa che c’è stato un errore: file troppo grande, upload interrotto, nessun file inviato o limite del server superato.
4. Validazione della dimensione
Nel nostro esempio abbiamo imposto un limite di 2 MB. Questa verifica è importante sia per motivi di sicurezza sia per evitare upload inutilmente pesanti. In un progetto reale puoi adattare il limite in base al tipo di file accettato.
5. Controllo dell’estensione
L’estensione viene estratta con pathinfo(). Anche se non basta da sola per identificare il file, è utile come prima barriera. Nel codice accettiamo solo pdf, jpg, jpeg e png.
6. Controllo MIME reale
Questo è uno degli aspetti più importanti. Il MIME dichiarato dal browser può essere falsificato, quindi conviene verificare il contenuto reale del file con finfo. In questo modo il server controlla se il file è davvero un PDF o un’immagine JPEG/PNG.
7. Nome file sicuro
Non bisogna mai salvare il file usando direttamente il nome originale fornito dall’utente. Un nome come miofile.php o ../../test.txt può creare problemi di sicurezza o conflitti. Per questo generiamo un nome casuale con random_bytes() e lo convertiamo in stringa esadecimale con bin2hex().
8. Salvataggio con move_uploaded_file()
La funzione move_uploaded_file() è la scelta corretta per spostare un file caricato via HTTP dalla cartella temporanea alla destinazione finale. È più sicura di un semplice rename() in questo contesto, perché verifica che il file provenga davvero da un upload.
Best practice
- Valida sempre lato server, anche se hai già limitazioni lato client con
accepto JavaScript. - Non fidarti del nome originale del file: genera sempre un nome nuovo e sicuro.
- Controlla sia estensione sia MIME: una sola verifica non è sufficiente.
- Imposta limiti di dimensione realistici in base al tuo caso d’uso.
- Archivia i file fuori dalla web root quando possibile, soprattutto se contengono dati sensibili.
- Rendi la cartella di upload non eseguibile, per evitare l’esecuzione accidentale di script caricati.
- Mostra messaggi di errore chiari ma non troppo tecnici, così l’utente capisce cosa correggere.
- Sanitizza sempre l’output con
htmlspecialchars()quando mostri dati provenienti dall’utente.
Riepilogo
La validazione del caricamento file in PHP è un caso pratico molto comune e, allo stesso tempo, una delle aree più sensibili della gestione dei form. Un upload gestito male può trasformarsi in un problema di sicurezza o in un bug difficile da diagnosticare.
Con una strategia corretta puoi rendere il processo robusto: controlli l’errore dell’upload, limiti la dimensione, verifichi estensione e MIME, assegni un nome sicuro e salvi il file solo se tutti i controlli sono superati. Questo approccio è semplice da mantenere e adatto a progetti professionali.
Se vuoi portare questa logica a un livello superiore, il passo successivo è integrare la validazione degli upload con autenticazione utente, controllo permessi e gestione sicura dei percorsi.
Approfondisci con risorse ufficiali
- PHP Manual - Handling file uploads: documentazione ufficiale sulla gestione degli upload.
- PHP Manual - move_uploaded_file(): dettagli sulla funzione consigliata per spostare i file caricati.
- PHP Manual - finfo: informazioni sulla rilevazione del MIME type tramite Fileinfo.
- PHP Manual - pathinfo(): utile per estrarre estensioni e componenti del nome file.
- PHP Manual - random_bytes(): funzione per generare nomi casuali robusti.
