1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace Kotchasan\Http;
12:
13: use Kotchasan\Image;
14: use Kotchasan\Language;
15: use Psr\Http\Message\UploadedFileInterface;
16:
17: 18: 19: 20: 21: 22: 23:
24: class UploadedFile implements UploadedFileInterface
25: {
26: 27: 28: 29: 30:
31: private $error;
32: 33: 34: 35: 36:
37: private $ext;
38: 39: 40: 41: 42:
43: private $isMoved = false;
44: 45: 46: 47: 48:
49: private $mime;
50: 51: 52: 53: 54:
55: private $name;
56: 57: 58: 59: 60:
61: private $sapi = false;
62: 63: 64: 65: 66:
67: private $size;
68: 69: 70: 71: 72:
73: private $stream;
74: 75: 76: 77: 78:
79: private $tmp_name;
80:
81: 82: 83: 84: 85: 86: 87: 88: 89: 90:
91: public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $sapi = true)
92: {
93: $this->tmp_name = $path;
94: $this->name = $originalName;
95: $this->mime = $mimeType;
96: $this->size = $size;
97: $this->error = $error;
98: $this->sapi = $sapi;
99: }
100:
101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111:
112: public function copyTo($targetPath)
113: {
114: if ($this->isMoved) {
115: throw new \RuntimeException(sprintf(Language::get('Uploaded file %1s has already been moved'), $this->name));
116: }
117: if (!is_writable(dirname($targetPath))) {
118: throw new \InvalidArgumentException(Language::get('Target directory is not writable'));
119: }
120: if (!copy($this->tmp_name, $targetPath)) {
121: throw new \RuntimeException(sprintf(Language::get('Error copying file %1s to %2s'), $this->name, $targetPath));
122: }
123:
124: return true;
125: }
126:
127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144:
145: public function cropImage($exts, $targetPath, $width, $height, $watermark = '')
146: {
147: $this->check($exts, dirname($targetPath));
148: $ret = Image::crop($this->tmp_name, $targetPath, $width, $height, $watermark);
149: if ($ret === false) {
150: throw new \RuntimeException(Language::get('Unable to create image'));
151: }
152:
153: return true;
154: }
155:
156: 157: 158: 159: 160: 161: 162: 163: 164:
165: public function getCleanFilename($replace = '_')
166: {
167: return preg_replace('/[^a-zA-Z0-9_\-\.\(\)]{1,}/', $replace, $this->name);
168: }
169:
170: 171: 172: 173: 174:
175: public function getClientFileExt()
176: {
177: if ($this->ext == null) {
178: $exts = explode('.', $this->name);
179: $this->ext = strtolower(end($exts));
180: }
181:
182: return $this->ext;
183: }
184:
185: 186: 187: 188: 189:
190: public function getClientFilename()
191: {
192: return $this->name;
193: }
194:
195: 196: 197: 198: 199:
200: public function getClientMediaType()
201: {
202: return $this->mime;
203: }
204:
205: 206: 207: 208: 209: 210:
211: public function getError()
212: {
213: return $this->error;
214: }
215:
216: 217: 218: 219: 220: 221: 222:
223: public function getErrorMessage()
224: {
225: switch ($this->error) {
226: case UPLOAD_ERR_INI_SIZE:
227: return sprintf('The file "%s" exceeds your upload_max_filesize ini directive (limit is %s).', $this->getClientFilename(), self::getUploadSize());
228: break;
229: case UPLOAD_ERR_FORM_SIZE:
230: return sprintf('The file "%s" exceeds the upload limit defined in your form.', $this->getClientFilename());
231: break;
232: case UPLOAD_ERR_PARTIAL:
233: return sprintf('The file "%s" was only partially uploaded.', $this->getClientFilename());
234: break;
235: case UPLOAD_ERR_CANT_WRITE:
236: return sprintf('The file "%s" could not be written on disk.', $this->getClientFilename());
237: break;
238: case UPLOAD_ERR_NO_TMP_DIR:
239: return 'File could not be uploaded: missing temporary directory.';
240: break;
241: case UPLOAD_ERR_EXTENSION:
242: return 'File upload was stopped by a PHP extension.';
243: break;
244: case UPLOAD_ERR_OK:
245: case UPLOAD_ERR_NO_FILE:
246: return null;
247: break;
248: default:
249: return sprintf('The file "%s" was not uploaded due to an unknown error.', $this->getClientFilename());
250: break;
251: }
252: }
253:
254: 255: 256: 257: 258:
259: public function getSize()
260: {
261: return $this->size;
262: }
263:
264: 265: 266: 267: 268: 269: 270:
271: public function getStream()
272: {
273: if (!is_file($this->tmp_name)) {
274: throw new \RuntimeException(sprintf(Language::get('Uploaded file %1s has already been moved'), $this->name));
275: }
276: if ($this->stream === null) {
277: $this->stream = new Stream($this->tmp_name);
278: }
279:
280: return $this->stream;
281: }
282:
283: 284: 285: 286: 287:
288: public function getTempFileName()
289: {
290: return $this->tmp_name;
291: }
292:
293: 294: 295: 296: 297: 298: 299:
300: public static function getUploadSize($return_byte = false)
301: {
302: $val = trim(ini_get('upload_max_filesize'));
303: if (is_numeric($val)) {
304: return $val;
305: } elseif ($return_byte && preg_match('/^([0-9]+)([gmk])$/', strtolower($val), $match)) {
306: $units = array('k' => 1024, 'm' => 1048576, 'g' => 1073741824);
307: $val = (int) $match[1] * $units[$match[2]];
308: }
309:
310: return $val;
311: }
312:
313: 314: 315: 316: 317: 318:
319: public function hasError()
320: {
321: if ($this->error === null || $this->error === UPLOAD_ERR_OK || $this->error === UPLOAD_ERR_NO_FILE) {
322: return false;
323: }
324:
325: return true;
326: }
327:
328: 329: 330: 331: 332: 333:
334: public function hasUploadFile()
335: {
336: return $this->error == UPLOAD_ERR_OK;
337: }
338:
339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349:
350: public function moveTo($targetPath)
351: {
352: if ($this->isMoved) {
353: throw new \RuntimeException(sprintf(Language::get('Uploaded file %1s has already been moved'), $this->name));
354: }
355: if (!is_writable(dirname($targetPath))) {
356: throw new \InvalidArgumentException(Language::get('Target directory is not writable'));
357: }
358: if (strpos($targetPath, '://') > 0) {
359: if (!copy($this->tmp_name, $targetPath)) {
360: throw new \RuntimeException(sprintf(Language::get('Error moving uploaded file %1s to %2s'), $this->name, $targetPath));
361: }
362: if (!unlink($this->tmp_name)) {
363: throw new \RuntimeException(sprintf(Language::get('Error removing uploaded file %1s'), $this->name));
364: }
365: } elseif ($this->sapi) {
366: if (!move_uploaded_file($this->tmp_name, $targetPath)) {
367: throw new \RuntimeException(sprintf(Language::get('Error moving uploaded file %1s to %2s'), $this->name, $targetPath));
368: }
369: } elseif (copy($this->tmp_name, $targetPath)) {
370: unlink($this->tmp_name);
371: } else {
372: throw new \RuntimeException(sprintf(Language::get('Error moving uploaded file %1s to %2s'), $this->name, $targetPath));
373: }
374: $this->isMoved = true;
375:
376: return true;
377: }
378:
379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394:
395: public function resizeImage($exts, $target, $name, $width, $watermark = '')
396: {
397: $this->check($exts, $target);
398: $ret = Image::resize($this->tmp_name, $target, $name, $width, $watermark);
399: if ($ret === false) {
400: throw new \RuntimeException(Language::get('Unable to create image'));
401: } else {
402: return $ret;
403: }
404: }
405:
406: 407: 408: 409: 410: 411: 412: 413:
414: public function validFileExt($exts)
415: {
416: return in_array($this->getClientFileExt(), $exts);
417: }
418:
419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430:
431: private function check($exts, $targetDir)
432: {
433: if (!$this->validFileExt($exts)) {
434: throw new \RuntimeException(Language::get('The type of file is invalid'));
435: }
436: if (!is_writable($targetDir)) {
437: throw new \InvalidArgumentException(Language::get('Target directory is not writable'));
438: }
439:
440: return true;
441: }
442: }
443: