Gestire file e cartelle con PHP

Giovanni Canella

20/08/2013

7049

La gestione dei files in un linguaggio di programmazione è uno degli aspetti più importanti, perchè ci permette di interagire con una risorsa interna al filesystem (o anche remota) eseguendo le più svariate operazioni.

Per esempio:

  • salvare,
  • eliminare,
  • rinominare,
  • controllare i permessi,
  • controllare se è leggibile.

e così via. PHP, ci mette a disposizione un'infinita serie di funzioni, che cercherò di illustrarvi in questo articolo: partiamo dai controlli!

Files all'interno di una cartella

 

 file_exists()

 

Controlla se il file/cartella passato come parametro esiste o no. Restituisce true in caso affermativo, false nell'altro caso. 

if(file_exists("file.php")) {
	echo "Esisto!";
} else {
	echo "Io no :(";
}

 

 is_file()

 

Determina se il file inserito è realmente un file. Se l'architettura del sistema operativo in uso è 32 bit, e il file inserito ha un peso maggiore di 2GB, alcuni filesystem potrebbe dare problemi e generare errori o warning inaspettati.

if(is_file("file.php")) {
	echo "Sono un file!";
} else {
	echo "Io no :(";
}

 

 is_dir()

 

Determina se la cartella passata come parametro è realmente una cartella. Si possono anche inserire percorsi relativi, e se è un link simbolico o fisico, esso viene prima risolto e controllato.

if(is_dir("immagini")) {
	echo "Sono una cartella!";
} else {
	echo "Io no :(";
}

 

 is_executable()

 

Determina se il file è eseguibile o meno. (Dalla versione 5.0.0, di PHP è disponibile anche per Windows)

if(is_executable("bash.sh")) {
	echo "Sono eseguibile!";
} else {
	echo "Io no :(";
}

 

 is_uploaded_file()

 

Determina se il file è stato caricato tramite richiesta POST. (funzione che vedremo nelle prossime pagine). Questo metodo bisognerebbe sempre usarlo per evitare che un malintenzionato possa ingannare l'applicazione e fare in modo che interagisca con file che non dovrebbero essere toccati (per es. /etc/passwd, se abbiamo abilitato un login tramite il file .htaccess).

if(is_uploaded_file("index.php")) {
	echo "Sono stato caricato sul server!";
} else {
	echo "Io no :(";
}

 

 is_link()

 

Determina se il file è un link simbolico.

if(is_link("articoli")) {
	echo "Sono un link simbolico!";
} else {
	echo "Io no :(";
}

In caso negativo possiamo crearlo tramite symlink(), in questo modo:

symlink('articoli.php', "articoli");

Oppure se vogliamo un collegamento fisico, usiamo link(),

link('articoli.php', "articoli");

E per vedere dove punta il link, c'è che ci restituisce il valore desiderato readlink().

 

 is_readable()

 

Svolge un po' la funzione di file_exist(), dato che oltre a controllare se il file esiste , controlla se il file esiste ed è leggibile.

if(is_readable("index.php")) {
	echo "Sono leggibile!";
} else {
	echo "Io no :(";
}

 

 is_writeable()

 

Determina se il file è scrivibile.

if(is_writable("index.php")) {
	echo "Sono scrivibile!";
} else {
	echo "Io no :(";
}

 

 is_writeable()

 

Identico al metodo precedente, cambia solo il nome. Questo permette ai programmatori di sentirsi più comodi e agevolati utilizzando le funzioni che risultano loro immediate, come per esempio die() e exit().

C'è anche una lista con tutti gli alias.

Dopo aver visto i controlli base, ora passiamo alla gestione vera e propria: aprire/creare/modificare/eliminare files e cartelle.

 

 fopen($file, $modalità)

 

Non esiste una vera e propria funzione per la creazione di file, ma viene utilizzato un metodo in comune anche per l'apertura: fopen(): accetta due parametri obbligatori: il file con cui vogliamo lavorare, e la modalità di apertura di quest'ultimo, per esempio sola lettura, scrittura ecc. Ecco una lista di quelle principali, e a mio parere, più utili nell'applicazione comune:

  • r: Apre il file in modalità di sola lettura posizionando il puntatore all'inizio del file,
  • r+: Apre il file modalità di lettura/scrittura posizionando il puntatore all'inizio del file,
  • w: Apre il file in modalità si sola scrittura posizionando il puntatore all'inizio del file cancellando tutto il , e se non esiste viene creato,
  • w+: Apre il file in modalità di lettura/scrittura, cancellando tutto il contenuto del file, e se non esiste viene creato,
  • a: Apre il file in modalità di sola scrittura, posizionando il puntatore alla fine del file, se non esiste viene creato,
  • a+: Apre il file in modalità di lettura/scrittura, posizionando il puntatore alla fine del file, se non esiste viene creato.

Come abbiamo potuto vedere dalla descrizione della modalità, ognuna ha un suo differente scopo che può essere sfruttato in diverse occasioni, ad esempio, se vogliamo creare un file possiamo utilizzare "w" e "a", mentre se vogliamo per esempio creare un log, "appendendo" ogni informazione, alla riga precedente, senza sovrascriverla, usiamo "a".

fopen(), oltre ad aprire file interni al filesystem, permette anche di operare su file remoti, attraverso i seguenti protocolli:

  • http://,
  • ftp://,
  • rar://,
  • ssh2://,
  • zlib://,
  • phar://,
  • ogg://,
  • data://,
  • glob://

Per maggiori informazioni leggere la documentazione ufficiale

Detto ciò ecco un esempio di utlizzo:

$puntatore = fopen("commenti.txt", "a+");

Se il file viene aperto correttamente viene restituito il puntatore, in caso contrario FALSE.

 

 fwrite($puntatore, $testo, $lunghezza)

 

Scrive all'interno di un file, identificato grazie al puntatore passato come parametro, una determinata stringa. I primi due parametri sono obbligatori, mentre il terzo è opzionale, e se impostato specifica la lunghezza massima (in byte) del testo che deve essere scritto.

es.

fwrite($puntatore, "Sono un testo dentro un file! :D");

Se chiamiamo due o più  volte separatamente fwrite, sullo stesso puntatore, il testo viene appeso es.

fwrite($puntatore, "Primo testo\n");
fwrite($puntatore, "Seconda testo\n");

L'output sarà:

Primo testo
Secondo testo

e non:

Secondo testo

La funzione restituisce TRUE, se la scrittura è andata a buon fine, FALSE in caso di errore.

 

 fputs($puntatore)

 

Alias di fwrite().

 

 file_put_contents($file, $contenuto, $flags)

 

E' equivalente al chiamare fopen(), fwrite() e fclose() consecutivamente. Se il file non esiste viene creato. Restituisce il numero di bytes che sono stati scritti.

Nel terzo parametro possiamo impostare uno dei seguenti valori, o se vogliamo anche unirli con "|", (OR):

  • FILE_USE_INCLUDE_PATH, Controlla  il file nella cartella include.
  • FILE_APPEND, Se il file esiste invece di sovrascriverlo, "appende" il testo inserito all'ultima riga presente (simile al parametro "a" in fopen()),
  • LOCK_EX: Blocca il file esclusivamente, impedendo ad altri la modifica nello stesso momento.
$nuovo_account = "Ginho";
file_put_contents("account.txt", $nuovo_account . "\n");

Se adesso riprovassimo aggiungendo il flag FILE_APPEND..

$nuovo_account = "Ginhoz";
file_put_contents("account.txt", $nuovo_account . "\n", FILE_APPEND);

Possiamo verificare quanto detto prima:

Esempio file dopo aver usato file_put_contents()

$nuovo_account = "Ginhoz";
file_put_contents("account.txt", $nuovo_account . "\n", FILE_APPEND | LOCK_EX);

 

 fread($puntatore, $limite)

 

Legge il contenuto del file tramite il suo puntatore fino al limite (in byte) impostato. Se non ci sono errori, restituice il valore ottenuto, altrimenti FALSE. Per leggere tutto il file si può utilizzare la funzione filesize(), in questo modo:

$contenuto = fread($puntatore, filesize($file));

Anche se non è il migliore approccio (vedi la prossima funzione).

 

 file_get_contents($file)

 

Restituisce una stringa, in caso di successo, contenente l'intero contenuto del file specificato come parametro, altrimenti FALSE. E' consigliata se vogliamo tutto il file, perchè più veloce rispetto alla tecnica file() + filesize(), vista precedentemente, dato che file_get_contents(), utilizza la tecnica chiamata memory mapping. (se supportata dal sistema operativo in uso).

// File locale
$contenuto = file_get_contents("file.txt);

// File remoto
$contenuto_remoto = file_get_contents("http://ginho,it");

 

 file($file)

 

Simile a file_get_contents(), ma invece di restituire una stringa, restituisce un array (con indice 0) di tutte le linee del file, in caso di errore FALSE

Array
(
    [0] => Ginho!
    [1] => Benvenuti in Ginho!
    [2] => Il sito che parla di tutto ciò che ruota attorno all'informatica!
)

 

 feof($puntatore)

 

feof(), acronimo di file end of file, Scorre tutte le linee del file finche non terminano, e in questo caso restituisce FALSE. Si utilizza in un ciclo while in questo modo:

$contatore = 0;

while(!feof($puntatore)) {
	fgets($puntatore);
	echo "Linea n° " . $contatore;
	$contatore++;
}

 

 fgets($puntatore, $limite)
 

Ottiene una linea dal puntatore del file, e se impostato il limite (in byte) tronca fino al valore stabilito. E' consigliato utilizzarlo in accoppiata con feof(), così mentre scorre tutte le linee fgets() ottiene i valori. In caso contrario restituisce FALSE.

$contatore = 0;
echo "Di seguito stampo tutte le righe di un file! :D!";

while(!feof($puntatore)) {
	$riga = fgets($puntatore);
	
	echo $contatore . ". " . $riga;
	$contatore++;
}

 

 fgetc($puntatore)

 

Restituisce un carattere dalla riga su cui è posizionato il puntatore. Restituisce FALSE, quando non ci sono più linee. Prendendo questo file di testo:

ginho.it

verrà restituito:

g

Il primo carattere. Se volessimo stamparli tutti:

while(!fof ($file)) {
	echo fgetc($file);
}

 

Però facciamo attenzione che questa funzione è lenta. Su piccoli file non ce ne accorgiamo, ma dovessimo lavorare su file molto grandi è consigliato utilizzare prima fgets(), e dal suo risultato applicare fgetc()

 

 fgetss($puntatore, $limite, $whitelist)

 

Identica a fgets(), ma in aggiunta elimina tutti i tag HTML e PHP presenti nel contenuto del file, e se impostato l'ultimo parametro ($whitelist), permette di filtrare e decidere quali tag non toccare.

$linea = fgtess($puntatore, 1024, "<p>,<span>");

Notare come bisogna inserire i tag per essere filtrati, ovvero separati da virgola senza spazi. Il limite se non viene impostato è di default 1024 byte (1 kB).

 

 fseek($puntatore, $posizione, $modalità)

 

Sposta il puntatore all'interno del file ad una certa posizione definita nella variabile $posizione, secondo una certa modalità, che può essere:

  • SEEK_SET: Imposta la posizione secondo la posizione inserita (default),
  • SEEK_CUR: Imposta la posizione in quella corrente più quella da noi definita.
  • SEEK_END: Imposta la posizione alla fine più quella da noi definita.

Restituisce 0 in caso di successo, -1 in caso negativo:

$puntatore = fopen("file.txt", "r+");

fwrite($puntatore, "Sono un grande");
fseek($puntatore, 8);
fwrite($puntatore, "PRO");
fclose($puntatore);

L'ouput sarà:

Sono un PRO

Questo perchè dopo aver scritto "Sono un grande", abbiamo spostato il puntatore di 8 caratteri, e successivamente abbiamo scritto un nuovo testo: "PRO".

 

 rewind($puntatore)

 

Svolge la stessa funzione di fseek(), tranne per il fatto che sposta sempre il puntatore all'inizio del file. 

$puntatore = fopen("file.txt", "r+");

fwrite($puntatore, "10000000 €");
rewind($puntatore);
fwrite($puntatore, "9");
fclose($puntatore);

L'ouput è:

90000000 €

 

 ftell($puntatore)

 

Restituisce l'attuale posizione del puntatore come intero. Su sistemi a 32bit, alcuni filesystem potrebbe restituire risultati inaspettati con file di dimensioni maggiori di 2GB.

$puntatore = fopen("file.txt", "r+");

fseek($puntatore, 15);
echo ftell($puntatore);

Come previsto verrà stampato: int(15).

 

 ftruncate($puntatore, $limite)

 

Elimina il contenuto del file da un certo limite da noi impostato. Questa operazione non avrà effetto sul puntatore, che rimarrà nella posizione precedente.

Prendendo come esempio questo file:

Ginho
Ginhoz
ginho.it
pro

se facciamo un:

$puntatore = fopen("account.txt", "r+");
ftruncate($puntatore, 5);

Il file diventrà:

Ginho

 fclose($puntatore)

 

Chiude il puntatore relativo al file con cui abbiamo lavorato. E' caldamente consiglliato utilizzarlo sempre dopo aver finito tutte le operazioni su quel file: quando l'applicazione termina l'esecuzione, il file viene chiuso automaticamente da solo, però è una buona pratica farlo manualmente perchè  nel frattempo si utilizza memoria inutilmente.

fclose($puntatore);

 

 copy($file, $nuovoFile)

 

Copia il file impostato nel primo parametro, in un secondo file. Se questo esiste, viene sovrascritto. Restituisce TRUE in caso di successo e FALSE in caso di errore.

if (!copy("account.txt", "account_backup.txt")) {
	echo "Errore durante la copia di un file...";
}

 

 rename($file, $nuovoFile)

 

Rinomina il file impostato nel primo parametro, in un secondo file. Se si imposta un percorso differente, oltre ad essere rinominato viene anche spostato. Restituisce TRUE in caso di successo e FALSE in caso di errore.

if (!rename("account.txt", "account_backup.txt")) {
	echo "Errore nel rinominare un file...";
}

 unlink($file)

 

Elimina il file inserito. Restituisce TRUE in caso di successo e FALSE in caso di errore.

if(unlink("account_backup.txt")) {
	echo "File eliminato con successo!";
}

Ecco un esempio pratico di utilizzo delle funzioni viste in precedenza:

$file = "account.txt";

if(file_exists($file)) {
	if(is_readable($file)) {
		if(is_writable($file)) {
			// Apro il file
			$puntatore = fopen($file, "r+");
			echo "Posizione puntatore di default: <strong>" . ftell($puntatore) . "</strong><br>";
			
			// Scrivo una linea
			fwrite($puntatore, "Io sono la prima linea!\n");
			
			// Ne scrivo un'altra
			fwrite($puntatore, "Mentre io sono la seconda linea e non sovrascrivo la prima perche' ho chiamato due volte fwrite!");
			
			echo "Posizione puntatore dopo aver scritto del testo: <strong>" . ftell($puntatore) . "</strong><br>";
			
			// Sposto il puntatore di 15 caratteri
			fseek($puntatore, 15, SEEK_SET);
			
			echo "Posizione puntatore dopo aver utilizzato fseek(): <strong>" . ftell($puntatore) . "</strong><br>";
			
			echo "______________________________________________________<br><br>";
			
			// Porto il puntatore all'inizio del file
			rewind($puntatore);
			
			$contatore = 0;
			
			while(!feof($puntatore)) {
				echo "Lettera iniziale: <strong>" . fgetc($puntatore) . "</strong><br>";
			    echo "Linea numero: <strong>" . $contatore . "</strong>: " . fgets($puntatore) . "<br><br>";
			    $contatore++;
			}
			
			echo "<br>Numero linee totali: <strong>" . $contatore . "</strong><br>";
			
			// Tronco il file a 105 caratteri
			ftruncate($puntatore, 105);
			
			echo "Output di <strong>file_get_contents()</strong>: "; var_dump(file_get_contents($file)); echo "<br><br>";
			
			echo "Output di <strong>file()</strong>: <pre>"; print_r(file($file)); echo "</pre>";
		
			
		} else {
			echo "Il file " . $file . " non è scrivibile";
		}
	} else {
		echo "Il file " . $file . " non è leggibile";
	}
} else {
	echo "Il file " . $file . " non esiste";
}

Esempio riassuntivo delle funzioni descritte precedentemente

 

 mkdir($cartella, $permessi = 0777)

 

Crea una cartella con il nome del parametro $cartella passato. I permessi impostati di default sono 0777 (lettura/scrittura/esecuzione), ma possiamo modicarli impostando un valore al secondo parametro. Restituisce TRUE in caso di successo e FALSE in caso di errore.

if(mkdir("account")) {
	echo "Cartella account creata con successo! :D";
}

 

 opendir($cartella)

 

Apre la cartella passata come parametro e imposta un puntatore, se la cartella esiste e si dispone dei permessi sufficienti, altrimenti restituisce FALSE e generare un errore di livello E_WARNING, così da usare readdir(), closedir(), rewinddir().

if ($puntatore = opendir("account")) {
	// Operazioni sulla cartella
}

 

 readdir($cartella)

 

Restituisce il nome del prossimo elemento della cartella secondo l'ordine del filesystem, in cui è salvato:

while (($elemento = readdir($puntatore)) !== false) {
	echo $elemento . "\n";
}

Unico "problema" è che vengono mostrati degli elementi inaspettati: "." e "..", che corrispondono rispettivamente alla cartella stessa, e a quella padre. Per rimuoverli basta filtrarli con il costrutto if { else }, in questo modo:

if($elemento <> "." || $elemento <> "..") {
	echo $elemento;
}

 scandir($cartella, $ordine)

 

Restituisce un array di file e cartelle contenuti nella cartella impostata, secondo l'ordine definito. Di default è in ordine alfabetico cresce. Ecco i valori possibili:

  • SCANDIR_SORT_DESCENDING: Ordine alfabetico decrescente,
  • SCANDIR_SORT_NONE: Nessun ordine.

In caso di successo, FALSE e un errore di livello E_WARNING, in caso di errore.

$elementi = scandir("account");

print_r($elementi);

Ecco il risultato:

Risultato di scandir()

Se vogliamo estrarre ogni singolo file, utilizziamo un semplice ciclo foreach:

$contatore = 0;
		
foreach($elementi as $elemento) {
	echo "Elemento numero " . $contatore . ": " . $elemento . "
";
	$contatore++;
}

Estrarre ogni singolo con file da scandir() con un ciclo foreach

 

 closedir($puntatore)

 

Chiude il puntatore della cartella in questione.

closedir($puntatore);

 

 rmdir($cartella)

 

Rimuove la cartella a patto che sia vuota, e che si abbia i permessi sufficienti per farlo. Restituisce TRUE in caso di successo e FALSE in caso di errore.

if(rmdir("account")) {
	echo "Cartella account eliminata con successo! :D";
}

Come ultima parte di questo articolo, vediamo funzioni che possono esserci di utilità per i più disparati scopi:

 

  fileatime($file)

 

Restituisce l'ultimo accesso eseguito (in formato unix timestamp) al file passato come parametro, in caso di errore FALSE.  Per ottenere un valore da noi comprensibile si utilizza la funzione date(), in questo modo:

if (file_exists("account.txt")) {
    echo "L'ultimo accesso è stata il: " . date("d m Y H:i:s.", fileatime("account.txt"));
}

Che mostreà:

L'ultimo accesso è stato il: 25 07 2013 - 15:32:36.

 

 filemtime($file)

 

Restituisce la data (in formato unix) di ultima modifica al file passato come parametro, in caso di errore FALSE. 

if (file_exists("account.txt")) {
    echo "L'ultima modifica è stata effettuata il: " . date("d m Y H:i:s.", fileatime("account.txt"));
}

Mostrerà:

L'ultima modifica è stata effettuata il: 25 07 2013 - 17:12:06.

 

 fileperms($file)

 

Restituisce i permessi relativi al file passato come parametro, in modalità numerica (es. 0755), in caso di errore genera un E_WARNING.

echo substr(sprintf('%o', fileperms("account.txt")), -4);

 

Così una volta ottenuti i permessi, tramite sprintf(), con parametro %o, convertiamo il numero in base ottale.

Nel caso del file in questione l'output è:

0777

 

 filesize($file)

 

Restituisce le dimensioni del file specificato in byte, in caso di errore, FALSE.

 

echo "Le dimensioni del file sono: " . filesize("account.txt") . " bytes";

 filetype($file)

 

Restituisce il tipo di file passato come parametro. I possibili valori sono:

  • file,
  • dir,
  • link,
  • block,
  • char,
  • fifo,
  • socket,
  • unknown.

In caso di errore restituisce FALSE. e un messaggio E_WARNING.

echo filetype('account');
echo filetype('account.txt');

L'output:

dir
file

 

 pathinfo($percorso, $opzioni)

 

Restituisce informazioni riguardo il percorso specificato, se $opzioni non è impostata allora restituisce tutti gli elementi possibili. Può avere uno di questi valori:

  • PATHINFO_DIRNAME,
  • PATHINFO_BASENAME,
  • PATHINFO_EXTENSION,
  • PATHINFO_FILENAME.
$percorso = pathinfo("/home/giovanni/public_html/prove/account.txt");

echo "Nome cartella: " . $percorso["dirname"];
echo "File: " . $percorso["basename"];
echo "Nome file: " . $percorso["filename"];
echo "Estensione: " . $percorso["extension"];

L'output:

pathinfo()

Se invece per esempio, vogliamo ottenere solo l'estensione:

$estensione = pathinfo("/home/giovanni/public_html/prove/account.txt", PATHINFO_EXTENSION);

 

 touch($file, $data = time())

 

Modifica la data di ultimo accesso e modifica, del file passato come parametro, con quella attuale se non è impostato il secondo parametro, altrimenti viene utilizzata quest'ultima. Se il file non esiste viene creato. Restituisce TRUE in caso di successo, FALSE in caso di errore.

if (touch("account.txt")) {
	echo "Operazione eseguita correttamente! :D";
	echo "Data ultimo accesso: " . date("d m Y - H:i:s", fileatime());
	echo "Data ultima modifica: " . date("d m Y - H:i:s", filemtime());
} else {
	echo "Errore durante l'esecuzione di touch()";
}

Output:

touch()

 

 disk_free_space($cartella)

 

In base alla cartella passata come parametro restituisce il numero di bytes disponibili nel filesystem o partizione del disco. come numero in virgola mobile (float), e in caso di errore FALSE. E' da sottolineare che funziona solo sul filesystem locale e non in remoto.

echo disk_free_space("/"); 

Mentre se siamo su Windows:

echo disk_free_space("C:"); 

L'output nel mio caso è 89919942656 bytes, ovvero 89.9 GB disponibili, o 83,74 GiB.

 

 disk_total_space($cartella)

 

In base alla cartella passata come parametro restituisce il numero totale di bytes del filesystem o partizione corrente, come numero in virgola mobile (float), e in caso di errore FALSE. E' da sottolineare che funziona solo sul filesystem locale e non in remoto.

echo disk_total_space("/");

 L'output nel mio caso è 107393245184 kB, ovvero circa 100 GiB.

 diskfreespace()

 

Alias di disk_free_space().

Conclusioni

In questo articolo ho cercato di illustrarvi la maggior parte delle funzioni per la gestione del filesystem, che a mio parere sono più utili nell'utilizzo "pratico". In ogni caso se l'avete letto tutto ora sapete maneggiare a 360° files e cartelle e sfruttare appieno le potenzialità delle funzioni che PHP ci mette a disposizione.

Ti potrebbero interessare

I più letti