Come gestire i file: le basi (parte 2)

Nel precedente articolo abbiamo potuto vedere come gestire le informazioni necessarie a lavorare con un file.

Ora andiamo avanti ed entriamo più nello specifico aggiungendo altri metodi alla classe YCFile riguardanti la ricerca, la lettura e l’eliminazione dei file sul tuo server così da completarla e poterla utilizzare in alcuni casi reali nei prossimi due articoli! 

Struttura della guida

Come gestire i file: le basi (parte 1)
Questo primo articolo è incentrato sulla creazione di una classe per gestire i file. Ti mostro come effettuare controlli di sicurezza preliminari e alcuni metodi per ottenere informazioni basilari.

Come gestire i file: le basi (parte 2)
Ecco come cercare, leggere ed eliminare i file, effettuando controlli a monte ed eliminando ogni futuro e possibile problema.

Come gestire i file: il download
Vediamo come effettuare il download dei file in sicurezza per te e per i tuoi utenti.

Come gestire i file: l’upload
Ed infine ecco come effettuare l’upload dei file sul server, con l’ausilio della classe class.upload.

Come posso trovare un file nel mio sito?

Solitamente quando si carica un file sul proprio server, si modifica il suo nome, per comodità o per necessità. Io per entrambi i motivi, rinomino il file con la funzione time().
Questo però implica che diventi successivamente impossibile per me sapere il nome di un file (vi sfido a ricordare tutti i timestamp dei vostri file!).
Ma è sempre possibile trovarlo con il metodo YCFile::find(). L’unico problema è che questo metodo ha bisogno del nome del file per trovarlo.
Riflettici un attimo, è un vero problema? Prima di rispondere a questa domanda voglio mostrarti come funziona il metodo.

/* … */

class YCFile {
	/* … */

	//Cerca un file e in caso positivo, restituisce il suo percorso
	public static function find($file, $base) {
		if(is_dir($base) AND self::exists($base . DIRECTORY_SEPARATOR . $file)) {
			return $base . DIRECTORY_SEPARATOR . $file;
		}

	   return false;
	}

	/* … */
}

Puoi notare che è semplicissimo.
Lo script riceve in input il nome di un file ($file) e la cartella in cui cercarlo ($base). Del resto si occuperà tutto lui.
Ma come faremo a dirgli il nome del file se non possiamo saperlo? In realtà è impossibile non saperlo. Un qualche riferimento al file dobbiamo averlo salvato per forza in un database o in qualsiasi altro posto se hai intenzione di riutilizzarlo.
Io sono solito salvare nel database il link completo al file. È poi sufficiente esplodere questo link per ricavare il suo nome.
Probabilmente ti starai chiedendo perché si potrebbe avere la necessità di cercarlo se si ha un link di riferimento. La risposta richiede l’analisi di una determinata situazione che non mi è possibile trattare in questo articolo poiché mi dovrei dilungare troppo nell’argomento. Ma tranquillo, la tratterò nella terza parte della guida riguardante il download dei file!

Cercare più file contemporaneamente

Se invece stiamo cercando tutti i file contenuti in una cartella specifica, possiamo invocare il metodo YCFile::listFile().

/* … */

class YCFile {
	/* … */

	//Genera un array contenente i file trovati nella cartella indicata
	public static function listFile($dir, $ext = false) {
		$dp = FALSE;

		if(is_dir($dir)) {
			$dp = opendir($dir);
		}

		if($dp !== FALSE) {
			$return = array();
			while(FALSE !== ($file = readdir($dp))) {
				if($ext) {
					if(is_array($ext)) {
						if(in_array(strtolower(self::ext($file)), $ext)) {
							$return[$file] = $dir . $file;
						}
					} else {
						if(strtolower(self::ext($file)) == $ext) {
							$return[$file] = $dir . $file;
						}
					}
				} else {
					$return[$file] = $dir . $file;
				}
			}

         closedir($dp)
			return $return;
		}

		return false;
	}
	/* … */
}

Anche questo metodo non ha un funzionamento complesso.
Riceve in input il percorso di una cartella, ed eventualmente l’estensione dei file da cercare in formato stringa per cercare per singola estensione o in formato array per cercare più estensioni.
Lo trovo particolarmente utile quando devo includere molte classi contemporaneamente in un determinato script.
Eseguo il metodo sulla cartella contenente le mie classi, cercando per estensione ‘php’ così da ottenere solo i file che mi interessano per poterli poi includere.

Warning? No, grazie!

Ti è mai capitato di dover eliminare un file e ricevere continui Warning o Fatal Error?
Purtroppo a me è capitato anche troppo spesso. Per risolvere questo problema, ecco il metodo YCFile::delete().

/* … */

class YCFile {
	/* … */

   //Elimina un file
	public static function delete($file){
		if(self::exists($file)) {
			if(@unlink($file)) {
				return true;
			}
		}

		return false;
	}

	/* … */
}

Il metodo controlla che il file esista e lo elimina.
Se leggi attentamente il codice, sicuramente noterai il carattere @ prima della funzione unlink().
In questo modo evito che vengano sollevati errori da PHP e permetto al metodo di continuare il suo lavoro e fargli restituire un valore booleano che potrò gestire successivamente nel migliore dei modi.

Leggere file con una sola riga di codice!

Spesso mi sono trovato a dover leggere il contenuto di un file che ho allegato ad una email, per esempio, oppure che ho messo a disposizione per il download agli utenti.
Ogni volta dovevo scrivere sempre le stesse righe di codice, gestire possibili errori e poi lavorare il contenuto.
Di questo, però, si occuperà il metodo YCFile::read() con una sola riga di codice, restituendo il contenuto del file pronto per essere elaborato.

/* … */

class YCFile {
	/* … */

   //Legge il contenuto di un file
	public static function read($file, $totalChunk = 0, $chunkSize = 8192, $offset = 0) {
	   if(!self::isReadable($file)) {
			return false;
		}

		if($totalChunk AND $chunkSize > $totalChunk) {
			$chunkSize = $totalChunk;
		}

		$fp = fopen($file, 'rb');

		if($fp !== FALSE) {
			if($offset) {
				fseek($fp, $offset);
			}

			$fileSize = filesize($file);

			if($fileSize < $totalChunk) {
				$totalChunk = $fileSize;
			}

			$fileData = '';
			while(!feof($fp) AND (!$totalChunk OR strlen($data) < $totalChunk)) {
				$fileData .= fread($fp, $chunkSize);
			}

			fclose($fp);

			return $fileData;
		}

		return false;
	}

	/* … */
}

Questo metodo è un pò più complesso, ma niente di cui preoccuparsi.
Con il parametro $file specifichiamo il file da leggere.
Possiamo specificare la quantità di contenuto da leggere con $totalChunk esprimendo un valore in byte.
Possiamo anche indicare al metodo di leggere un certo numero di byte alla volta del file con $chunkSize.
E se per caso non avessimo bisogno di leggere il file dall’inizio, ma da un punto ben preciso, lo specifichiamo tramite $offset.

La classe YCFile è completa. Di seguito troverai tutto il codice scritto in questo articolo e nel precedente  oltre a poterlo scaricare insieme ai file di esempio.

/**
* Classe per velocizzare i controlli e la gestione dei file.
*/

class YCFile {
	//Controlla l’esistenza del file ed eventualmente restituisce informazioni con pathinfo().
	public static function exists($file, $getInfo = false) {
		//Controlliamo che il file esista e che sia veramente un file
		if(is_file($file)) {
			//Se vogliamo, ci facciamo restituire informazioni come l’estensione, il nome ecc...
			if($getInfo) { return pathinfo($file); }

			return true;
		}

		return false;
	}

	//Controlla che il file sia leggibile o meno
	public static function isReadable($file) {
		return (self::exists($file) AND is_readable($file));
	}

	//Restituisce l’estensione del file
	public static function ext($file) {
		return substr($file, strrpos($file, '.') + 1);
	}

	//Restituisce il nome del file senza estensione
	public static function name($file) {
		//Se non sono in ambiente Unix, sostituisco il separatore con il carattere slash ( / )
		if(DIRECTORY_SEPARATOR != '/') {
			$file = str_replace(DIRECTORY_SEPARATOR, '/', $file);
		}

		$lastSlash = strrpos($file, '/');

		//Il parametro $file è una path, quindi estraggo solo il nome del file.
		if($lastSlash) {
			$file = substr($file, $lastSlash + 1);
		}

		return substr($file, 0, strrpos($file, '.'));

   }

   //Restituisce il nome del file comprensivo di estensione
	public static function realName($file) {
		return self::name($file) . '.' . self::ext($file);
	}

	//Elimina caratteri non consentiti dal nome del file
	public static function sanitizeName($file) {
		$fileName = preg_replace('/[^\w\.\-]/', '_', self::realName($file));
		return dirname($file) . DIRECTORY_SEPARATOR . $fileName;
	}

	//Cerca un file e in caso positivo, restituisce il suo percorso
	public static function find($file, $base) {
		if(is_dir($base) AND self::exists($base . $file)) {
			return $base . DIRECTORY_SEPARATOR . $file;
		}

	   return false;
	}

	//Genera un array contenente i file trovati nella cartella indicata
	public static function listFile($dir, $ext = false) {
		$dp = FALSE;

		if(is_dir($dir)) {
			$dp = opendir($dir);
		}

		if($dp !== FALSE) {
			$return = array();
			while(FALSE !== ($file = readdir($dp))) {
				if($ext) {
					if(is_array($ext)) {
						if(in_array(strtolower(self::ext($file)), $ext)) {
							$return[$file] = $dir . $file;
						}
					} else {
						if(strtolower(self::ext($file)) == $ext) {
							$return[$file] = $dir . $file;
						}
					}
				} else {
					$return[$file] = $dir . $file;
				}
			}

         closedir($dp)
			return $return;
		}

		return false;
	}

   //Elimina un file
	public static function delete($file){
		if(self::exists($file)) {
			if(@unlink($file)) {
				return true;
			}
		}

		return false;
	}

   //Legge il contenuto di un file
	public static function read($file, $totalChunk = 0, $chunkSize = 8192, $offset = 0) {
	   if(!self::isReadable($file)) {
			return false;
		}

		if($totalChunk AND $chunkSize > $totalChunk) {
			$chunkSize = $totalChunk;
		}

		$fp = fopen($file, 'rb');

		if($fp !== FALSE) {
			if($offset) {
				fseek($fp, $offset);
			}

			$fileSize = filesize($file);

			if($fileSize < $totalChunk) {
				$totalChunk = $fileSize;
			}

			$fileData = '';
			while(!feof($fp) AND (!$totalChunk OR strlen($data) < $totalChunk)) {
				$fileData .= fread($fp, $chunkSize);
			}

			fclose($fp);

			return $fileData;
		}

		return false;
	}
}

Conclusioni

Con questo articolo si chiude la parte relativa alla creazione di YCFile.
Nei prossimi articoli affronteremo l’upload ed il download dei file. Mi raccomando, non perderli!

Ti ho presentato alcuni dei problemi che ho avuto io durante la gestione dei file.
Hai alcune idee per poter migliorare ulteriormente YCFile? Pensi che sia complessa?
Hai pensato che questi metodi possono essere riscritti in brevissimo tempo per poter essere utilizzati sulle directory?
Probabilmente pubblicherò una mini guida sulla classe YCDir e il suo utilizzo, ma nell’attesa perchè non provi tu a crearla?

Download
Tag: , ,

L'autore

Sviluppo per il web da alcuni anni. Ultimamente mi sono specializzato in WordPress tramite Your Inspiration, con il quale attualmente lavoro nell'area di supporto clienti e sviluppo temi e plugin per WP.

Sito web dell'autore | Altri articoli scritti da

Articoli correlati

Potresti essere interessato anche ai seguenti articoli:

  • Come gestire i file: le basi (Parte 1)

    “Bene, non mi resta altro da fare che caricare questa nuova copertina e la galleria fotografica sarà pronta!… Ma... come è possibile che il file...

  • C’è ancora bisogno dell’attività SEO?

    La SEO è quell'attività di ottimizzazione di un sito internet che, mediante l’utilizzo di parole chiave opportune, è finalizzata ad aumentare il...

  • Come gestire i file: l’upload

    Eccoci arrivati all’ultima parte della nostra guida. Abbiamo parlato fin ora della classe YCFile e del download, di come rinominare, controllare,...

26 commenti

Trackback e pingback

  1. Articoli settimana 05/02/2012 | Saverio Gravagnola
    [...] gestire i file: le basi (parte 2) (Your Inspiration [...]
  2. Come gestire i file: il download | Your Inspiration Web
    [...] articoli precedenti (Come gestire i file: le basi parte 1 e parte 2) abbiamo visto come creare la classe…
  3. Come gestire i file: le basi (Parte 1) | Your Inspiration Web
    [...] Come gestire i file: le basi (parte 2) Ecco come cercare, leggere ed eliminare i file, effettuando controlli a…
  4. Come gestire i file: l’upload | Your Inspiration Web
    [...] Come gestire i file: le basi (parte 2) Ecco come cercare, leggere ed eliminare i file, effettuando controlli a…