Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/mvc/components/file

[return to app]
1 <?php
2
/**
3  * Tools for file management and image modification
4  */
5
class fileComponent {
6     
/**
7      * Cache of files as they are processed
8      * @var array
9      */
10     
public $files = array(), $resizedFiles = array();
11
12     
/**
13      * Get the dimensions of an image file, results are cached in the $this->files array and are indexed by $src
14      *
15      * @param string $src Location of the image file
16      * @return mixed Array on success, boolean false on failure
17      */
18     
public function getImageSize($src) {
19         
//error-suppression is needed to avoid error warnings from invalid images
20         
$imageSize = @getimagesize($src);
21         if (
$imageSize) {
22             list(
$this->files[$src]['width'], $this->files[$src]['height'], $this->files[$src]['type']) =
 
$imageSize;
23         }
24         return 
$imageSize;
25     }
26
27     
/**
28      * Get the format (gif, jpeg, etc.) of an image file
29      *
30      * @param string $src
31      * @return mixed String for standard image types, int for non-standard, boolean false on failure
32      */
33     
public function getImageFormat($src) {
34         if (!isset(
$this->files[$src])) {
35             if (
function_exists('exif_imagetype')) {
36                 return 
exif_imagetype($src);
37             } else if (!
$this->getImageSize($src)) {
38                 return 
false;
39             }
40         }
41         
//matches the PHP IMAGETYPE constants relevant to standard images (the two TIFF types are 7 & 8)
42         
$formats = array(=> 'gif''jpg''png''swf''psd''bmp''tif''tif'15 => 'bmp');
43         return (isset(
$formats[$this->files[$src]['type']]) ? $formats[$this->files[$src]['type']]
44                                                             : 
$this->files[$src]['type']);
45     }
46
47     
/**
48      * Validates a file uploaded from a form
49      *
50      * @param string $id
51      * @param boolean or int $imagesOnly, a value of -1 will populate $this->files via getImageSize() but won't
 
throw an
52      
*                                    error for non-images
53      
* @return boolean
54      
*/
55     public function 
validateUploadedFile($id$imagesOnly false) {
56         if (
substr($_FILES[$id]['name'], 01) == '.') {
57             
$error 'File names must not begin with a dot';
58         } else if (!isset(
$_FILES[$id]['error'])) {
59             
$error 'File upload failed, please try again';
60         } else {
61             switch (
$_FILES[$id]['error']) {
62                 case 
'0':
63                     if (
$imagesOnly) {
64                         
$imageSpecs $this->getImageSize($_FILES[$id]['tmp_name']);
65                         if (isset(
$this->files[$_FILES[$id]['tmp_name']])) {
66                             
$this->files[$_FILES[$id]['tmp_name']]['name'] = $_FILES[$id]['name'];
67                         }
68                         if (
$imagesOnly 0) {
69                             if (!
$imageSpecs) {
70                                 
$error 'Upload failed or file is not an image';
71                             } else if (!
in_array($this->files[$_FILES[$id]['tmp_name']]['type'], range(13))) {
72                                 
$error 'Image type is not supported. Please upload a jpg, gif or png file.';
73                             }
74                         }
75                     }
76                     break;
77                 case 
'1':
78                 case 
'2':
79                     
$error 'The file exceeds the maximum permitted filesize';
80                     break;
81                 case 
'3':
82                     
$error 'The file was only partially uploaded, please try again';
83                     break;
84                 case 
'4':
85                     
$error 'Please select a file before pressing the upload button';
86                     break;
87                 default:
88                     
$error 'There was a system error and your file could not be saved, please try again';
89                     break;
90             }
91         }
92         if (isset(
$error)) {
93             
$_POST['errors'][$id] = $error;
94             return 
false;
95         }
96         return 
true;
97     }
98
99     
/**
100      * Validates and moves a file uploaded from a form
101      *
102      * @param string $id
103      * @param string $destination
104      * @param boolean $imagesOnly
105      * @return boolean
106      */
107     
public function moveUploadFile($id$destination$imagesOnly false) {
108         
$return false;
109         if (
$this->validateUploadedFile($id$imagesOnly)) {
110             if (
is_dir($destination)) {
111                 
$destinationLast substr($destination, -1);
112                 if (
$destinationLast != '/' && $destinationLast != '\\') {
113                     
$destination .= '/';
114                 }
115                 
$destination .= $_FILES[$id]['name'];
116             }
117             if (
move_uploaded_file($_FILES[$id]['tmp_name'], $destination)) {
118                 
chmod($destination0777);
119                 
$return true;
120             } else {
121                 
$_POST['errors'][$id] = 'Could not move the uploaded file';
122             }
123         }
124         return 
$return;
125     }
126
127     
/**
128      * Resizes an image or generates a set of resized images, optionally adding a watermark to any of them.
129      *
130      * To resize/watermark/process a file immediately after being uploaded:
131      *
132      * $id = 'example'; //name of form's upload field
133      * $args = array('outputFolder' => 'exampleDestination'); //add args as desired
134      * $fileComponent = get::component('fileComponent');
135      * $fileComponent->validateUploadedFile($id);
136      * $fileComponent->resizeImage($args);
137      *
138      * Requires the PHP GD extension to be enabled.
139      *
140      * $specs array keys:
141      * width - default is original width, if constrain is true then this becomes the maximum width
142      * height - default is original height, if constrain is true then this becomes the maximum height
143      * constrain - Boolean, default is true - resize the image proportionally, if false the end image may be
 
stretched
144      
onlyReduce Boolean, default is true - if true the end image can only be reducednever be enlarged
 
(which
145      
*              usually degrades quality)
146      * 
outputFolder folder where to output the file, if omitted the system default will be used
147      
outputFile filename, if omitted the source filename will be used
148      
stdout - return the image bitstream to stdout (echo the image out), overrides outputFolder outputFile
 
options
149      
watermark string containing src or an array with keys:
150      *     
src requiredthe path to the watermark image file
151      
*     opacity Optional, default if omitted is 14
152      
*
153      * 
You can specify generation of multiple output files by setting specs to an array of $specseg.:
154      * 
$specs[] = array('width' => 200'height' => 100'outputFile' => 'thumbnail.jpg');
155      * 
$specs[] = array('width' => 500'height' => 300'outputFile' => 'bigfile.jpg');
156      *
157      * @
param array $specs
158      
* @param string $src Optional, if omitted the last file $this->files will be used which is populated
 
automatically
159      
*                    with the uploaded-file when you call validateUploadedFile() or uploadFile().
160      *                    
The array is also populated manually from $src when you call getImageSize($src)
161      *                    if 
$this->files is empty then the method will exit
162      * @return 
boolean
163      
*/
164     public function 
resizeImage(array $specs$src null) {
165         if (!
$src && $this->files) {
166             
end($this->files);
167             
$src key($this->files);
168         }
169         if (!isset(
$this->files[$src]) && !$this->getImageSize($src)){
170             return 
false;
171         }
172         
$types = array(=> 'gif''jpeg''png');
173         
$createImageFunction 'imagecreatefrom' $types[$this->files[$src]['type']];
174         
$sourceImage $createImageFunction($src);
175         if (!
is_array(current($specs))) {
176             
$specs = array($specs);
177         }
178         foreach (
$specs as $image) {
179             
$width $image['width'];
180             
$height $image['height'];
181             if (!isset(
$image['constrain']) || $image['constrain']) {
182                 
$proportion = ($width $this->files[$src]['width']);
183                 
$proposedHeight round($this->files[$src]['height'] * $proportion);
184                 if (
$proposedHeight <= $height) {
185                     
$height $proposedHeight;
186                 } else {
187                     
$proportion = ($height $this->files[$src]['height']);
188                     
$width round($this->files[$src]['width'] * $proportion);
189                 }
190
191                 
//need to check both height && width as due to rounding it is possible for
192                 //one dimension to be equal and the other to be 1px larger than original
193                 
if ((!isset($image['onlyReduce']) || $image['onlyReduce'])
194                     && (
$width $this->files[$src]['width'] || $height $this->files[$src]['height'])) {
195                     
$width $this->files[$src]['width'];
196                     
$height $this->files[$src]['height'];
197                 }
198             }
199
200             if (
$this->files[$src]['type']== 2) {
201                 
$finalImage imagecreatetruecolor($width$height);
202             } else {
203                 
$finalImage imagecreate($width$height);
204                 
imagecolortransparent($finalImageimagecolorallocate($finalImage000));
205             }
206             
imagecopyresampled($finalImage$sourceImage0000$width$height,
207                                
$this->files[$src]['width'], $this->files[$src]['height']);
208             if (isset(
$image['watermark']) && !is_array($image['watermark'])) {
209                 
$image['watermark'] = array('src' => $image['watermark']);
210             }
211             if (isset(
$image['watermark']) && isset($image['watermark']['src'])) {
212                 
$watermarkSrc $image['watermark']['src'];
213                 if (!isset(
$this->files[$watermarkSrc]) && !$this->getImageSize($watermarkSrc)) {
214                     if (
DEBUG_MODE) {
215                         
trigger_error('Watermark file' $watermarkSrc ' is invalid'E_USER_NOTICE);
216                     }
217                 }
218                 if (isset(
$this->files[$watermarkSrc])) {
219                     
$watermark $this->files[$watermarkSrc];
220                     
$watermarkOffsetX max(round(($width $watermark['width']) / 2), 0);
221                     
$watermarkOffsetY max(round(($height $watermark['height']) / 2), 0);
222                     if (
$width $watermark['width'] && $height $watermark['height']) {
223                         if (!isset(
$this->files[$watermarkSrc]['image'])) {
224                             
$this->files[$watermarkSrc]['image'] = imagecreatefrompng($watermarkSrc);
225                             if (
$this->files[$src]['type'] != 2) {
226                                 
$endOfWatermarkX = ($watermarkOffsetX $watermark['width']);
227                                 
$endOfWatermarkY = ($watermarkOffsetY $watermark['height']);
228                                 
$transparency imagecolorat($this->files[$watermarkSrc]['image'], 11);
229                                 for (
$x $watermarkOffsetX$x <= $endOfWatermarkX$x++) {
230                                     for (
$y $endOfWatermarkY$y <= $endOfWatermarkY$y++) {
231                                         
$rbg imagecolorsforindex($finalImageimagecolorat($finalImage$x,
 
$y));
232                                         if (
$rbg['alpha'] == 127) {
233                                             
imagesetpixel($this->files[$watermarkSrc]['image'],
234                                                           (
$x $watermarkOffsetX), ($y $watermarkOffsetY),
235                                                           
$transparency);
236                                         }
237                                     }
238                                 }
239                             }
240                         }
241                         
$opacity = (isset($image['watermark']['opacity']) ? $image['watermark']['opacity'] : 14);
242                         
imagecopymerge($finalImage$this->files[$watermarkSrc]['image'], $watermarkOffsetX,
243                                        
$watermarkOffsetY00$watermark['width'], $watermark['height'],
 
$opacity);
244                     }
245                 }
246             }
247
248             if (isset(
$image['stdout']) && $image['stdout']) {
249                 
$outputPath null//send to stdout
250             
} else if (!isset($image['outputFolder']) && !isset($image['outputFile'])) {
251                 
$outputPath $src//overwrite original upload
252             
} else {
253                 
$outputPath = (isset($image['outputFolder']) ? $image['outputFolder'] : '');
254                 
$outputPathLast substr($outputPath, -1);
255                 if (
$outputPath && $outputPathLast != '/' && $outputPathLast != '\\') {
256                     
$outputPath .= '/';
257                 }
258                 if (!isset(
$image['outputFile'])) {
259                     if (isset(
$this->files[$src]['name'])) {
260                         
$image['outputFile'] = $this->files[$src]['name'];
261                     } else {
262                         
$srcParts explode('/'str_replace('\\''/'$src));
263                         
$image['outputFile'] = end($srcParts);
264                     }
265                 }
266                 
$outputPath .= $image['outputFile'];
267             }
268
269             
$this->resizedFiles[$outputPath]['width'] = $width;
270             
$this->resizedFiles[$outputPath]['height'] = $height;
271             
$quality = ($this->files[$src]['type'] == 75 null);
272             
$generateImageFunction 'image' $types[$this->files[$src]['type']];
273             
$generateImageFunction($finalImage$outputPath$quality);
274             
imagedestroy($finalImage);
275         }
276         
imagedestroy($sourceImage);
277         return 
true;
278     }
279 }