Funzioni PHP per gestire file e directory

Gestione dei file in PHP: funzioni avanzate

Dopo aver analizzato le funzioni che consentono le operazioni di base sui file, in questa guida vediamo quelle per effettuare operazioni più complesse come ad esempio copiare, rinominare ed eliminare un file, ottenere informazioni su un file e le funzioni per la gestione di cartelle.

Come già sappiamo, per poter lavorare con un file abbiamo bisogno di:

  1. Aprire il file con fopen(), che ci restituisce il puntatore al file.
  2. Effettuare le operazioni sul file utilizzando il suo puntatore.
  3. Chiudere il file con fclose() utilizzando il suo puntatore.

Copiare un file

PHP mette a disposizione la funzione copy() per effettuare la copia di un file. Può essere utilizzata per fare piccole copie di backup ad intervalli di tempo prestabiliti.

La funzione accetta due argomenti di tipo stringa che fanno riferimento rispettivamente al percorso del file di origine e a quello di destinazione.

Nel seguente script di esempio, la funzione copia il file file.txt che si trova nella directory corrente nella posizione indicata in $newFile, restituendo false in caso di errore.

<?php
$file = 'file.txt';
$newFile = 'file-copied.txt';

if (false === copy($file, $newFile)) {
    printf("Impossibile copiare il file %s", $file);
}

Nota: Se avessi voluto copiare il file di origine verso una directory diversa da quella corrente, avremmo dovuto specificare un valore diverso in $newFile come ad esempio "directory/file-copied.txt".

Rinominare o spostare un file

L'operazione di spostamento di un file, ossia quella che di solito eseguiamo tramite le combinazioni di tasti Ctrl + X (Copia) e Ctrl + V (Incolla), è possibile effettuarla tramite la funzione rename(), che come suggerisce il nome consente di rinominare un file.

La funzione prende in input il nome del file da rinominare ritornando true in caso di successo, false altrimenti.

<?php
$oldFile = '/old-path/image.jpg';
$newFile = '/new-path/new-image.jpg';

if (false === rename($oldFile, $newFile)) {
    printf('Impossibile rinominare il file %s', $oldPath);
}

Eliminare un file

La funzione unlink() consente di eliminare un file in maniera permanente dal file system. La funzione prende un singolo parametro, il nome del file che si vuole cancellare.

<?php
$file = 'file.txt';

if (false === unlink($file)) {
    printf("Impossibile eliminare il file %s", $file);
}

Verificare l'esistenza di un file

Le funzioni PHP viste fino ad ora potrebbero generare messaggi di errore, dovuti solitamente al fatto che il file sul quale vogliamo operare non esiste.

Non è insolito infatti imbattersi in un avviso simile al seguente

PHP Warning: fopen(file.txt): failed to open stream: No such file or directory in...

Per verificare se un file esiste o meno, il linguaggio mette a disposizione la funzione file_exists() che, preso il nome del file ne verifica l'esistenza restituendo true oppure false.

Riprendendo l'esempio relativo alla cancellazione di un file, per rendere il codice più robusto possiamo scrivere quanto segue

<?php
$file = 'file.txt';

if (false === file_exists($file)) {
    printf("Il file %s non esiste", $file);
    exit;
}

if (false === unlink($file)) {
    printf("Impossibile eliminare il file %s", $file);
}

Lo script termina stampando a video un errore se il file non esiste, altrimenti prosegue con l'operazione di cancellazione vera e propria.

Ottenere informazioni

I file contengono diverse informazioni riguardo se stessi, ad esempio la dimensione, la data di ultima modifica, l'id del proprietario.

Possiamo recuperare queste informazioni tramite la funzione stat() che prende il nome del file come parametro e restituisce un array contenente statistiche e informazioni sul file, elencate nella seguente tabella

Indice numerico Indice associativo Informazioni
0 dev Numero del dispositivo
1 ino Numero dell'inode
2 mode Modalità di protezione dell'inode
3 nlink Numero di collegamenti
4 uid ID utente del proprietario
5 gid ID di gruppo del proprietario
6 rdev Tipo del dispositivo, se dispositivo inode
7 size Dimensioni in byte
8 atime Data dell'ultimo accesso
9 mtime Data dell'ultima modifica
10 ctime Data dell'ultimo cambiamento
11 blksize Dimensioni dei blocchi per l'I/O del file system
12 blocks Numero di blocchi allocati

Un esempio di output della funzione stat() eseguita su sistema operativo Linux

<?php
$stats = stat('robots.txt');

print_r($stats);

/* Output

Array
(
    [0] => 2050
    [1] => 6951840
    [2] => 33204
    [3] => 1
    [4] => 1004
    [5] => 1005
    [6] => 0
    [7] => 25
    [8] => 1572203082
    [9] => 1568766637
    [10] => 1572203082
    [11] => 4096
    [12] => 8
    [dev] => 2050
    [ino] => 6951840
    [mode] => 33204
    [nlink] => 1
    [uid] => 1004
    [gid] => 1005
    [rdev] => 0
    [size] => 25
    [atime] => 1572203082
    [mtime] => 1568766637
    [ctime] => 1572203082
    [blksize] => 4096
    [blocks] => 8
)
*/

Nota: non tutte le informazioni richiamabili tramite la funzione stat() sono disponibili in tutti i sistemi operativi. Ad esempio, in Windows non saranno disponibili l'ID utente e l'ID di gruppo perché, a differenza di Linux, Windows non fornisce tali informazioni.

Esistono anche alcune funzioni specifiche per ricavare le proprietà correlate al tempo, che restituiscono valori nel formato Unix timestamp:

  • fileatime() restituisce l'ora in cui il file ha ricevuto l'ultimo accesso, o false in caso di errore.
  • filectime() restituisce l'ora in cui il file è stato cambiato l'ultima volta, o false in caso di errore. Un file viene considerato come cambiato se viene creato o scritto o quando sono stati cambiati i suoi permessi.
  • filemtime() restituisce l'ora in cui il file è stato modificato l'ultima volta, o false in caso di errore. Un file viene considerato come modificato se viene creato o i suoi contenuti sono cambiati.

La funzione filemtime() in particola si rivela molto utile per forzare l'aggiornamento di un foglio di stile di un sito web che ha subito una modifica recente.

Infatti, nella realizzazione di un sito web si è soliti memorizzare nella cache del browser risorse statiche come CSS, JS in maniera tale che i successivi accessi al sito siano più veloci. Tuttavia una eventuale modifica a uno di questi file statici non verrebbe visualizzata dall'utente che ha già visitato quel sito.

La soluzione è quella di modificare il nome del file per forzare l'aggiornamento della cache. Per fare ciò possiamo sfruttare filemtime() in questo modo

<link rel="stylesheet" href="/assets/css/amdesk.css?ver=<?php echo filemtime('/assets/css/amdesk.css')">

che produce un output simile a questo

<link rel="stylesheet" href="/assets/css/amdesk.css?ver=1574506234">

Leggere uno o più caratteri

Usata in combinazione con la funzione feof() che restituisce true quando viene raggiunta la fine di un file, la funzione fgetc() può essere utilizzata per leggere un carattere alla volta da un file.

La funzione accetta come argomento il puntatore al file e restituisce un solo carattere dal file a cui fa riferimento.

Di seguito è mostrato un esempio di funzionamento

<?php
$file = 'file.txt';
$handler = fopen($file, 'r');

while (!feof($handler)) {
    $char = fgetc($handler);
    echo $char."\n";
}

fclose($handler);

La funzione fgetc() é tanto semplice da usare quanto scomoda se abbiamo a che fare con file di grandi dimensioni, poiché leggendo un carattere per volta, è necessario un tempo molto lungo per leggere tutto il file.

Per fortuna PHP mette a disposizione la funzione fgets(), che consente di leggere intere righe di caratteri.

La funzione fgets() accetta due parametri: il puntatore al file e il parametro $lenght, restituendo una stringa di lunghezza massima in byte pari a $lenght - 1.

La lettura termina quando:

  • È stato letto il numero specificato di byte.
  • Viene incontrata una nuova riga.
  • Viene raggiunta la fine del file.
<?php
$handler = fopen("file.txt", "r");

if (false !== $handler) {
    while (false !== ($buffer = fgets($handler, 4096))) {
        echo $buffer;
    }
    if (!feof($handler)) {
        echo "Errore nella lettura tramite fgets()\n";
    }
    fclose($handler);
}

Accesso casuale ai dati

Le funzioni viste fino ad ora consentono di leggere i dati in modo sequenziale, cioè nello stesso ordine in cui sono disposti nel file.

Questa modalità di lettura crea una limitazione: quando l'indicatore di posizione del file supera un determinato punto, per tornare a leggere prima di quel punto è necessario chiudere e riaprire il file.

PHP offre la possibilità di posizionarsi in un punto qualsiasi all'interno di un file tramite la funzione fseek(), la cui sintassi è la seguente

fseek ( resource $handle , int $offset [, int $whence = SEEK_SET ] ) : int

dove:

  1. $handle è il puntatore al file
  2. $offset è la nuova posizione corrente (in byte)
  3. $whence è la posizione da cui iniziare a leggere

La funzioni restituisce 0 in caso di successo, -1 altrimenti. I possibili valori di $whence sono rappresentati da 3 costanti

  1. SEEK_SET: l'inizio del file (default)
  2. SEEK_CUR: posizione corrente
  3. SEEK_END: fine del file

Nota: la funzione fseek() è comoda nei casi in cui sappiamo a priori come è stato costruito il file che dobbiamo leggere (ad esempio con le righe aventi lunghezza 10 caratteri).

Facciamo un esempio per chiarire meglio il funzionamento di fseek()

<?php
$filename = 'file.txt';
$offset = 35;
$handler = fopen($filename, 'w+');

if (false !== $handler) {
    for ($i = 1; $i < 10; $i++) {
        fwrite($handler, "Riga $i\n");
    }
    
    fseek($handler, $offset);
    $buffer = fread($handler, 1024);
}
fclose($handler);

echo $buffer;

Tramite un ciclo for scriviamo 9 volte una stringa di lunghezza fissa all'interno di un file, successivamente spostiamo l'indicatore di posizione avanti di 35 byte (riga 11), dunque la lettura successiva (riga 12) tramite fread() comincerà da "Riga 6" fino al termine.

Funzioni per gestire directory

Vediamo ora un set di funzioni per manipolare le directory. Alcune di queste utilizzano un descrittore di directory, mentre altre utilizzano una stringa che rappresenta il nome della directory.

Un descrittore di directory è molto simile a un descrittore di file, ossia un numero intero che punta a una directory.

Creare una cartella

La prima funzione che andiamo a presentare è mkdir() che consente di creare una cartella, specificandone il nome come parametro

$dirname = 'php';

if (false === mkdir($dirname)) {
    printf('Impossibile creare la directory %s', $dirname);
}

Se l'operazione è andata a buon fine, la funzione restituisce true.

Cancellare una cartella

Così come è possibile creare una directory, allo stesso modo la si può eliminare.

Per cancellare una directory dal file system è necessario utilizzare la funzione rmdir()

<?php
$dirname = 'php';

if (false === rmdir($dirname)) {
    printf('Impossibile eliminare la directory %s', $dirname);
}

Leggere il contenuto di una cartella

Per leggere il contenuto di una cartella è necessario inanzitutto farsi ritornare il puntatore alla directory. La funzione preposta a tale operazione è opendir().

Una volta che abbiamo il puntatore alla directory, possiamo utilizzare la funzione readdir() per leggere, un file alla volta, il contenuto della directory.

L'elenco delle voci ritornate da readdir() include pure . (che specifica la directory corrente) e .. (che specifica il genitore della directory corrente).

Alla fine dell'operazione di lettura possiamo rilasciare le risorse tramite la funzione closedir().

<?php
$handler = opendir('php-7');

if (false !== $handler) {
    while ($file = readdir($handler)) {
        echo $file."\n";
    }
}
closedir($handler);

Rinominare una cartella

Concludiamo questa guida con la funzione per rinominare una cartella che poi è la stessa utilizzata per rinominare i file, ossia rename()

<?php
$dirname = 'php-5';

if (false === rename($dirname, 'php-7')) {
    printf('Impossibile rinominare la directory %s', $dirname);
}

La lista completa sulle funzioni per gestire il file system è disponibile a questo indirizzo.

Nelle prossime guide vedremo invece come gestire i file CSV, JSON e XML.