Introduzione
Quando si parla di gestione dei file in PHP, spesso ci si concentra su lettura e scrittura del contenuto. In realtà, una parte altrettanto importante riguarda la gestione delle directory: creare cartelle per organizzare upload, verificare se un percorso esiste, elencare i file presenti, rimuovere cartelle vuote e costruire flussi di lavoro ordinati e sicuri.
Questo argomento è molto utile in applicazioni reali: ad esempio, un sistema di upload immagini può creare automaticamente una cartella per ogni utente, un’app di report può salvare i file in directory separate per mese, oppure uno script di manutenzione può pulire cartelle temporanee non più necessarie.
In questo tutorial vedremo come lavorare con le directory in PHP in modo pratico e con attenzione alla sicurezza. Useremo funzioni comuni come mkdir(), is_dir(), scandir(), rmdir() e alcune tecniche per evitare errori frequenti, come percorsi non validi o cancellazioni accidentali.
Codice completo
<?php
declare(strict_types=1);
/**
* Esempio pratico: gestione di una directory per upload e pulizia.
* Obiettivi:
* - creare una cartella se non esiste
* - salvare file in una sottocartella organizzata per data
* - elencare i file presenti
* - rimuovere file vecchi
* - eliminare una directory vuota
*/
$baseDir = __DIR__ . ´/storage´;
$uploadDir = $baseDir . ´/uploads´;
$todayDir = $uploadDir . ´/´ . date(´Y-m-d´);
/**
* Crea una directory in modo sicuro se non esiste.
*/
function ensureDirectory(string $path, int $permissions = 0755): void
{
if (is_dir($path)) {
return;
}
if (!mkdir($path, $permissions, true) && !is_dir($path)) {
throw new RuntimeException("Impossibile creare la directory: {$path}");
}
}
/**
* Salva un file di esempio in una directory.
*/
function saveExampleFile(string $directory, string $filename, string $content): string
{
ensureDirectory($directory);
$safeFilename = basename($filename); // evita path injection semplice
$fullPath = $directory . ´/´ . $safeFilename;
if (file_put_contents($fullPath, $content) === false) {
throw new RuntimeException("Impossibile scrivere il file: {$fullPath}");
}
return $fullPath;
}
/**
* Restituisce l´elenco dei file in una directory, escludendo . e ..
*/
function listFiles(string $directory): array
{
if (!is_dir($directory)) {
return [];
}
$items = scandir($directory);
if ($items === false) {
throw new RuntimeException("Impossibile leggere la directory: {$directory}");
}
$files = [];
foreach ($items as $item) {
if ($item === ´.´ || $item === ´..´) {
continue;
}
$fullPath = $directory . ´/´ . $item;
if (is_file($fullPath)) {
$files[] = $fullPath;
}
}
return $files;
}
/**
* Elimina i file più vecchi di un certo numero di giorni.
*/
function deleteOldFiles(string $directory, int $days): int
{
if (!is_dir($directory)) {
return 0;
}
$threshold = time() - ($days * 86400);
$deleted = 0;
$files = listFiles($directory);
foreach ($files as $file) {
if (filemtime($file) !== false && filemtime($file) < $threshold) {
if (unlink($file)) {
$deleted++;
}
}
}
return $deleted;
}
/**
* Elimina una directory solo se è vuota.
*/
function removeEmptyDirectory(string $directory): bool
{
if (!is_dir($directory)) {
return false;
}
$items = scandir($directory);
if ($items === false) {
throw new RuntimeException("Impossibile leggere la directory: {$directory}");
}
$realItems = array_diff($items, [´.´, ´..´]);
if (!empty($realItems)) {
return false;
}
return rmdir($directory);
}
// --------------------
// ESECUZIONE ESEMPIO
// --------------------
try {
// 1) Crea le directory necessarie
ensureDirectory($todayDir);
// 2) Salva alcuni file di esempio
$file1 = saveExampleFile($todayDir, ´report.txt´, "Report generato il " . date(´Y-m-d H:i:s´) . PHP_EOL);
$file2 = saveExampleFile($todayDir, ´note.txt´, "Nota operativa" . PHP_EOL);
// 3) Elenca i file presenti
$files = listFiles($todayDir);
echo "<h3>File presenti nella cartella di oggi</h3>";
echo "<ul>";
foreach ($files as $file) {
echo "<li>" . htmlspecialchars(basename($file)) . "</li>";
}
echo "</ul>";
// 4) Pulisce file più vecchi di 7 giorni
$deletedCount = deleteOldFiles($uploadDir, 7);
echo "<p>File vecchi eliminati: " . $deletedCount . "</p>";
// 5) Prova a rimuovere la directory del giorno se vuota
$removed = removeEmptyDirectory($todayDir);
echo "<p>Directory del giorno rimossa: " . ($removed ? ´sì´ : ´no´) . "</p>";
} catch (Throwable $e) {
echo "<p>Errore: " . htmlspecialchars($e->getMessage()) . "</p>";
}
?> Spiegazione
Il codice mostra un flusso completo e realistico. Prima di tutto, definisce una struttura di cartelle sotto storage/uploads, con una sottocartella giornaliera nel formato YYYY-MM-DD. Questa strategia è molto utile perché distribuisce i file in modo ordinato e semplifica la manutenzione.
1. Creazione della directory
La funzione ensureDirectory() controlla se una cartella esiste già con is_dir(). Se non esiste, la crea con mkdir() e il terzo parametro impostato a true, così PHP crea anche eventuali directory intermedie. Questo è importante quando si lavora con percorsi annidati.
La gestione dell’errore è fondamentale: se la creazione fallisce, il codice lancia una RuntimeException. In un’applicazione reale, questo evita comportamenti silenziosi difficili da diagnosticare.
2. Salvataggio di un file
La funzione saveExampleFile() usa file_put_contents() per scrivere il contenuto. Prima invoca ensureDirectory(), così non rischia di scrivere in una cartella inesistente.
Un dettaglio importante è l’uso di basename() sul nome file. Questo riduce il rischio che un input malevolo contenga percorsi come ../../qualcosa e tenti di uscire dalla directory prevista. Non è una protezione completa per tutti gli scenari, ma è una buona prima difesa.
3. Elenco dei file
La funzione listFiles() usa scandir() per leggere il contenuto della directory. Poi filtra . e .., che rappresentano rispettivamente la directory corrente e quella padre. Infine, controlla con is_file() che ogni elemento sia davvero un file e non una sottocartella.
Questo approccio è utile quando vuoi mostrare all’utente solo i file effettivi o eseguire operazioni automatiche su di essi.
4. Pulizia dei file vecchi
La funzione deleteOldFiles() elimina i file con data di modifica precedente a una soglia temporale. Usa filemtime() per leggere la data dell’ultimo aggiornamento e unlink() per cancellare il file.
Questo schema è perfetto per cartelle temporanee, cache personalizzate o file di export che devono essere conservati solo per un certo periodo.
5. Rimozione di directory vuote
La funzione removeEmptyDirectory() controlla che la cartella sia davvero vuota prima di invocare rmdir(). PHP non permette di eliminare una directory non vuota con rmdir(), quindi questo controllo evita errori inutili.
Per verificare se la directory è vuota, il codice usa scandir() e poi array_diff() per escludere . e ... Se restano altri elementi, la directory non viene rimossa.
Best practice
- Controlla sempre l’esistenza della directory con
is_dir()prima di leggere, scrivere o cancellare. - Usa percorsi assoluti quando possibile, soprattutto in script di manutenzione o backend, per evitare ambiguità legate alla working directory.
- Valida i nomi dei file provenienti dall’utente. Funzioni come
basename()aiutano, ma spesso è meglio applicare una whitelist di caratteri consentiti. - Non cancellare mai ricorsivamente senza controlli. La rimozione di directory con file interni richiede logica aggiuntiva e deve essere protetta con attenzione.
- Gestisci gli errori con eccezioni o con un sistema di logging, invece di ignorare i fallimenti delle funzioni filesystem.
- Imposta permessi ragionevoli quando crei directory, ad esempio
0755, evitando permessi troppo aperti. - Separa i file per contesto: data, utente, ambiente o tipo di documento. Una buona struttura evita caos e rende la manutenzione molto più semplice.
Riepilogo
La gestione delle directory in PHP è una competenza fondamentale per sviluppare applicazioni ordinate, sicure e facili da mantenere. Con poche funzioni ben scelte puoi:
- creare cartelle automaticamente quando servono;
- salvare file in strutture organizzate;
- leggere il contenuto di una directory;
- eliminare file obsoleti;
- rimuovere directory vuote in modo controllato.
Il punto chiave è non considerare il filesystem come un dettaglio secondario: una buona strategia di gestione delle directory migliora affidabilità, sicurezza e chiarezza del progetto.
Approfondisci con risorse ufficiali
- mkdir() — documentazione ufficiale PHP
- is_dir() — verifica se un percorso è una directory
- scandir() — elenco del contenuto di una directory
- rmdir() — rimozione di directory vuote
- unlink() — eliminazione di file
- filemtime() — data dell’ultima modifica di un file
- basename() — estrazione del nome file da un percorso
Se vuoi, posso anche scrivere un secondo tutorial collegato, ad esempio su cancellazione ricorsiva di directory in PHP oppure su upload di file con creazione automatica delle cartelle.
