46. Classi per l'upload di file

In questo capitolo vedremo un esempio più complesso del precedente, ma che ci fornirà un'interfaccia più intuitiva e sicura per gestire l'upload dei file sui nostri server.

Per questo scopo ho creato due classi a cui vi consiglio più di applicare i concetti di ereditarietà e polimorfismo per creare poi le vostre classi per una libreria personalizzata più adeguata alle esigenze della vostra applicazione.

Prima di introdurre il codice della libreria è necessario sapere che esiste uno specifico tipo MIME per ogni tipo di file inviato.

Come abbiamo visto nel capitolo precedente, una volta inviato il file questo è accessibile attraverso l'array globale $_FILES[], in particolare con la chiave "type" è possibile accedere alla stringa che descrive che tipo di file è stato inviato.

Se "type" è uguale a "image/jpeg" abbiamo ricevuto una jpeg, se è uguale a "audio/x-mpeg-3" abbiamo ricevuto un file mp3 e così via ...
Una lista dettagliata dei tipi di file con i rispettivi MIME.

Iniziamo dalla classe "ErroreFile" per una gestione mirata delle eccezioni, che si appoggerà alle costanti globali degli errori di upload che abbiamo già visto nel capitolo precedente.

Nella libreria che segue ho definito inoltre altre costanti per gestire delle eccezioni non previste dal set di costanti globali.

Lo scopo principale della classe che segue è quello di fornire un set di messaggi d'errore tradotti in italiano, attraverso un array statico. Derivando da questa classe potrete quindi fornire dei messaggi in altre lingue o messaggi di errore aggiuntivi per gestire eccezioni particolari, personalizzate per le vostre applicazioni.

ErroreFile.php
<?php

	define("UPLOAD_ERR_MOVE", 7);
	define("UPLOAD_ERR_NO_FORM", 8);
	define("UPLOAD_ERR_NO_TYPE", 9);
	define("UPLOAD_ERR_UNKNOWN", 10);

	class ErroreFile extends Exception
	{
		private $errore;
		static $tipo = array(
			UPLOAD_ERR_OK => "File inviato con successo",
			UPLOAD_ERR_INI_SIZE => "Dimensione del file troppo grande",
			UPLOAD_ERR_FORM_SIZE => "Dimensione del file troppo grande",
			UPLOAD_ERR_PARTIAL => "File ricevuto parzialmente",
			UPLOAD_ERR_NO_FILE => "Nessun file ricevuto",
			UPLOAD_ERR_NO_TMP_DIR => "Cartella temporanea non trovata",
			UPLOAD_ERR_MOVE => "Impossibile archiviare il file",
			UPLOAD_ERR_NO_FORM => "Nessun file specificato",
			UPLOAD_ERR_NO_TYPE => "Formato file non consentito",
			UPLOAD_ERR_UNKNOWN => "Errore sconosciuto"

		);

		public function ErroreFile($err_no)
		{ $this->errore = (int) $err_no; }

		public function messaggio()
		{
			foreach (ErroreFile::$tipo as $chiave => $valore)
				if ($this->errore == $chiave)
					return $valore;

			return ErroreFile::$tipo[UPLOAD_ERR_UNKNOWN];
		}
	}

?>

Ho aggiunto 4 costanti per descrivere altri tipi di eccezioni che potrebbero verificarsi, facendo attenzione a non utilizzare i codici già presi dalle altre costanti predefinite, ossia 0, 1, 2, 3, 4 e 6 (non chiedetemi che fine ha fatto il 5).

Vediamo ora la classe File che si occuperà di gestire il file inviato e di controllare che sia compatibile con le nostre esigenze.


Classe File

A inizio sorgente dichiarerò anche qui un set di costanti globali, per non dover ricordare il tipo MIME dei file che consideriamo validi per la nostra applicazione. Per il mio esempio ho scelto questi :

  • GIF - "image/gif"
  • JPEG - "image/jpeg"
  • PNG - "image/png"
  • PSD - "image/psd"
  • BMP - "image/bmp"
  • TIFF - "image/tiff"
  • FLASH - "application/x-shockwave-flash"

Un'occhiata veloce agli attributi e ai metodi della classe File :

  • private $file - L'array che conterrà solo il file specificato
  • private $cartella - La cartella remota dove andrà memorizzato il file inviato
  • public function File() - Il costruttore della classe che prenderà in input il nome del file da gestire e la cartella di destinazione
  • public function tipo() - Metodo che prende un numero di parametri variabile, ossia tutti i tipi di file che consideriamo validi
  • public function dimensione() - La dimensione massima accettata per il file inviato. Prende due parametri, il primo la dimensione massima e il secondo l'unità di misura per valutare la dimensione, ossia "byte", "kb" o "mb" (default "byte")
  • public function sposta() - Sposta il file nella cartella di destinazione
  • public function immagine() - Restituisce la stringa già formattata in HTML per visualizzare l'immagine inviata
    Prima di usare verificare che il file sia un'immagine con il metodo tipo()
  • public function __get() - Restituisce il valore dell'attributo specificato
  • public function __toString() - Restituisce il nome originale del file
File.php
<?php

	require_once("ErroreFile.php");

	// MIME Types
	define("GIF", "image/gif");
	define("JPEG", "image/jpeg");
	define("PNG", "image/png");
	define("PSD", "image/psd");
	define("BMP", "image/bmp");
	define("TIFF", "image/tiff");
	define("FLASH", "application/x-shockwave-flash"); // Filmato Adobe Flash

	class File
	{
		private $file;
		private $cartella;

		public function File($nome, $cartella = "/uploads/")
		{
			if (isset($_FILES[$nome]))
			{
				$this->file = $_FILES[$nome];
				$this->cartella = $cartella;
			}
			else
				throw new ErroreFile(UPLOAD_ERR_NO_FORM);
		}

		public function tipo()
		{
			if (!func_num_args()) throw new ErroreFile(UPLOAD_ERR_NO_TYPE);

			$tipi = func_get_args();

			foreach ($tipi as $chiave => $valore)
				if ($this->file["type"] == $valore)
					return true;

			throw new ErroreFile(UPLOAD_ERR_NO_TYPE);
		}

		public function dimensione($dim, $unita = "byte")
		{
			if (strcasecmp($unita, "byte") == 0) $dim = (int) $dim;
			else if (strcasecmp($unita, "kb") == 0) $dim = (int) $dim * 1024;
			else if (strcasecmp($unita, "mb") == 0) $dim = (int) $dim * 1024 * 1024;
			else $dim = 0;

			if ($this->file["size"] > $dim) throw new ErroreFile(UPLOAD_ERR_FORM_SIZE);
		}

		public function sposta()
		{
			$ok = @move_uploaded_file($this->file["tmp_name"], $this->cartella . $this->file["name"]);

			if (!$ok) throw new ErroreFile(UPLOAD_ERR_MOVE);
		}

		public function immagine($dir, $alt = NULL)
		{
			$dimensioni = getimagesize($dir . $this->file["name"]);
			$dimensioni = $dimensioni[3]; // height="yyy" width="xxx"

			
			$imgtag = "<img src=\"$dir" . $this->file["name"] . "\" ";
			$imgtag .= "$dimensioni ";
			$imgtag .= (strlen($alt) ? "alt=\"$alt\"" : "") . " />"; // Imposta l'attributo ALT per l'immagine

			return $imgtag;
		}

		public function __get($attributo)
		{
			if (array_key_exists($attributo, $this->file))
				return $this->file[$attributo];

			return false;
		}

		public function __toString()
		{ return $this->file["name"]; }
	}

?>

Per l'esempio ho utilizzato alcune funzioni native che non avevo ancora spiegato, di seguito i nomi e la relativa documentazione :

A seguire la pagina upload.php dell'esempio precedente, riscritta per l'uso delle nuove classi.

upload.php

<?php

	require_once("File.php");

	try
	{
		$file = new File("immagine", "C:/Appserv/www/test/uploads/");
		$file->tipo(JPEG, PNG); // Accettate solo immagini in formato JPEG o PNG
		$file->dimensione(100, "kb"); // Solo immagini più piccole di 100Kb
		$file->sposta(); // Sposta il file inviato nella cartella di destinazione

		echo "Nome file : $file<br /><br />";
		echo $file->immagine("uploads/", "Immagine inviata"); // Visualizza l'immagine appena inviata
	}
	catch (ErroreFile $e)
	{ echo $e->messaggio(); }
	catch (Exception $e)
	{ echo $e->getMessage(); }

?>

Il risultato ottenuto inviando un'immagine che soddisfa i parametri dell'esempio.