PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.0
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Http / Request / File.php

File.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.10.0, at vendor/wpfluent/framework/src/WPFluent/Http/Request/File.php

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