PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.95
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.95
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Http / Request / File.php

File.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.95, at vendor/wpfluent/framework/src/WPFluent/Http/Request/File.php

583 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\Framework\Http\Request;
4
5 use ArrayAccess;
6 use SplFileInfo;
7 use JsonSerializable;
8 use RuntimeException;
9 use FluentBoards\Framework\Support\Util;
10 use FluentBoards\Framework\Foundation\App;
11 use FluentBoards\Framework\Validator\Contracts\File as Contract;
12
13 class File extends SplFileInfo implements Contract, JsonSerializable, ArrayAccess
14 {
15 /**
16 * Original file name.
17 *
18 * @var string $originalName
19 */
20 private $originalName;
21
22 /**
23 * Mime type of the file.
24 *
25 * @var string $mimeType
26 */
27 private $mimeType;
28
29 /**
30 * File size in bytes.
31 *
32 * @var int|null $size
33 */
34 private $size;
35
36 /**
37 * File upload error.
38 *
39 * @var int $error
40 */
41 private $error;
42
43 /**
44 * HTTP File instantiator.
45 *
46 * @param $path
47 * @param $originalName
48 * @param null $mimeType
49 * @param null $size
50 * @param null $error
51 */
52 public function __construct(
53 $path,
54 $originalName,
55 $mimeType = null,
56 $size = null,
57 $error = null
58 ) {
59 $this->init($path, $size, $error);
60 $this->originalName = $this->getName($originalName);
61 $this->mimeType = $this->getFileMimeType($mimeType);
62 }
63
64 /**
65 * Init the file object with size and error.
66 *
67 * @param string $path
68 * @param int|null $size
69 * @param string|null $error
70 * @return void
71 */
72 protected function init($path, $size, $error)
73 {
74 $this->size = $size ?: @filesize($path);
75 $this->error = $error ?: UPLOAD_ERR_OK;
76 parent::__construct($path);
77 }
78
79 /**
80 * @taken from \Symfony\Component\HttpFoundation\File\File
81 *
82 * Returns locale independent base name of the given path.
83 *
84 * @param string $name The new file name
85 *
86 * @return string containing
87 */
88 public function getName($name)
89 {
90 $originalName = str_replace('\\', '/', $name);
91
92 $pos = strrpos($originalName, '/');
93
94 $originalName = false === $pos ? $originalName : substr(
95 $originalName, $pos + 1
96 );
97
98 return sanitize_file_name($originalName);
99 }
100
101 public function getFileMimeType($mimeType)
102 {
103 $mimeType = $mimeType ?: $this->getMimeType();
104
105 if (!$mimeType) {
106 $path = $this->getPathname() ?: $this->getRealPath();
107
108 if (!file_exists($path)) {
109 throw new RuntimeException(
110 "File does not exist at path: $path"
111 );
112 }
113
114 if ($handle = @fopen($path, 'rb')) {
115 $data = fread($handle, 8192);
116 $finfo = new \finfo(FILEINFO_MIME_TYPE);
117 $mimeType = $finfo->buffer($data);
118 fclose($handle);
119 } else {
120 throw new RuntimeException(
121 "Failed to open file at path: $path"
122 );
123 }
124 }
125
126 return $mimeType ?: 'application/octet-stream';
127 }
128
129 /**
130 * Get the file upload error.
131 *
132 * @return int
133 */
134 public function getError()
135 {
136 return $this->error;
137 }
138
139 /**
140 * Returns whether the file was uploaded successfully.
141 *
142 * @return bool True if the file has been uploaded with HTTP and no error occurred
143 */
144 public function isValid()
145 {
146 $isOk = UPLOAD_ERR_OK === $this->getError();
147
148 // Allow fake uploads in tests
149 if (str_contains(App::make()->env(), 'test')) {
150 return $isOk;
151 }
152
153 return $isOk && is_uploaded_file($this->getPathname());
154 }
155
156 /**
157 * Returns the original file name.
158 *
159 * @return string The Name Of the file
160 */
161 public function getClientOriginalName()
162 {
163 return $this->originalName;
164 }
165
166 /**
167 * Returns the original file extension.
168 *
169 * It is extracted from the original file name that was uploaded.
170 * Then it should not be considered as a safe value.
171 *
172 * @return string The extension
173 */
174 public function getClientOriginalExtension()
175 {
176 return pathinfo($this->originalName, PATHINFO_EXTENSION);
177 }
178
179 /**
180 * Take an educated guess of the file's extension.
181 *
182 * @return mixed|null
183 */
184 public function guessExtension()
185 {
186 return $this->getMimeTypeAndExtension()['ext'];
187 }
188
189 /**
190 * Take an educated guess of the file's mime type.
191 *
192 * @return string
193 */
194 public function getMimeType()
195 {
196 return $this->getMimeTypeAndExtension()['type'];
197 }
198
199 /**
200 * Take an educated guess of the file's mime type and ext
201 * based on the WordsPress' get_allowed_mime_types.
202 *
203 * @return array
204 * @see https://developer.wordpress.org/reference/functions/get_allowed_mime_types
205 * @see https://developer.wordpress.org/reference/functions/wp_get_mime_types
206 */
207 public function getMimeTypeAndExtension()
208 {
209 $path = $this->getPathname();
210
211 if(!function_exists('wp_check_filetype_and_ext')) {
212 require_once ABSPATH .'wp-admin/includes/file.php';
213 }
214
215 return wp_check_filetype_and_ext($path, $this->originalName);
216 }
217
218 /**
219 * Get the file name.
220 *
221 * @return string
222 */
223 public function getSavedFileName()
224 {
225 if ($name = $this->originalName) {
226 return $name;
227 }
228
229 return basename($this->getPathname());
230 }
231
232 /**
233 * Get the url from path.
234 *
235 * @return string
236 */
237 public function getUrl()
238 {
239 return $this->url($this->getPathname());
240 }
241
242 /**
243 * Returns the contents of the file.
244 *
245 * @return string the contents of the file
246 *
247 * @throws RuntimeException
248 */
249 public function getContents()
250 {
251 $level = error_reporting(0);
252 $content = file_get_contents($this->getPathname());
253 error_reporting($level);
254 if (false === $content) {
255 $error = error_get_last();
256 throw new RuntimeException($error['message']);
257 }
258
259 return $content;
260 }
261
262 /**
263 * Move the file to a new location.
264 *
265 * @param string $directory Target Path
266 * @param string $name Target file name (optional)
267 * @return self
268 * @throws RuntimeException
269 */
270 public function move($directory, $name = null)
271 {
272 $err = '';
273
274 $target = $this->getTargetFile($directory, $name);
275
276 set_error_handler(function ($_, $msg) use (&$err) { $err = $msg; });
277
278 try {
279 $renamed = rename($this->getPathname(), $target);
280 } finally {
281 restore_error_handler();
282 }
283
284 if (!$renamed) {
285 throw new RuntimeException(
286 sprintf(
287 'Could not move the file "%s" to "%s" (%s).',
288 $this->getPathname(), $target, strip_tags($err)
289 )
290 );
291 }
292
293 @chmod($target, 0666 & ~umask());
294
295 return new static($target, basename($target));
296 }
297
298 /**
299 * Save the uploaded file.
300 *
301 * @param string $path
302 * @return self (File Object)
303 * @throws RuntimeException
304 */
305 public function save($path = null)
306 {
307 $path = $this->resolveTargetPath($path);
308
309 return $this->move($path);
310 }
311
312 /**
313 * Save the uploaded file with a given name.
314 *
315 * @param string $name
316 * @param string $path
317 * @return self (File Object)
318 * @throws RuntimeException
319 */
320 public function saveAs($name, $path = null)
321 {
322 $path = $this->resolveTargetPath($path);
323
324 return $this->move($path, $name);
325 }
326
327 /**
328 * Check that the given path exists.
329 *
330 * @param string $path
331 * @return string
332 * @throws RuntimeException
333 */
334 protected function resolveTargetPath($path)
335 {
336 if ($this->isAbsolutePath($path)) {
337
338 $pieces = array_values(
339 array_filter(
340 explode(DIRECTORY_SEPARATOR, $path)
341 )
342 );
343
344 // Sometimes developers can pass a relative directory like an
345 // absolute directory so in that case, If the root is not a
346 // real directory then make it relative and resolve it:
347 // i.e: 'a/relative/path/looks/like/an/absolute/path'
348
349 if (!is_dir(DIRECTORY_SEPARATOR.$pieces[0])) {
350 return $this->resolveTargetPath(trim($path, DIRECTORY_SEPARATOR));
351 }
352
353 $path = dirname($this->getTargetFile($path));
354
355 if (!is_dir($path)) {
356 throw new RuntimeException("Invalid file upload path: {$path}");
357 }
358
359 return $path;
360 }
361
362 $config = App::make('config');
363
364 $default = $config->get(
365 'app.file_upload_path', function() use ($config) {
366 $slug = $config->get('app.slug');
367 $uploadDir = wp_upload_dir()['basedir'];
368 $uploadDir .= DIRECTORY_SEPARATOR . $slug;
369 return $uploadDir;
370 }
371 );
372
373 $path = trim(
374 ($default . DIRECTORY_SEPARATOR . $path), DIRECTORY_SEPARATOR
375 );
376
377 $baseDir = trim(wp_upload_dir()['basedir'], DIRECTORY_SEPARATOR);
378
379 if (strpos($path, $baseDir) !== 0) {
380 $path = $baseDir . DIRECTORY_SEPARATOR . $path;
381 }
382
383 if (is_file($path)) {
384 throw new RuntimeException("Invalid file upload path: {$path}");
385 }
386
387 return DIRECTORY_SEPARATOR.$path;
388 }
389
390 /**
391 * Check if given path is absolute.
392 *
393 * @param string $path
394 * @return boolean
395 */
396 function isAbsolutePath($path)
397 {
398 if (!$path) return false;
399
400 // For Unix-like systems
401 if (DIRECTORY_SEPARATOR === '/') {
402 return $path[0] === '/';
403 }
404
405 // For Windows
406 if (DIRECTORY_SEPARATOR === '\\') {
407 return preg_match(
408 '/^[a-zA-Z]:\\\\/', $path
409 ) || substr($path, 0, 2) === '\\\\';
410 }
411
412 return false;
413 }
414
415 /**
416 * Get the URL from the file path.
417 *
418 * @param string $path
419 * @return string
420 */
421 public function url($path = '')
422 {
423 return Util::pathToUrl($path ?: $this->getPathname());
424 }
425
426 /**
427 * Get the target file name to move (full path).
428 *
429 * @param string $directory Target Path
430 * @param string $name Target file name (optional)
431 * @return string
432 * @throws RuntimeException
433 */
434 protected function getTargetFile($directory, $name = null)
435 {
436 if (!is_dir($directory)) {
437 if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
438 throw new RuntimeException(
439 sprintf('Unable to create the "%s" directory.', $directory)
440 );
441 }
442 } elseif (!is_writable($directory)) {
443 throw new RuntimeException(
444 sprintf('Unable to write in the "%s" directory.', $directory)
445 );
446 }
447
448 return $this->makeTargetPath($directory, $name);
449 }
450
451 /**
452 * Resolves the absolute path for saving.
453 *
454 * @param string $dir
455 * @param string $name
456 * @return string
457 */
458 protected function makeTargetPath($dir, $name)
459 {
460 return $dir . DIRECTORY_SEPARATOR . $this->resolveFileName($name);
461 }
462
463 /**
464 * Resolves the file name for saving.
465 *
466 * @param string|null $name
467 * @return string
468 */
469 protected function resolveFileName($name = null)
470 {
471 if ($name) {
472 $name = $this->getName($name);
473 if (!$this->hasExtension($name)) {
474 $name .= '.' . $this->guessExtension();
475 }
476 } else {
477 $name = $this->originalName;
478 }
479
480 return $name;
481 }
482
483 /**
484 * Retrieve the extension of the uploaded file.
485 *
486 * @return string
487 */
488 public function extension()
489 {
490 return $this->guessExtension();
491 }
492
493 /**
494 * Get the temporary file path.
495 *
496 * @return string
497 */
498 public function path()
499 {
500 return $this->getPathname();
501 }
502
503 /**
504 * Check if the given filename has an extension.
505 *
506 * @param string $filename
507 * @return boolean
508 */
509 protected function hasExtension($filename)
510 {
511 $info = pathinfo($filename);
512 return isset($info['extension']) && $info['extension'] !== '';
513 }
514
515 /**
516 * Get original HTTP file array
517 *
518 * @return array
519 */
520 public function toArray()
521 {
522 return [
523 'type' => $this->mimeType,
524 'size_in_bytes' => $this->size,
525 'size' => size_format($this->size),
526 'name' => $this->getSavedFileName(),
527 'path' => $this->getPathname(),
528 'url' => $this->getUrl(),
529 'tmp_name' => $this->getPathname(),
530 'error' => $this->getError(),
531 ];
532 }
533
534 /**
535 * JsonSerialize implementation
536 * @return array
537 */
538 #[\ReturnTypeWillChange]
539 public function jsonSerialize()
540 {
541 return $this->toArray();
542 }
543
544 /* ArrayAccess methods */
545
546 /**
547 * Check if the property exists.
548 * @param string $offset
549 * @return bool
550 */
551 #[\ReturnTypeWillChange]
552 public function offsetExists($offset)
553 {
554 return array_key_exists($offset, $this->toArray());
555 }
556
557 /**
558 * Get the property.
559 *
560 * @param string $offset
561 * @return string
562 */
563 #[\ReturnTypeWillChange]
564 public function offsetGet($offset)
565 {
566 $array = $this->toArray();
567
568 return $array[$offset] ?? null;
569 }
570
571 #[\ReturnTypeWillChange]
572 public function offsetSet($offset, $value)
573 {
574 //...
575 }
576
577 #[\ReturnTypeWillChange]
578 public function offsetUnset($offset)
579 {
580 //...
581 }
582 }
583