This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Agavi\Request; |
||
3 | |||
4 | // +---------------------------------------------------------------------------+ |
||
5 | // | This file is part of the Agavi package. | |
||
6 | // | Copyright (c) 2005-2011 the Agavi Project. | |
||
7 | // | | |
||
8 | // | For the full copyright and license information, please view the LICENSE | |
||
9 | // | file that was distributed with this source code. You can also view the | |
||
10 | // | LICENSE file online at http://www.agavi.org/LICENSE.txt | |
||
11 | // | vi: set noexpandtab: | |
||
12 | // | Local Variables: | |
||
13 | // | indent-tabs-mode: t | |
||
14 | // | End: | |
||
15 | // +---------------------------------------------------------------------------+ |
||
16 | |||
17 | /** |
||
18 | * AgaviUploadedFile is a container with information for files that were |
||
19 | * uploaded or submitted with the request. |
||
20 | * |
||
21 | * @package agavi |
||
22 | * @subpackage request |
||
23 | * |
||
24 | * @author David Zülke <[email protected]> |
||
25 | * @copyright Authors |
||
26 | * @copyright The Agavi Project |
||
27 | * |
||
28 | * @since 0.11.0 |
||
29 | * |
||
30 | * @version $Id$ |
||
31 | */ |
||
32 | class UploadedFile implements \ArrayAccess |
||
33 | { |
||
34 | /** |
||
35 | * @var string The name of the file. |
||
36 | */ |
||
37 | protected $name; |
||
38 | |||
39 | /** |
||
40 | * @var string The type of the file. |
||
41 | */ |
||
42 | protected $type; |
||
43 | |||
44 | /** |
||
45 | * @var int The size of the file, in bytes. |
||
46 | */ |
||
47 | protected $size; |
||
48 | |||
49 | /** |
||
50 | * @var string A local path name to the file, if persisted to disk. |
||
51 | */ |
||
52 | protected $tmpName; |
||
53 | |||
54 | /** |
||
55 | * @var int An UPLOAD_ERR_* error code for the file upload. |
||
56 | */ |
||
57 | protected $error; |
||
58 | |||
59 | /** |
||
60 | * @var bool Whether or not this was a file uploaded via an HTML form. |
||
61 | */ |
||
62 | protected $isUploadedFile; |
||
63 | |||
64 | /** |
||
65 | * @var bool Whether or not this file has been moved already. |
||
66 | */ |
||
67 | protected $isMoved; |
||
68 | |||
69 | /** |
||
70 | * @var string A string of the raw binary contents of the file. |
||
71 | */ |
||
72 | protected $contents; |
||
73 | |||
74 | /** |
||
75 | * @var resource A resource pointer to the stream with the contents. |
||
76 | */ |
||
77 | protected $stream; |
||
78 | |||
79 | /** |
||
80 | * @var array An array to map get* method name fragments to indices. |
||
81 | */ |
||
82 | protected static $indexMap = array( |
||
83 | 'name' => 'name', |
||
84 | 'type' => 'type', |
||
85 | 'size' => 'size', |
||
86 | 'tmp_name' => 'tmpName', |
||
87 | 'error' => 'error', |
||
88 | 'is_uploaded_file' => 'isUploadedFile', |
||
89 | 'is_moved' => 'isMoved', |
||
90 | 'contents' => 'contents', |
||
91 | 'stream' => 'stream', |
||
92 | ); |
||
93 | |||
94 | /** |
||
95 | * Constructor. |
||
96 | * |
||
97 | * @param array The fields for this file. |
||
98 | * |
||
99 | * @see ArrayObject::__construct() |
||
100 | * |
||
101 | * @author David Zülke <[email protected]> |
||
102 | * @since 0.11.0 |
||
103 | */ |
||
104 | public function __construct(array $array) |
||
105 | { |
||
106 | $defaults = array( |
||
107 | 'name' => null, |
||
108 | 'type' => null, |
||
109 | 'size' => -1, |
||
110 | 'tmp_name' => null, |
||
111 | 'error' => UPLOAD_ERR_OK, |
||
112 | 'is_uploaded_file' => false, |
||
113 | 'contents' => null, |
||
114 | 'stream' => null, |
||
115 | ); |
||
116 | $array = array_merge($defaults, $array, array('is_moved' => false)); // make sure it's marked not moved by default |
||
117 | |||
118 | // we need exactly one of tmp_name, contents or stream |
||
119 | if (isset($array['tmp_name'], $array['contents'], $array['stream']) || |
||
120 | isset($array['tmp_name'], $array['contents']) || |
||
121 | isset($array['tmp_name'], $array['stream']) || |
||
122 | isset($array['contents'], $array['stream']) || |
||
123 | (!isset($array['tmp_name']) && !isset($array['contents']) && !isset($array['stream'])) |
||
124 | ) { |
||
125 | throw new InvalidArgumentException('Value for exactly one of keys "tmp_name", "contents" or "stream" must be supplied.'); |
||
126 | } |
||
127 | |||
128 | // fill local props |
||
129 | foreach (self::$indexMap as $index => $property) { |
||
130 | if (isset($array[$index])) { |
||
131 | $this->$property = $array[$index]; |
||
132 | } |
||
133 | } |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * Property access overload. |
||
138 | * |
||
139 | * @param string The key to fetch. |
||
140 | * |
||
141 | * @return mixed The value of the key or null. |
||
142 | * |
||
143 | * @author David Zülke <[email protected]> |
||
144 | * @since 1.1.0 |
||
145 | */ |
||
146 | public function __get($key) |
||
147 | { |
||
148 | if ($this->__isset($key)) { |
||
149 | $method = 'get' . $key; |
||
150 | return $this->$method(); |
||
151 | } |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Property isset overload. |
||
156 | * |
||
157 | * @param string The key to check. |
||
158 | * |
||
159 | * @return bool Whether the given key exists. |
||
160 | * |
||
161 | * @author David Zülke <[email protected]> |
||
162 | * @since 1.1.0 |
||
163 | */ |
||
164 | public function __isset($key) |
||
165 | { |
||
166 | trigger_error('Property access in AgaviUploadedFile is deprecated and will be removed in Agavi 1.2. Please use getter methods or array access instead.', E_USER_DEPRECATED); |
||
167 | return in_array($key, array('name', 'type', 'size', 'tmpName', 'error', 'isUploadedFile')) && isset($this->$key); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Array access: existence check. |
||
172 | * |
||
173 | * @param string The key to check. |
||
174 | * |
||
175 | * @return bool Whether or not the key exists. |
||
176 | * |
||
177 | * @author David Zülke <[email protected]> |
||
178 | * @since 1.1.0 |
||
179 | */ |
||
180 | public function offsetExists($key) |
||
181 | { |
||
182 | if (in_array($key, array('name', 'type', 'size', 'tmp_name', 'error', 'is_uploaded_file'))) { |
||
183 | $property = self::$indexMap[$key]; |
||
184 | return isset($this->$property); |
||
185 | } |
||
186 | return false; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Array access: fetch value. |
||
191 | * |
||
192 | * @param string The key of the value to fetch. |
||
193 | * |
||
194 | * @return mixed The key for the given value. |
||
195 | * |
||
196 | * @author David Zülke <[email protected]> |
||
197 | * @since 1.1.0 |
||
198 | */ |
||
199 | public function offsetGet($key) |
||
200 | { |
||
201 | if ($this->offsetExists($key)) { |
||
202 | $method = 'get' . self::$indexMap[$key]; |
||
203 | return $this->$method(); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Array access: set value. |
||
209 | * |
||
210 | * @param string The key to set. |
||
211 | * @param string The value to set. |
||
212 | * |
||
213 | * @throws BadMethodCallException AgaviUploadedFile objects are immutable. |
||
214 | * |
||
215 | * @author David Zülke <[email protected]> |
||
216 | * @since 1.1.0 |
||
217 | */ |
||
218 | public function offsetSet($key, $value) |
||
219 | { |
||
220 | throw new BadMethodCallException('AgaviUploadedFile objects are immutable.'); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Array access: unset value. |
||
225 | * |
||
226 | * @param string The key to unset. |
||
227 | * |
||
228 | * @throws BadMethodCallException AgaviUploadedFile objects are immutable. |
||
229 | * |
||
230 | * @author David Zülke <[email protected]> |
||
231 | * @since 1.1.0 |
||
232 | */ |
||
233 | public function offsetUnset($key) |
||
234 | { |
||
235 | throw new BadMethodCallException('AgaviUploadedFile objects are immutable.'); |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Get the name of the file as submitted by the client. |
||
240 | * |
||
241 | * @return string The file name as submitted by the client. |
||
242 | * |
||
243 | * @author David Zülke <[email protected]> |
||
244 | * @since 1.1.0 |
||
245 | */ |
||
246 | public function getName() |
||
247 | { |
||
248 | return $this->name; |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Get the type of the file as submitted by the client. |
||
253 | * |
||
254 | * @return string The file type as submitted by the client. |
||
255 | * |
||
256 | * @author David Zülke <[email protected]> |
||
257 | * @since 1.1.0 |
||
258 | */ |
||
259 | public function getType() |
||
260 | { |
||
261 | return $this->type; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Get the size of the file. |
||
266 | * |
||
267 | * @return int The length of the file in bytes. |
||
268 | * |
||
269 | * @author David Zülke <[email protected]> |
||
270 | * @since 1.1.0 |
||
271 | */ |
||
272 | public function getSize() |
||
273 | { |
||
274 | return $this->size; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Get the temporary filename of this file. |
||
279 | * |
||
280 | * @return string The temporary filename for this file. |
||
281 | * |
||
282 | * @author David Zülke <[email protected]> |
||
283 | * @since 1.1.0 |
||
284 | */ |
||
285 | public function getTmpName() |
||
286 | { |
||
287 | if (!$this->hasTmpName() && !$this->hasOpenStream()) { |
||
288 | // have faith in the ctor :) |
||
289 | $this->stream = tmpfile(); |
||
290 | if (!$this->stream) { |
||
291 | throw new FileException('Cannot create temporary file via tmpfile()'); |
||
292 | } |
||
293 | fwrite($this->stream, $this->contents); |
||
294 | rewind($this->stream); |
||
295 | } |
||
296 | |||
297 | if ($this->hasOpenStream()) { |
||
298 | $meta = stream_get_meta_data($this->getStream()); |
||
299 | return $meta['uri']; |
||
300 | } |
||
301 | |||
302 | return $this->tmpName; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Check if this uploaded file has a temporary filename. |
||
307 | * If a file has no temp name, then it means that this object was constructed |
||
308 | * internally using the file contents rather than by PHP's upload handler. |
||
309 | * On calling getTmpName(), the contents will be flushed to disk so access |
||
310 | * using file methods is possible. |
||
311 | * |
||
312 | * @return bool Whether or not the file contents are on disk yet. |
||
313 | * |
||
314 | * @author David Zülke <[email protected]> |
||
315 | * @since 1.1.0 |
||
316 | */ |
||
317 | public function hasTmpName() |
||
318 | { |
||
319 | return isset($this->tmpName); |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * Get the error code for this uploaded file. |
||
324 | * |
||
325 | * @return int One of PHP's UPLOAD_ERR_* constants. |
||
326 | * |
||
327 | * @author David Zülke <[email protected]> |
||
328 | * @since 1.1.0 |
||
329 | */ |
||
330 | public function getError() |
||
331 | { |
||
332 | return $this->error; |
||
333 | } |
||
334 | |||
335 | /** |
||
336 | * Check if this file is a multipart/form-data upload handled by PHP itself, |
||
337 | * or another type of file submission handled by Agavi. |
||
338 | * |
||
339 | * @return bool Whether or not this file was uploaded through a web form. |
||
340 | * |
||
341 | * @author David Zülke <[email protected]> |
||
342 | * @since 1.1.0 |
||
343 | */ |
||
344 | public function getIsUploadedFile() |
||
345 | { |
||
346 | return $this->isUploadedFile; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Check if this file has been moved from it's temporary location. |
||
351 | * |
||
352 | * @return bool Whether or not the temporary file has been moved yet. |
||
353 | * |
||
354 | * @author David Zülke <[email protected]> |
||
355 | * @since 1.1.0 |
||
356 | */ |
||
357 | public function getIsMoved() |
||
358 | { |
||
359 | return $this->isMoved; |
||
360 | } |
||
361 | |||
362 | /** |
||
363 | * Check whether or not this file has an error. |
||
364 | * |
||
365 | * This only returns PHP's own information, not validator's. |
||
366 | * |
||
367 | * @return bool True in case of UPLOAD_ERR_OK, false otherwise. |
||
368 | * |
||
369 | * @author David Zülke <[email protected]> |
||
370 | * @since 0.11.0 |
||
371 | */ |
||
372 | public function hasError() |
||
373 | { |
||
374 | return $this->getError() !== UPLOAD_ERR_OK; |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Whether or not this file is movable. |
||
379 | * |
||
380 | * @return bool True if this file has not been moved yet, otherwise false. |
||
381 | * |
||
382 | * @author David Zülke <[email protected]> |
||
383 | * @since 0.11.0 |
||
384 | */ |
||
385 | public function isMovable() |
||
386 | { |
||
387 | return $this->hasTmpName() && !$this->getIsMoved(); |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Retrieve the contents of the uploaded file. |
||
392 | * |
||
393 | * @return string The file contents. |
||
394 | * |
||
395 | * @throws Exception If the file has errors or has been moved. |
||
396 | * |
||
397 | * @author David Zülke <[email protected]> |
||
398 | * @author Peter Limbach <[email protected]> |
||
399 | * @since 0.11.2 |
||
400 | */ |
||
401 | public function getContents() |
||
402 | { |
||
403 | // for a file where we wrote the contents to a temp file in getTmpName(), we can always return contents |
||
404 | View Code Duplication | if ($this->hasError() || (!$this->isMovable() && !$this->hasBufferedContents() && !$this->hasOpenStream())) { |
|
0 ignored issues
–
show
|
|||
405 | throw new Exception('Cannot get contents of erroneous or moved file.'); |
||
406 | } |
||
407 | |||
408 | // we intentionally don't store the result of file_get_contents() here to keep memory usage low |
||
409 | return |
||
410 | $this->hasBufferedContents() |
||
411 | ? $this->contents |
||
412 | : ($this->hasOpenStream() |
||
413 | ? stream_get_contents($this->getStream(), -1, 0) |
||
414 | : file_get_contents($this->getTmpName())) |
||
415 | ; |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * Check if this uploaded file object already has the file contents buffered. |
||
420 | * If this method returns false, the getContents() method will still attempt |
||
421 | * to read the file from the temporary location on disk. |
||
422 | * |
||
423 | * @return bool Whether or not the file contents are on disk yet. |
||
424 | * |
||
425 | * @author David Zülke <[email protected]> |
||
426 | * @since 1.1.0 |
||
427 | */ |
||
428 | public function hasBufferedContents() |
||
429 | { |
||
430 | return isset($this->contents); |
||
431 | } |
||
432 | |||
433 | /** |
||
434 | * Check if there is an open stream resource for this uploaded file. |
||
435 | * Will be true if this object has been constructed with a resource pointer, |
||
436 | * or if it has been constructed with raw contents and after getStream() has |
||
437 | * been called once. |
||
438 | * |
||
439 | * @return bool Whether there is an open stream with the file contents. |
||
440 | * |
||
441 | * @author David Zülke <[email protected]> |
||
442 | * @since 1.1.0 |
||
443 | */ |
||
444 | public function hasOpenStream() |
||
445 | { |
||
446 | return isset($this->stream); |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * Retrieve a stream handle of the uploaded file. |
||
451 | * |
||
452 | * @param string The fopen mode, defaults to 'rb'. Only used for files. |
||
453 | * |
||
454 | * @return resource The stream. |
||
455 | * |
||
456 | * @throws Exception If the file has errors or has been moved. |
||
457 | * |
||
458 | * @author David Zülke <[email protected]> |
||
459 | * @since 0.11.2 |
||
460 | */ |
||
461 | public function getStream($mode = 'rb') |
||
462 | { |
||
463 | View Code Duplication | if ($this->hasError() || (!$this->isMovable() && !$this->hasBufferedContents() && !$this->hasOpenStream())) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
464 | throw new Exception('Cannot get contents of erroneous or moved file.'); |
||
465 | } |
||
466 | |||
467 | if (!$this->hasOpenStream() && $this->hasBufferedContents()) { |
||
468 | // copy the contents into a temp stream |
||
469 | $this->stream = fopen('php://temp', 'rb+'); |
||
470 | fputs($this->stream, $this->getContents()); |
||
471 | rewind($this->stream); |
||
472 | } |
||
473 | |||
474 | return $this->hasOpenStream() ? $this->stream : fopen($this->getTmpName(), $mode); |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * Return the MIME type of this file using the fileinfo extension. |
||
479 | * |
||
480 | * @param bool Whether to return the charset of the file as well. |
||
481 | * |
||
482 | * @return string The MIME type of the file. |
||
483 | * |
||
484 | * @author David Zülke <[email protected]> |
||
485 | * @since 1.1.0 |
||
486 | */ |
||
487 | public function getMimeType($charset = false) |
||
488 | { |
||
489 | $finfo = new finfo($charset ? FILEINFO_MIME : FILEINFO_MIME_TYPE); |
||
490 | // don't use finfo_file() to avoid unnecessary hits to disk in case it's not an uploaded file |
||
491 | return $finfo->buffer($this->getContents()); |
||
492 | } |
||
493 | |||
494 | /** |
||
495 | * Move the uploaded file. |
||
496 | * |
||
497 | * @param string The destination filename. |
||
498 | * @param int The mode of the destination file, defaults to 0664. |
||
499 | * @param bool Whether or not subdirs should be created if necessary. |
||
500 | * @param int The mode to use when creating subdirs, defaults to 0775. |
||
501 | * |
||
502 | * @return bool True, if the operation was successful, false otherwise. |
||
503 | * |
||
504 | * @throws FileException If chmod or mkdir calls failed. |
||
505 | * |
||
506 | * @author David Zülke <[email protected]> |
||
507 | * @since 0.11.0 |
||
508 | */ |
||
509 | public function move($dest, $fileMode = 0664, $create = true, $dirMode = 0775) |
||
510 | { |
||
511 | if ($this->hasError()) { |
||
512 | return false; |
||
513 | } elseif (!$this->isMovable()) { |
||
514 | return false; |
||
515 | } |
||
516 | |||
517 | // get our directory path from the destination filename |
||
518 | $directory = dirname($dest); |
||
519 | if (!is_readable($directory)) { |
||
520 | if ($create && !Toolkit::mkdir($directory, $dirMode, true)) { |
||
521 | // failed to create the directory |
||
522 | $error = 'Failed to create file upload directory "%s"'; |
||
523 | $error = sprintf($error, $directory); |
||
524 | throw new FileException($error); |
||
525 | } |
||
526 | } elseif (!is_dir($directory)) { |
||
527 | // the directory path exists but it's not a directory |
||
528 | $error = 'File upload path "%s" exists, but is not a directory'; |
||
529 | $error = sprintf($error, $directory); |
||
530 | throw new FileException($error); |
||
531 | } elseif (!is_writable($directory)) { |
||
532 | // the directory isn't writable |
||
533 | $error = 'File upload path "%s" is not writable'; |
||
534 | $error = sprintf($error, $directory); |
||
535 | throw new FileException($error); |
||
536 | } |
||
537 | |||
538 | if ($this->getIsUploadedFile()) { |
||
539 | $moved = @move_uploaded_file($this->getTmpName(), $dest); |
||
540 | } else { |
||
541 | if (is_writable($dest)) { |
||
542 | unlink($dest); |
||
543 | } |
||
544 | $moved = @rename($this->getTmpName(), $dest); |
||
545 | } |
||
546 | |||
547 | if ($moved) { |
||
548 | $this->isMoved = true; |
||
549 | // chmod our file |
||
550 | if (!@chmod($dest, $fileMode)) { |
||
551 | throw new FileException('Failed to chmod uploaded file after moving'); |
||
552 | } |
||
553 | } else { |
||
554 | // moving the file failed |
||
555 | throw new FileException('Failed to move uploaded file'); |
||
556 | } |
||
557 | |||
558 | return true; |
||
559 | } |
||
560 | } |
||
561 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.