Orion PHP  0.11.12
The PHP5.3 framework
upload.php
Go to the documentation of this file.
00001 <?php
00002 
00003 namespace Orion\Core;
00004 
00005 
00006 /**
00007  * \Orion\Core\Upload
00008  * 
00009  * Orion upload class for images or files
00010  *
00011  * Usage: new Upload(ID, FOLDER); upload(); [thumbnail();]
00012  *
00013  * @author Thibaut Despoulain
00014  * @license BSD 4-clauses
00015  * @version 0.2.11
00016  */
00017 class Upload
00018 {
00019     /**
00020      * Store the upload data : ['tmpfile','size','type','targetdir','targetfile','basename','thumbnail','fullpath']
00021      * @var array $_DATA
00022      */
00023     private $_DATA;
00024 
00025     /**
00026      * contains upload status
00027      * @var boolean $_SUCCESS
00028      */
00029     private $_SUCCESS = false;
00030 
00031     /**
00032      * Stores temporary form FILE
00033      * @var FILE $_INPUT
00034      */
00035     private $_INPUT;
00036 
00037     /**
00038      * Contains the upload dir path
00039      * @var string $UPLOAD_DIR
00040      */
00041     private $UPLOAD_DIR;
00042 
00043     /**
00044      * Type image/Jpeg for example
00045      * @var array $ALLOWED_FILETYPES
00046      */
00047     private $ALLOWED_FILETYPES;
00048 
00049     /**
00050      * Allow or disallow folder creation when it does not exist
00051      * @var boolean
00052      */
00053     private $ALLOW_FOLDER_CREATION = false;
00054 
00055     /**
00056      * Base upload directory placeholder
00057      * @var String
00058      */
00059     private $BASE_UPLOAD_DIR = '';
00060     
00061     // Extensions
00062     public static $BMP = array( 'bmp' );
00063     public static $JPEG = array( 'jpeg', 'jpg' );
00064     public static $PNG = array( 'png' );
00065     public static $GIF = array( 'gif' );
00066     public static $SWF = array( 'swf' );
00067     public static $XML = array( 'xml' );
00068     public static $FILE3D = array( 'dae', 'tmw' );
00069 
00070     const IMAGE_UPLOAD_DIR = 'IMAGE_UPLOAD_DIR';
00071     const FILE_UPLOAD_DIR = 'FILE_UPLOAD_DIR';
00072 
00073     /**
00074      *
00075      * @global configuration $config
00076      * @param string $id the file form identifier
00077      * @param string $folder path where to store the file, either real path or Upload::IMAGE_UPLOAD_DIR, Upload::FILE_UPLOAD_DIR to use configuration file
00078      * @return object Upload object or false if error
00079      */
00080     function __construct( $id=null, $folder=null )
00081     {
00082         if ( $id == null ) // blank usage, no bindings.
00083             return;
00084 
00085         if ( !isset( $_FILES[ $id ] ) )
00086             throw new Exception( 'File in $_FILES["' . $id . '"] not found.', E_USER_ERROR, get_class() );
00087 
00088         if ( !\Orion::config()->defined( 'UPLOAD_DIR' ) )
00089             throw new Exception( 'UPLOAD_DIR is not defined in Orion configuration.', E_ERROR, get_class() );
00090 
00091         $this->BASE_UPLOAD_DIR = \Orion::config()->get( 'UPLOAD_DIR' );
00092 
00093         if ( \Orion::config()->defined( $folder ) )
00094         {
00095             $this->UPLOAD_DIR = $this->BASE_UPLOAD_DIR . \Orion::config()->get( $folder );
00096             if ( !file_exists( $this->UPLOAD_DIR ) )
00097                 throw new Exception( 'Upload directory read from configuration does not exist.', E_ERROR, get_class() );
00098         }
00099         else
00100         {
00101             if ( file_exists( $this->BASE_UPLOAD_DIR . $folder ) )
00102             {
00103                 $this->UPLOAD_DIR = $this->BASE_UPLOAD_DIR . $folder;
00104             }
00105             else
00106             {
00107                 throw new Exception( 'Please provide a valid path to upload file.', E_USER_ERROR, get_class() );
00108             }
00109         }
00110 
00111         if ( !is_writable( $this->UPLOAD_DIR ) )
00112             throw new Exception( 'Directory [' . Security::preventInjection( $this->UPLOAD_DIR ) . '] is not writable. Upload failed.', E_USER_ERROR, get_class() );
00113 
00114         $this->_INPUT = &$_FILES[ $id ];
00115 
00116         $this->_DATA = $this->getData();
00117     }
00118 
00119     /**
00120      * Get useful data for upload
00121      * @return array data
00122      */
00123     private function getData()
00124     {
00125         $data = pathinfo( $this->_INPUT[ 'name' ] );
00126         $data[ 'extension' ] = strtolower($data[ 'extension' ]);
00127         $data[ 'tmpfile' ] = $this->_INPUT[ 'tmp_name' ];
00128         $data[ 'size' ] = $this->_INPUT[ 'size' ];
00129         $data[ 'type' ] = $this->_INPUT[ 'type' ];
00130         $data[ 'targetdir' ] = $this->UPLOAD_DIR;
00131         $data[ 'targetfile' ] = $data[ 'filename' ].'.'.$data['extension'];
00132 
00133         return $data;
00134     }
00135 
00136     /**
00137      * Prepare upload by checking parameters integrity and dupe existence
00138      * @return boolean success or failure
00139      */
00140     private function prepare()
00141     {
00142         if ( $this->ALLOW_FOLDER_CREATION && !is_dir( $this->_DATA[ 'targetdir' ] ) )
00143             mkdir( $this->_DATA[ 'targetdir' ], 0755, true );
00144 
00145         if ( !file_exists( $this->_DATA[ 'targetdir' ] ) )
00146             throw new Exception( 'Target directory [' . $this->_DATA[ 'targetdir' ] . '] does not exist', E_USER_ERROR, get_class() );
00147         if ( !is_uploaded_file( $this->_DATA[ 'tmpfile' ] ) )
00148             throw new Exception( 'Internal error, unable to upload image.', E_USER_ERROR, get_class() );
00149         if ( !empty( $this->ALLOWED_FILETYPES ) && !in_array( $this->_DATA[ 'extension' ], $this->ALLOWED_FILETYPES ) )
00150             throw new Exception( 'Trying to upload an unauthorized filetype', E_USER_ERROR, get_class() );
00151         if ( strpos( $this->_DATA[ 'targetfile' ], '..' ) !== false )
00152             throw new Exception( 'Unauthorized ".." char in file name.', E_USER_ERROR, get_class() );
00153 
00154         if ( file_exists( $this->_DATA[ 'targetdir' ] . $this->_DATA[ 'targetfile' ] ) )
00155             $this->fixDupe();
00156 
00157         return true;
00158     }
00159 
00160     /**
00161      * Define a new filename if file already exists (e.g. filename-1.ext)
00162      */
00163     private function fixDupe()
00164     {
00165         $i = 1;
00166         $tmpname = $this->_DATA[ 'targetfile' ];
00167         $path = pathinfo( $this->_DATA[ 'targetfile' ] );
00168         while ( file_exists( $this->_DATA[ 'targetdir' ] . $tmpname ) )
00169         {
00170             $tmpname = $path[ 'filename' ] . '-' . $i . '.' . $path[ 'extension' ];
00171             $i++;
00172         }
00173 
00174         $this->_DATA[ 'targetfile' ] = $tmpname;
00175     }
00176 
00177     /**
00178      * Allows creation of folder when it does not exist
00179      */
00180     public function enableFolderCreation()
00181     {
00182         $this->ALLOW_FOLDER_CREATION = true;
00183     }
00184 
00185     /**
00186      * Adds a prefix to the filename
00187      * @param string $prefix
00188      */
00189     public function setPrefix( $prefix )
00190     {
00191         $this->_DATA[ 'prefix' ] = $prefix;
00192         $this->_DATA[ 'targetfile' ] = $prefix . $this->_DATA[ 'targetfile' ];
00193     }
00194 
00195     /**
00196      * Puts the file into provided sub folder(s)
00197      * @param string $folders Sub folders (multiple = func_get_args)
00198      */
00199     public function setSubFolder( $folders=null )
00200     {
00201         $subpath = '';
00202         $args = func_get_args();
00203         if ( empty( $args ) )
00204             return;
00205 
00206         foreach ( $args as $folder )
00207         {
00208             if ( !empty( $folder ) )
00209             {
00210                 $subpath .= $folder . DS;
00211             }
00212         }
00213 
00214         $this->_DATA[ 'targetdir' ] .= $subpath;
00215     }
00216 
00217     /**
00218      * Restricts file types
00219      * @param [string|string[]] mime types
00220      */
00221     public function restrict()
00222     {
00223         $arr = func_get_args();
00224         foreach ( $arr as $type )
00225         {
00226             if ( is_array( $type ) )
00227                 foreach ( $type as $item )
00228                     $this->ALLOWED_FILETYPES[ ] = $item;
00229             else
00230                 $this->ALLOWED_FILETYPES[ ] = $type;
00231         }
00232     }
00233 
00234     /**
00235      * Create a thumbnail using uploaded image as source
00236      * @param int[] $maxsize in pixels or an array [width, height]
00237      * @param string $newname The new name for the thumbnail
00238      * @param string [$_targetdir] The target directory if the thumbnail needs to be in another directory
00239      * @param string [$_source] The source image path (To generate a thumbnail not from an uploaded image, but from a static file on the server)
00240      * @param string[] [$format] Provide an overriden format of the thumbnail. This forat consists of an array if the imagenew method in [0] and the extention in [1], example: array('ImageJpeg', 'jpg')
00241      * @return boolean success or failure
00242      */
00243     public function thumbnail( $maxsize, $newname=null, $_targetdir=null, $_source=null, $format=null )
00244     {
00245         if ( !$this->_SUCCESS && $_source == null )
00246             throw new Exception( 'You need to execute upload() before calling thumbnail().', E_USER_WARNING, get_class() );
00247         
00248         if( $_source != null && !file_exists( $_source) )
00249             throw new Exception( 'Source image does not exist' );
00250 
00251         $dir = ($_targetdir == null ? $this->_DATA[ 'targetdir' ] . '.thumb/' : $_targetdir);
00252         
00253         if(!is_dir($dir))
00254             mkdir( $dir, 0755, true );
00255             
00256         if($_source != null)
00257         {  
00258             $info = pathinfo($_source);
00259             $ext = $info[ 'extension' ];
00260             $fullpath = $_source;
00261         }
00262         else
00263         {
00264             $ext = $this->_DATA[ 'extension' ];
00265             $fullpath = $this->_DATA[ 'fullpath' ];
00266         }
00267         
00268         if ( !in_array( $ext, array('jpg','jpeg','png','gif') ) )
00269             throw new Exception( 'Unable to resize image to create thumbnail.', E_USER_WARNING, get_class() );
00270         
00271         $target = empty( $newname ) ? $dir . $this->_DATA[ 'targetfile' ] : $dir . $newname . $ext;
00272 
00273         switch ( $ext )
00274         {
00275             case "jpeg":
00276             case "jpg":
00277                 $function_image_create = "ImageCreateFromJpeg";
00278                 $function_image_new = "ImageJpeg";
00279                 break;
00280             case "png":
00281                 $function_image_create = "ImageCreateFromPng";
00282                 $function_image_new = "ImagePNG";
00283                 break;
00284             case "gif":
00285                 $function_image_create = "ImageCreateFromGif";
00286                 $function_image_new = "ImageGif";
00287                 break;
00288             default:
00289                 throw new Exception( 'Image was not resized : type not supported.', E_USER_NOTICE, get_class() );
00290                 break;
00291         }
00292         
00293         if($format != null && is_array($format))
00294         {
00295             $function_image_new = $format[0];
00296             $pos = strlen($target) - strlen($ext);
00297             $target = substr( $target, 0, $pos) . $format[1];
00298         }
00299 
00300         list($width, $height) = getimagesize( $fullpath );
00301         if(is_array($maxsize))
00302         {
00303             $newwidth = $maxsize[0];
00304             $newheight = $maxsize[1];
00305             $newratio = $newwidth/$newheight;
00306             $ratio = $width / $height;
00307             
00308             if($ratio > $newratio)
00309             { // fixheight
00310                 $cropheight = $height;
00311                 $cropwidth = $height*$newratio;
00312                 $cropx = floor(($width - $cropwidth)/2);
00313                 $cropy = 0;
00314             }
00315             else
00316             { // fixwidth
00317                 $cropheight = $width/$newratio;
00318                 $cropwidth = $width;
00319                 $cropx = 0;
00320                 $cropy = floor(($height - $cropheight)/2);
00321             }
00322             
00323             $thumb = ImageCreateTrueColor( $newwidth, $newheight );
00324             $source = @$function_image_create( $fullpath );
00325 
00326             ImageCopyResampled( $thumb, $source, 0, 0, $cropx, $cropy, $newwidth, $newheight, $cropwidth, $cropheight );
00327 
00328             $this->_DATA[ 'thumbnail' ] = $target;
00329 
00330             if ( @$function_image_new( $thumb, $target ) )
00331                 $this->_SUCCESS = true;
00332 
00333             return $this->_SUCCESS;
00334         }
00335         elseif ( $width > $maxsize || $height > $maxsize ) // below is a compatibility fallback for ratio-keeping resize (ignore)
00336         {
00337             $ratio = $height / $width;
00338             $newheight = ($height > $width) ? $maxsize : $maxsize * $ratio;
00339             $newwidth = ($width > $height) ? $maxsize : $newheight / $ratio;
00340 
00341             $thumb = ImageCreateTrueColor( $newwidth, $newheight );
00342             $source = @$function_image_create( $fullpath );
00343 
00344             ImageCopyResampled( $thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );
00345 
00346             $this->_DATA[ 'thumbnail' ] = $target;
00347 
00348             if ( @$function_image_new( $thumb, $target ) )
00349                 $this->_SUCCESS = true;
00350 
00351             return $this->_SUCCESS;
00352         }
00353 
00354         throw new Exception( 'Image was not resized : Image is too small.', E_USER_NOTICE, get_class() );
00355     }
00356 
00357     /**
00358      * Process the final upload using copy()
00359      * @return boolean success or failure
00360      */
00361     public function upload()
00362     {
00363         if ( !$this->prepare() )
00364             throw new Exception( 'Internal error, unable to prepare image for upload.', E_USER_ERROR, get_class() );
00365 
00366         $this->_DATA[ 'fullpath' ] = $this->_DATA[ 'targetdir' ] . $this->_DATA[ 'targetfile' ];
00367         $this->_SUCCESS = copy( $this->_DATA[ 'tmpfile' ], $this->_DATA[ 'fullpath' ] );
00368         
00369         if ( !$this->_SUCCESS )
00370             throw new Exception( 'Internal error, unable to copy image to upload directory.', E_USER_ERROR, get_class() );
00371 
00372         return true;
00373     }
00374 
00375     /**
00376      * Used to retrive an identifier to store in a database for example
00377      * @param string $type 'path' or 'name' for full path or only filename
00378      * @return string Identifier
00379      */
00380     public function getIdentifier( $type='path', $noprefix=false )
00381     {
00382         switch ( $type )
00383         {
00384             case 'name':
00385             case 'NAME':
00386                 if ( $noprefix && isset( $this->_DATA[ 'prefix' ] ) )
00387                     return substr( $this->_DATA[ 'targetfile' ], strlen( $this->_DATA[ 'prefix' ] ) );
00388                 else
00389                     return $this->_DATA[ 'targetfile' ];
00390                 break;
00391             case 'path':
00392             case 'PATH':
00393             default:
00394                 return $this->_DATA[ 'targetdir' ] . $this->_DATA[ 'targetfile' ];
00395                 break;
00396         }
00397     }
00398     
00399     public function isImage($extension=null)
00400     {
00401         return in_array( ($extension==null ? $this->_DATA[ 'extension'] : $extension ), array('jpg','jpeg','png','gif') );
00402     }
00403 
00404     /**
00405      * Gets uploaded file size in Octets
00406      * @return int 
00407      */
00408     public function getSize()
00409     {
00410         return $this->_DATA[ 'size' ];
00411     }
00412 
00413     /**
00414      * Used to retrive an identifier of the thumbnail to store in a database for example
00415      * @param string $type 'path' or 'name' for full path or only filename of the thumbnail
00416      * @return string Thumbnail identifier
00417      */
00418     public function getThumbnailIdentifier( $type='path' )
00419     {
00420         switch ( $type )
00421         {
00422             case 'name':
00423             case 'NAME':
00424                 return basename( $this->_DATA[ 'thumbnail' ] );
00425                 break;
00426             case 'path':
00427             case 'PATH':
00428             default:
00429                 return $this->_DATA[ 'thumbnail' ];
00430                 break;
00431         }
00432     }
00433 
00434     /**
00435      * Returns if the Upload was a success or a failure
00436      * @return boolean success or failure
00437      */
00438     public function isSuccess()
00439     {
00440         return $this->_SUCCESS;
00441     }
00442 
00443     /**
00444      * Deletes a file from upload dir
00445      * @param String $path The relative path to the file to delete. (relative to the upload directory)
00446      * @return boolean 
00447      */
00448     public static function delete( $path=null )
00449     {
00450         if ( $path == null )
00451             throw new Exception( 'Trying to delete a file with an empty path.' );
00452 
00453         if ( !\Orion::config()->defined( 'UPLOAD_DIR' ) )
00454             throw new Exception( 'UPLOAD_DIR is not defined in Orion configuration.', E_ERROR, get_class() );
00455 
00456         $uploadDir = \Orion::config()->get( 'UPLOAD_DIR' );
00457 
00458         if ( !Tools::startWith( $path, $uploadDir ) )
00459             $path = $uploadDir . $path;
00460 
00461         if ( !file_exists( $path ) )
00462             throw new Exception( 'Trying to delete a file that does not exist' );
00463 
00464         if ( !@unlink( $path ) )
00465             throw new Exception( 'Internal error. Could not delete file.' );
00466 
00467         return true;
00468     }
00469 
00470     /**
00471      *
00472      * @param String $directory The relative path to the directory to empty. (relative to the upload directory)
00473      * @param boolean $empty Set this to TRUE to only empty the directory, FALSE|NULL to empty AND remove the directory
00474      * @return boolean 
00475      */
00476     public static function deleteDir( $directory, $empty = false )
00477     {
00478         if ( !\Orion::config()->defined( 'UPLOAD_DIR' ) )
00479             throw new Exception( 'UPLOAD_DIR is not defined in Orion configuration.', E_ERROR, get_class() );
00480 
00481         $base = $directory;
00482         $directory = \Orion::config()->get( 'UPLOAD_DIR' ) . $directory;
00483 
00484         if ( substr( $directory, -1 ) == "/" )
00485         {
00486             $directory = substr( $directory, 0, -1 );
00487         }
00488 
00489         if ( !file_exists( $directory ) || !is_dir( $directory ) )
00490         {
00491             return false;
00492         }
00493         elseif ( !is_readable( $directory ) )
00494         {
00495             return false;
00496         }
00497         else
00498         {
00499             $directoryHandle = opendir( $directory );
00500 
00501             while ( $contents = readdir( $directoryHandle ) )
00502             {
00503                 if ( $contents != '.' && $contents != '..' )
00504                 {
00505                     $path = $directory . "/" . $contents;
00506 
00507                     if ( is_dir( $path ) )
00508                     {
00509                         self::deleteDir( $base."/".$contents );
00510                     }
00511                     else
00512                     {
00513                         unlink( $path );
00514                     }
00515                 }
00516             }
00517 
00518             closedir( $directoryHandle );
00519 
00520             if ( $empty == false )
00521             {
00522                 if ( !rmdir( $directory ) )
00523                 {
00524                     return false;
00525                 }
00526             }
00527 
00528             return true;
00529         }
00530     }
00531     
00532     /**
00533      * Get the size of a file/directory in K-blocks (~Ko)
00534      * /!\ Works only on Linux servers /!\
00535      * @param String $dir Path
00536      * @return int The size in K-blocks (~Ko)
00537      */
00538     public static function getSizeOf( $dir )
00539     {
00540         $f = Security::sanitizePath( $dir );
00541         if(!file_exists($f))
00542             return 0;
00543         else
00544         {
00545             $io = popen ( '/usr/bin/du -sk "' . $f.'"', 'r' );
00546             $size = fgets ( $io, 4096);
00547             $size = substr ( $size, 0, strpos ( $size, ' ' ) );
00548             pclose ( $io );
00549             return intval($size);
00550         }
00551     }
00552 
00553 }
00554 
00555 ?>