[php5] - classe d'upload

Contenu du snippet

Oui je sais, ca fait encore une de plus ! :)
Mais personnellement je n'ai rien trouvé qui me convienne en Php5.

Cette classe essaye au maximum d'éviter des attaques lors de l'upload : gestion des erreurs retournées par http, vérification de la taille avec filesize, aboli l'usage de caractères dangeureux (permettant des actions pas très saines !), et vérification de toutes les étapes importantes lors du transfert.

De plus vous avez la possibilité de choisir si vous voulez déposer le fichier dans un endroit particulier, avec un nom particulier, si vous voulez créer les dossiers et sous dossiers ou ira votre fichier, si vous souhaitez renommer le fichier s'il existe déjà un fichier du même nom, et si vous voulez avoir un fichier ayant un nom propre (alphanumérique, ., _ et -)

Regardez la source pour plus de compréhension :)

Source / Exemple :


<?php
/**

  • @name SpecialException
  • @filesource SpecialException.php
  • @author Cyril Nicodème
  • @license Gnu/Agpl
  • @version 0.1
  • @since 18/01/2008
  • /
class SpecialException extends Exception { public function __construct ($iCode, $iParameter = null) { // Ici vous pouvez appeler un fichier xml pour aller dans le noeud __lang__>Class>Code // Class étant la classe à l'origine de l'erreur // Et le code le code retourné par l'erreur // Comme cela vous disposez d'une gestion d'erreur multilangue // C'est qu'une idée, c'est pour ca que je n'ai mis que des commentaires, que vous adaptiez la classe à vos besoins ! :) } } ?> <?php /**
  • @name InvalidParameterException
  • @filesource InvalidParameterException.php
  • @author Cyril Nicodème
  • @license Gnu/Agpl
  • @version 0.1
  • @since 18/01/2008
  • /
class InvalidParameterException extends SpecialException { const BOOLEAN_NEEDED = 0; const INT_NEEDED = 1; const STRING_NEEEDED = 2; const ARRAY_NEEDED = 4; const RESOURCE_NEEDED = 8; const OBJECT_NEEDED = 16; const INVALID_KEY = 32; } ?> <?php define ('DIR_S', DIRECTORY_SEPARATOR); /**
  • @name UploadException
  • /
class UploadException extends SpecialException {}; /**
  • @name Upload
  • @filesource Upload.php
  • @author Cyril Nicodème
  • @license Gnu/Agpl
  • @version 0.1
  • @since 12/01/2008
  • /
class UploadHelper { /**
  • @property Constants of designed error
  • /
const UNAUTHORIZED_FORM_SIZE = 1; // The file size is over the form authorized size const UNAUTHORIZED_SIZE = 2; // The file size is over the authorized size const UNALLOWED_EXTENSION = 3; // The file does not have an authorized extension const ILLEGAL_FILE_NAME = 4; // The file contains Illegal Characters const INCOMPLETE_FILE = 10; // The file was not completely uploaded const UNUPLOADED_FILE = 11; // No file was uploaded const INVALID_FILE = 12; // The file is not a valid uploaded file const INVALID_IMAGE_FILE = 13; // The file is not a valid Image File const UNABLE_CREATE_FOLDER = 20; // Unable to create a subfolder const INEXISTANT_DESTINATION = 21; // Destination Folder does not exists ! const EXISTANT_FILE = 22; // The file already exists const INEXISTANT_FILE = 23; // The file does not exists const UNABLE_TO_MOVE = 24; // Unable to upload the file. Maybe you haven\'t to write into the folder ? /**
  • @var Array $_aParameters
  • Contain all the parameters
  • /
private $_aParameters = array ( // @var String : Contain the Path to the destination folder 'destinationFolder' => '', // @var String : Contain the name of the file 'fileName' => '', // @var String : Contain the destination folder + the file name 'filePath' => '', // @var Array : Contain an array of Allowed extensions. If it's an empty array, all the extensions will be allowed 'allowedExt' => array (), // @var boolean : Indicate if we rename the file (if already exists) or not 'rename' => false, // @var boolean : Indicate if we cleaning the file from strange characters (allow only alphanumeric, ., - and _) 'cleanFileName' => false, // @var boolean : Indicate if we create the subfolder for the destinationFolder value 'createSubFolders' => true, // @var int : Indicate the max file size for the uploaded file 'maxFileSize' => 0, // @var boolean : Indicate if we need to check if the file is an image 'isImage' => false); /**
  • @name __set
  • Modify the values : destinationFolder, fileName, filePath, allowedExt, rename, cleanFileName, createSubFolders, maxFileSize, isImage
  • @param String $sKey
  • @param Mixed (String, Array, Boolean) $mValue
  • @return void
  • /
public function __set ($sKey, $mValue) { if (!is_string ($sKey)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); switch (strtolower ($sKey)) { case 'destinationfolder': if (!is_string ($mValue)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 2); $mValue = str_replace(array ("\\", "/"), DIR_S, $mValue); if (substr ($mValue, -1) != DIR_S) $mValue .= DIR_S; $this->_aParameters['destinationFolder'] = $mValue; break; case 'filename': if (!is_string ($mValue)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 2); $this->_aParameters['fileName'] = $mValue; break; case 'filepath': if (!is_string ($mValue)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 2); $mValue = str_replace(array ("\\", "/"), DIR_S, $mValue); $this->_aParameters['destinationFolder'] = substr ($mValue, 0, strrpos ($mValue, DIR_S)+1); $this->_aParameters['fileName'] = substr ($mValue, strrpos ($mValue, DIR_S)+1); $this->_aParameters['filePath'] = $mValue; break; case 'allowedext': if (!is_array ($mValue)) throw new InvalidParameterException (InvalidParameterException::ARRAY_NEEEDED, 2); $this->_aParameters['allowedExt'] = $mValue; break; case 'rename': if (!is_bool ($mValue)) throw new InvalidParameterException (InvalidParameterException::BOOLEAN_NEEDED, 2); $this->_aParameters['rename'] = $mValue; break; case 'cleanfilename': if (!is_bool ($mValue)) throw new InvalidParameterException (InvalidParameterException::BOOLEAN_NEEDED, 2); $this->_aParameters['cleanFileName'] = $mValue; break; case 'createsubfolders': if (!is_bool ($mValue)) throw new InvalidParameterException (InvalidParameterException::BOOLEAN_NEEDED, 2); $this->_aParameters['createSubFolders'] = $mValue; break; case 'maxfilesize': if (!is_int ($mValue)) throw new InvalidParameterException (InvalidParameterException::INT_NEEDED, 2); $this->_aParameters['maxFileSize'] = $mValue; break; case 'isimage': if (!is_bool ($mValue)) throw new InvalidParameterException (InvalidParameterException::BOOLEAN_NEEDED, 2); $this->_aParameters['isImage'] = $mValue; break; default: throw new InvalidParameterException (InvalidParameterException::INVALID_KEY, $sKey); } } /**
  • @name __get
  • Get the values : destinationFolder, fileName, filePath, allowedExt, rename, cleanFileName, createSubFolders, maxFileSize, isImage
  • @param String $sKey
  • @return Mixed (String, Array, Boolean)
  • /
public function __get ($sKey) { if (!is_string ($sKey)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); if (isset ($this->_aParameters [$sKey])) return $this->_aParameters [$sKey]; else throw new InvalidParameterException (InvalidParameterException::INVALID_KEY, $sKey); } /**
  • @name addAllowedExtension
  • Add a specific extension or an array of extensions
  • @param Mixed (Array, String) $mValue
  • @return void
  • /
public function addAllowedExtension ($mValue) { if (is_array ($mValue)) $this->_aParameters['allowedExt'] = array_merge ($this->_aParameters['allowedExt'], $mValue); elseif (is_string ($mValue)) $this->_aParameters['allowedExt'] [] = $mValue; else throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED + InvalidParameterException::ARRAY_NEEEDED, 1); } /**
  • @name cleanAllowedExtension
  • Set the $_aAllowedExt array empty
  • @return void
  • /
public function cleanAllowedExtension () { $this->_aParameters['allowedExt'] = array (); } /**
  • @name createSubFolders
  • Create sub folders from a specific path or from the $_sDestinationFolder
  • @param String $sFolderToCreate (optional)
  • @return void
  • /
public function createSubFolders ($sFolderToCreate=null) { if (isset ($sFolderToCreate) && !is_string ($sFolderToCreate)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); if (!isset ($sFolderToCreate)) $sFolderToCreate = $this->_aParameters['destinationFolder']; $aFolders = explode (DIR_S, $sFolderToCreate); $sFinalFolder = ''; foreach ($aFolders as $sFolder) { $sFinalFolder .= $sFolder.DIR_S; if (!is_dir ($sFinalFolder)) { if (!mkdir ($sFinalFolder)) throw new UploadException (UploadHelper::UNABLE_CREATE_FOLDER); } } } /**
  • @name cleanFileName
  • Modify the value to be only alphanumeric, _, - and .
  • @param String $sFileName
  • @return String
  • /
public function cleanFileName ($sFileName) { if (!is_string ($sFileName)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); $aSearch = array ('#à|â|ä#i', '#é|è|ê|ë#i', '#î|ï#i', '#ô|ö#i', '#ù|û|ü#i', '#ç#i', '#&#i', '#@#i', "# |'#", '#"#', '#[^a-zA-Z0-9_\.-]*#i'); $aReplace = array('a', 'e', 'i', 'o', 'u', 'c', '_and_', 'at', '_'); return preg_replace($aSearch, $aReplace, strtolower($sFileName)); } /**
  • @name renameFile
  • Rename a specific value while the file from the given file path exists and return the new file path
  • @param String $sFilePath
  • @return String
  • /
public function renameFile ($sFilePath) { if (!is_string ($sFilePath)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); $sFileName = substr ($sFilePath, strrpos ($sFilePath, DIR_S)+1); $sDestinationFolder = substr ($sFilePath, 0, strrpos ($sFilePath, DIR_S)+1); $sBaseName = substr ($sFileName, 0, strrpos ($sFileName, '.')); $sExtension = '.'.preg_replace ('`.*\.([^\.]*)$`', '$1', $sFileName); $sAdd = ''; $iWhile = 0; while (file_exists ($sDestinationFolder.$sBaseName.$sAdd.$sExtension)) { $sAdd = '('.$iWhile.')'; $iWhile++; } return $sDestinationFolder.$sBaseName.$sAdd.$sExtension; } /**
  • @name isImage
  • Get if the file is an image or not
  • @param String $sFilePath
  • @return Boolean
  • /
public function isImage ($sFilePath) { if (!is_string ($sFilePath)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); if (!file_exists ($sFilePath)) throw new UploadException (UploadHelper::INEXISTANT_FILE); $aParams = @getimagesize($sFilePath); /*
  • 1 = IMAGETYPE_GIF
  • 2 = IMAGETYPE_JPEG
  • 3 = IMAGETYPE_PNG
  • 4 = IMAGETYPE_SWF
  • 5 = IMAGETYPE_PSD
  • 6 = IMAGETYPE_BMP
  • 7 = IMAGETYPE_TIFF_II (ordre d'octets d'Intel)
  • 8 = IMAGETYPE_TIFF_MM (ordre d'octets Motorola)
  • 9 = IMAGETYPE_JPC
  • 10 = IMAGETYPE_JP2
  • 11 = IMAGETYPE_JPX
  • 12 = IMAGETYPE_JB2
  • 13 = IMAGETYPE_SWC
  • 14 = IMAGETYPE_IFF
  • 15 = IMAGETYPE_WBMP
  • 16 = IMAGETYPE_XBM
  • /
if (!isset ($aParams[2])) return false; elseif ($aParams[2] > 0 && $aParams[2] < 16) return true; else return false; } /**
  • @name upload
  • Upload the file given into the $_sDestinationFolder given and return the file path
  • @param Array $aSubmittedFile
  • @return String
  • /
public function upload ($aSubmittedFile) { if (!is_array ($aSubmittedFile)) throw new InvalidParameterException (InvalidParameterException::STRING_NEEEDED, 1); if ($aSubmittedFile['error'] == UPLOAD_ERR_INI_SIZE) throw new UploadException (UploadHelper::UNAUTHORIZED_SIZE); if ($aSubmittedFile['error'] == UPLOAD_ERR_FORM_SIZE) throw new UploadException (UploadHelper::UNAUTHORIZED_FORM_SIZE); if ($aSubmittedFile['error'] == UPLOAD_ERR_PARTIAL) throw new UploadException (UploadHelper::INCOMPLETE_FILE); if ($aSubmittedFile['error'] == UPLOAD_ERR_NO_FILE) throw new UploadException (UploadHelper::UNUPLOADED_FILE); if (!is_uploaded_file ($aSubmittedFile['tmp_name'])) throw new UploadException (UploadHelper::INVALID_FILE); if (isset ($this->_aParameters['maxFileSize']) && filesize ($aSubmittedFile['tmp_name']) > $this->_aParameters['maxFileSize']) throw new UploadException (UploadHelper::UNAUTHORIZED_SIZE); if (preg_match('#[\x00-\x1F\x7F-\x9F/\\\\]#', $aSubmittedFile['name'])) throw new UploadException (UploadHelper::ILLEGAL_FILE_NAME); if ((count ($this->_aParameters['allowedExt']) > 0) && !(in_array (preg_replace ('`.*\.([^\.]*)$`', '$1', $aSubmittedFile['name']), $this->_aParameters['allowedExt']))) throw new UploadException (UploadHelper::UNALLOWED_EXTENSION); if ($this->_aParameters['isImage'] && !$this->isImage($aSubmittedFile['tmp_name'])) throw new UploadException (UploadHelper::INVALID_IMAGE_FILE); if (!is_dir ($this->_aParameters['destinationFolder']) && $this->_aParameters['createSubFolders']) $this->createSubFolders (); if (!is_dir ($this->_aParameters['destinationFolder']) && !$this->_aParameters['createSubFolders']) throw new UploadException (UploadHelper::INEXISTANT_DESTINATION); $sFileName = (isset ($this->_aParameters['fileName'])) ? $this->_aParameters['fileName'] : $aSubmittedFile['name']; if ($this->_aParameters['cleanFileName']) $sFileName = $this->cleanFileName ($sFileName); $sFilePath = $this->_aParameters['destinationFolder'].$sFileName; if (file_exists ($sFilePath) && !$this->_aParameters['rename']) throw new UploadException (UploadHelper::EXISTANT_FILE); if (file_exists ($sFilePath) && $this->_aParameters['rename']) $sFilePath = $this->renameFile ($sFilePath); if (!move_uploaded_file ($aSubmittedFile['tmp_name'], $sFilePath)) throw new UploadException (UploadHelper::UNABLE_TO_MOVE); $this->_aParameters['filePath'] = $sFilePath; return $sFilePath; } } ?> Et l'exemple : <?php if (isset ($_FILES['fichier'])) { require_once ('../Alternativ/Helpers/UploadHelper.php'); $oU = new UploadHelper (); // Vous devez spécifier un répertoire de destination ! $oU->destinationFolder = "./repertoire/dupload/"; // Ensuite vous pouvez spécifier un nom de fichier, s'il n'y en a pas, // ce sera celui fournit par l'utilisateur qui sera pris $oU->fileName = "nomdefichier.extension"; // Au lieu d'utiliser la commande $oU->destinationFolder + $oU->fileName // Vous pouvez utiliser celle-ci, strictement identique ! $oU->filepath = "./repertoire/dupload/nomdefichier.extension"; // Vous pouvez spécifier un tableau d'extensions autorisées $oU->allowedExt = array ('png', 'bmp', 'gif', 'jpg', 'jpeg'); // Vous pouvez aussi passer par les fonctions addAllowedExtension en indiquant un tableau d'extensions // ou juste une extension. Dans les deux cas, les valeurs seront ajoutées au tableau en cours // Pour vider le tableau, vous faites $oU->allowedExt = array (); ou vous appelez la méthode $oU->cleanAllowedExtension (); // Si le tableau est vide, toutes les extensions seront autorisées // Si rename est à true, cela permet de renommer le fichier en ajoutant un (x) (ou x = 0 à n) // avec n un nombre tant que le fichier existe, ce qui peux donner fichier.txt, ou s'il existe // fichier(0).txt, ou s'il existe, fichier(1).txt, etc $oU->rename = true; // Valeur par défaut : false; // Si cleanFileName est à true, cela aura pour effet de remplacer tous les caractères autre que // alphanumérique, dot et _ par leur équivalent alphanumérique (é=>e par exemple) ou par un _ $oU->cleanFileName = true; // Valeur par défaut : false; // Si le repertoire dans lequel le fichier sera déposé n'exisite pas et que createSubFolders et à true // alors la classe créera tous les dossiers et sous dossiers nécéssaires. $oU->createSubFolders = true; // Valeur par défaut : true; // Limite la taille de chargement, et fait une vérification sur la taille réelle du fichier // (n'interprete pas les valeurs du navigateur (car elles peuvent être faussées)) $oU->maxFileSize=512000; // Si ce parametre est à true, va appeler la méthode isImage afin de vérifier l'en tête du fichier pour tester si c'est bien une image ! $oU->isImage = true; // On lance l'upload ! :) $oU->upload ($_FILES['fichier']); } else { ?> <form action="index.php" method="post" enctype="multipart/form-data"> <p><input type="file" name="fichier" /><input type="submit" value="Envoyer" /></p> </form> <?php } ?>

Conclusion :


Bon, ce n'est pas quelque chose de très sorcier, mais ca peux toujours servir.

Si toutefois vous avez des remarques, conseils et autre, n'hésitez pas ! :)

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.