1 <?php
2
3 4 5 6 7 8 9 10
11
12 namespace ICanBoogie;
13
14 use ICanBoogie\I18n;
15
16
17
18 class Uploaded
19 {
20 const ERR_TYPE = 20;
21
22 public $name;
23 public $size;
24 public $extension;
25 public $mime;
26
27 28 29 30 31
32 public $location;
33 protected $is_temporary = true;
34 protected $accepted_types;
35
36 public $er;
37 public $er_message;
38
39 40 41 42 43 44 45 46
47 public function __construct($key, $accepted_types=null, $required=false, $index=0)
48 {
49 if ($accepted_types == 'image')
50 {
51 $accepted_types = [
52
53 '.gif' => 'image/gif',
54 '.jpg' => 'image/jpeg',
55 '.jpeg' => 'image/jpeg',
56 '.png' => 'image/png'
57
58 ];
59 }
60
61 $this->accepted_types = $accepted_types;
62
63
64
65
66
67 if (empty($_FILES[$key]))
68 {
69
70
71
72
73 if ($required)
74 {
75 $this->setError(UPLOAD_ERR_NO_FILE);
76 }
77
78
79
80
81
82 return;
83 }
84
85 $data = $_FILES[$key];
86
87
88
89
90
91
92
93
94 $name = urldecode($data['name']);
95
96 if (get_magic_quotes_gpc())
97 {
98 $name = stripslashes($name);
99 }
100
101 if (is_array($name))
102 {
103 $consolidated = [];
104
105 foreach ($data as $key => $nodes)
106 {
107 $consolidated[$key] = $nodes[$index];
108 }
109
110 $data = $consolidated;
111 }
112
113
114
115
116
117
118 if (($data['error'] == UPLOAD_ERR_NO_FILE) && !$required)
119 {
120 return;
121 }
122
123 if ($data['error'] || !is_uploaded_file($data['tmp_name']))
124 {
125 $this->setError($data['error']);
126
127 return;
128 }
129
130 $this->size = $data['size'];
131 $this->mime = self::getMIME($name, $this->extension);
132
133 if ($data['type'] && !in_array($data['type'], [ 'application/octet-stream', 'application/force-download' ]))
134 {
135 $this->mime = $data['type'];
136 }
137
138 $this->name = $this->extension ? substr($name, 0, -strlen($this->extension)) : $name;
139
140 switch ($this->mime)
141 {
142 case 'image/gif':
143 {
144 $this->extension = '.gif';
145 }
146 break;
147
148 case 'image/png':
149 case 'image/x-png':
150 {
151 $this->mime = 'image/png';
152 $this->extension = '.png';
153 }
154 break;
155
156 case 'image/jpeg':
157 case 'image/pjpeg':
158 {
159 $this->mime = 'image/jpeg';
160 $this->extension = '.jpeg';
161 }
162 break;
163
164 case 'application/pdf':
165 {
166 $this->extension = '.pdf';
167 }
168 break;
169
170 case 'application/zip':
171 case 'application/x-zip':
172 case 'application/x-zip-compressed':
173 {
174 $this->mime = 'application/zip';
175 $this->extension = '.zip';
176 }
177 break;
178 }
179
180
181
182
183
184 if ($accepted_types)
185 {
186 $type = $this->mime;
187
188 if (is_array($accepted_types))
189 {
190 $ok = false;
191
192 foreach ($accepted_types as $accepted)
193 {
194 if ($type == $accepted)
195 {
196 $ok = true;
197 }
198 }
199
200 if (!$ok)
201 {
202 $this->setError(self::ERR_TYPE);
203
204 return;
205 }
206 }
207 else if ($type != $accepted_types)
208 {
209 $this->setError(self::ERR_TYPE);
210
211 return;
212 }
213 }
214
215
216
217
218
219 $this->location = $data['tmp_name'];
220 }
221
222 static public function isMultiple($slot)
223 {
224 if (empty($_FILES[$slot]))
225 {
226 return false;
227 }
228
229 if (is_array($_FILES[$slot]['name']))
230 {
231 return count($_FILES[$slot]['name']);
232 }
233
234 return false;
235 }
236
237 private function setError($error)
238 {
239 $this->er = $error;
240
241 switch ($error)
242 {
243 case UPLOAD_ERR_INI_SIZE:
244 {
245 $this->er_message = new I18n\FormattedString('Maximum file size is :size Mb', [ ':size' => (int) ini_get('upload_max_filesize') ]);
246 }
247 break;
248
249 case UPLOAD_ERR_FORM_SIZE:
250 {
251 $this->er_message = new I18n\FormattedString('Maximum file size is :size Mb', [ ':size' => round(MAX_FILE_SIZE / 1024 / 1024, 2) ]);
252 }
253 break;
254
255 case UPLOAD_ERR_NO_FILE:
256 {
257 $this->er_message = new I18n\FormattedString('No file was uploaded');
258 }
259 break;
260
261 case self::ERR_TYPE:
262 {
263 $this->er_message = new I18n\FormattedString('error.message.upload.mime', [ '%accepted' => implode(', ', array_keys($this->accepted_types)) ]);
264 }
265 break;
266
267 default:
268 {
269 $this->er_message = new I18n\FormattedString('Error code: :code', [ ':code' => $error ]);
270 }
271 break;
272 }
273 }
274
275 276 277 278 279 280 281 282 283 284
285 public function move($destination, $overrite=false)
286 {
287 if (!$this->location)
288 {
289 return;
290 }
291
292 if (is_file($destination))
293 {
294 if ($overrite)
295 {
296 unlink($destination);
297 }
298 else
299 {
300 throw new Exception
301 (
302 'Unable to move file %source to %destination, destination file already exists', [
303
304 '%source' => $this->location,
305 '%destination' => $destination
306
307 ]
308 );
309 }
310 }
311
312 $moved = false;
313
314 if ($this->is_temporary)
315 {
316 $moved = move_uploaded_file($this->location, $destination);
317
318 if ($moved)
319 {
320 $this->is_temporary = false;
321 }
322 }
323 else
324 {
325 $moved = rename($this->location, $destination);
326 }
327
328 if (!$moved)
329 {
330 throw new Exception
331 (
332 'Unable to move file %source to %destination', [
333
334 '%source' => $this->location,
335 '%destination' => $destination
336
337 ]
338 );
339 }
340
341 $this->location = $destination;
342
343 return true;
344 }
345
346 static public $mimes_by_extension = [
347
348 '.doc' => 'application/msword',
349 '.docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
350 '.flv' => 'video/x-flv',
351 '.gif' => 'image/gif',
352 '.jpg' => 'image/jpeg',
353 '.jpeg' => 'image/jpeg',
354 '.js' => 'application/javascript',
355 '.mp3' => 'audio/mpeg',
356 '.odt' => 'application/vnd.oasis.opendocument.text',
357 '.pdf' => 'application/pdf',
358 '.png' => 'image/png',
359 '.psd' => 'application/psd',
360 '.rar' => 'application/rar',
361 '.zip' => 'application/zip',
362 '.xls' => 'application/vnd.ms-excel',
363 '.xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
364
365 ];
366
367 static public function getMIME($filename, &$extension=null)
368 {
369 $extension = '.' . strtolower(pathinfo($filename, PATHINFO_EXTENSION));
370
371 if (array_key_exists($extension, self::$mimes_by_extension))
372 {
373 return self::$mimes_by_extension[$extension];
374 }
375
376 if (file_exists($filename))
377 {
378 if (extension_loaded('fileinfo'))
379 {
380 $fi = finfo_open(FILEINFO_MIME_TYPE);
381
382 if ($fi !== false)
383 {
384 if ($mime = finfo_file($fi, $filename))
385 {
386 finfo_close($fi);
387
388 return $mime;
389 }
390 }
391 }
392
393 if (function_exists('mime_content_type'))
394 {
395 if ($mime = mime_content_type($filename))
396 {
397 return $mime;
398 }
399 }
400 }
401
402 return 'application/octet-stream';
403 }
404 }