Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
150 / 150
100.00% covered (success)
100.00%
39 / 39
CRAP
100.00% covered (success)
100.00%
1 / 1
File
100.00% covered (success)
100.00%
150 / 150
100.00% covered (success)
100.00%
39 / 39
61
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 formatFileSize
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getMimeTypes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFileMimeType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isImage
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
 isWebImage
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 isVideo
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 isAudio
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 isText
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 isCompressed
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 isWord
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 isPdf
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setBasename
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getBasename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasBasename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setFilename
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getFilename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasFilename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setExtension
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getExtension
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasExtension
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPath
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getPath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasPath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSize
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getSize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasSize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatSize
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
13
 setMimeType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getMimeType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasMimeType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDefaultMimeType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultMimeType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDefaultMimeType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllMimeTypes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 exists
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getContents
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 toArray
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Pop PHP Framework (https://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@noladev.com>
7 * @copyright  Copyright (c) 2009-2026 NOLA Interactive, LLC.
8 * @license    https://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Utils;
15
16/**
17 * Pop utils file helper class
18 *
19 * @category   Pop
20 * @package    Pop\Utils
21 * @author     Nick Sagona, III <dev@noladev.com>
22 * @copyright  Copyright (c) 2009-2026 NOLA Interactive, LLC.
23 * @license    https://www.popphp.org/license     New BSD License
24 * @version    2.3.0
25 */
26class File
27{
28
29    /**
30     * Basename (filename.ext)
31     * @var ?string
32     */
33    protected ?string $basename = null;
34
35    /**
36     * Filename (filename)
37     * @var ?string
38     */
39    protected ?string $filename = null;
40
41    /**
42     * Extension (.ext)
43     * @var ?string
44     */
45    protected ?string $extension = null;
46
47    /**
48     * Path (/some/path)
49     * @var ?string
50     */
51    protected ?string $path = null;
52
53    /**
54     * Size
55     * @var int
56     */
57    protected int $size = 0;
58
59    /**
60     * Mime type
61     * @var ?string
62     */
63    protected ?string $mimeType = null;
64
65    /**
66     * Mime types
67     * @var array
68     */
69    protected static array $mimeTypes = [
70        'aiff'  => 'audio/aiff',
71        'avi'   => 'video/x-msvideo',
72        'bin'   => 'application/octet-stream',
73        'bmp'   => 'image/bmp',
74        'bz'    => 'application/x-bzip',
75        'bz2'   => 'application/x-bzip2',
76        'css'   => 'text/css',
77        'csv'   => 'text/csv',
78        'doc'   => 'application/msword',
79        'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
80        'flac'  => 'audio/flac',
81        'gz'    => 'application/gzip',
82        'gif'   => 'image/gif',
83        'htm'   => 'text/html',
84        'html'  => 'text/html',
85        'ico'   => 'image/vnd.microsoft.icon',
86        'ics'   => 'text/calendar',
87        'jar'   => 'application/java-archive',
88        'jpe'   => 'image/jpeg',
89        'jpg'   => 'image/jpeg',
90        'jpeg'  => 'image/jpeg',
91        'js'    => 'text/javascript',
92        'json'  => 'application/json',
93        'jwt'   => 'application/jwt',
94        'log'   => 'text/plain',
95        'mid'   => 'audio/midi',
96        'midi'  => 'audio/midi',
97        'mp3'   => 'audio/mpeg',
98        'm4a'   => 'audio/mp4',
99        'mp4'   => 'video/mp4',
100        'mov'   => 'video/quicktime',
101        'mpeg'  => 'video/mpeg',
102        'odp'   => 'application/vnd.oasis.opendocument.presentation',
103        'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
104        'odt'   => 'application/vnd.oasis.opendocument.text',
105        'ogg'   => 'audio/ogg',
106        'oga'   => 'audio/oga',
107        'ogv'   => 'video/ogg',
108        'ogx'   => 'application/ogg',
109        'otf'   => 'font/otf',
110        'png'   => 'image/png',
111        'pdf'   => 'application/pdf',
112        'php'   => 'application/x-httpd-php',
113        'ppt'   => 'application/vnd.ms-powerpoint',
114        'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
115        'psd'   => 'image/vnd.adobe.photoshop',
116        'rar'   => 'application/vnd.rar',
117        'rtf'   => 'application/rtf',
118        'sgml'  => 'application/sgml',
119        'sql'   => 'application/sql',
120        'svg'   => 'image/svg+xml',
121        'tar'   => 'application/x-tar',
122        'tif'   => 'image/tiff',
123        'tiff'  => 'image/tiff',
124        'ttf'   => 'font/ttf',
125        'tsv'   => 'text/tsv',
126        'txt'   => 'text/plain',
127        'wav'   => 'audio/wav',
128        'wmv'   => 'video/x-ms-wmv',
129        'xhtml' => 'application/xhtml+xml',
130        'xls'   => 'application/vnd.ms-excel',
131        'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
132        'xml'   => 'application/xml',
133        'yaml'  => 'application/yaml',
134        'zip'   => 'application/zip',
135    ];
136
137    /**
138     * Default mime types
139     * @var string
140     */
141    protected static string $defaultMimeType = 'application/octet-stream';
142
143    /**
144     * Constructor
145     *
146     * Instantiate the file object
147     *
148     * @param  ?string $filename
149     */
150    public function __construct(?string $filename = null)
151    {
152        $info = [];
153
154        if (!empty($filename)) {
155            $info = pathinfo($filename);
156            if (file_exists($filename)) {
157                $this->setSize(filesize($filename));
158            }
159        }
160
161        if (!empty($info['basename'])) {
162            $this->setBasename($info['basename']);
163        }
164        if (!empty($info['filename'])) {
165            $this->setFilename($info['filename']);
166        }
167        if (!empty($info['dirname'])) {
168            $this->setPath($info['dirname']);
169        }
170        if (!empty($info['extension'])) {
171            $this->setExtension($info['extension']);
172        }
173    }
174
175    /**
176     * Format file size
177     *
178     * @param  int    $filesize
179     * @param  int    $round
180     * @param  ?bool  $case null = UPPER (MB); true = Title (Mb); false = lower (mb)
181     * @param  string $space
182     * @return string
183     */
184    public static function formatFileSize(int $filesize, int $round = 2, ?bool $case = null, string $space = ' '): string
185    {
186        $file = new self();
187        $file->setSize($filesize);
188        return $file->formatSize();
189    }
190
191    /**
192     * Get common mime types
193     *
194     * @return array
195     */
196    public static function getMimeTypes(): array
197    {
198        return (new self())->getAllMimeTypes();
199    }
200
201    /**
202     * Get file's mime type
203     *
204     * @param  string $filename
205     * @return ?string
206     */
207    public static function getFileMimeType(string $filename): ?string
208    {
209        return (new self($filename))->getMimeType();
210    }
211
212    /**
213     * Check if file is an image file
214     *
215     * @param  string $filename
216     * @return bool
217     */
218    public static function isImage(string $filename): bool
219    {
220        $imageFormats = [
221            static::$mimeTypes['bmp'],
222            static::$mimeTypes['gif'],
223            static::$mimeTypes['ico'],
224            static::$mimeTypes['jpe'],
225            static::$mimeTypes['jpg'],
226            static::$mimeTypes['jpeg'],
227            static::$mimeTypes['png'],
228            static::$mimeTypes['psd'],
229            static::$mimeTypes['svg'],
230            static::$mimeTypes['tif'],
231            static::$mimeTypes['tiff'],
232        ];
233
234        return in_array(static::getFileMimeType($filename), $imageFormats);
235    }
236
237    /**
238     * Check if file is a web image file
239     *
240     * @param  string $filename
241     * @return bool
242     */
243    public static function isWebImage(string $filename): bool
244    {
245        $webImageFormats = [
246            static::$mimeTypes['gif'],
247            static::$mimeTypes['ico'],
248            static::$mimeTypes['jpe'],
249            static::$mimeTypes['jpg'],
250            static::$mimeTypes['jpeg'],
251            static::$mimeTypes['png'],
252            static::$mimeTypes['svg'],
253        ];
254
255        return in_array(static::getFileMimeType($filename), $webImageFormats);
256    }
257
258    /**
259     * Check if file is a video file
260     *
261     * @param  string $filename
262     * @return bool
263     */
264    public static function isVideo(string $filename): bool
265    {
266        $videoFormats = [
267            static::$mimeTypes['avi'],
268            static::$mimeTypes['mov'],
269            static::$mimeTypes['mp4'],
270            static::$mimeTypes['mpeg'],
271            static::$mimeTypes['ogv'],
272            static::$mimeTypes['ogx'],
273            static::$mimeTypes['wmv'],
274        ];
275
276        return in_array(static::getFileMimeType($filename), $videoFormats);
277    }
278
279    /**
280     * Check if file is an audio file
281     *
282     * @param  string $filename
283     * @return bool
284     */
285    public static function isAudio(string $filename): bool
286    {
287        $audioFormats = [
288            static::$mimeTypes['aiff'],
289            static::$mimeTypes['flac'],
290            static::$mimeTypes['mid'],
291            static::$mimeTypes['midi'],
292            static::$mimeTypes['mp3'],
293            static::$mimeTypes['m4a'],
294            static::$mimeTypes['ogg'],
295            static::$mimeTypes['oga'],
296            static::$mimeTypes['ogx'],
297            static::$mimeTypes['wav'],
298        ];
299
300        return in_array(static::getFileMimeType($filename), $audioFormats);
301    }
302
303    /**
304     * Check if file is a text file
305     *
306     * @param  string $filename
307     * @return bool
308     */
309    public static function isText(string $filename): bool
310    {
311        $textFormats = [
312            static::$mimeTypes['csv'],
313            static::$mimeTypes['log'],
314            static::$mimeTypes['tsv'],
315            static::$mimeTypes['txt'],
316        ];
317
318        return in_array(static::getFileMimeType($filename), $textFormats);
319    }
320
321    /**
322     * Check if file is a compressed file
323     *
324     * @param  string $filename
325     * @return bool
326     */
327    public static function isCompressed(string $filename): bool
328    {
329        $compressedFormats = [
330            static::$mimeTypes['bz'],
331            static::$mimeTypes['bz2'],
332            static::$mimeTypes['gz'],
333            static::$mimeTypes['jar'],
334            static::$mimeTypes['rar'],
335            static::$mimeTypes['tar'],
336            static::$mimeTypes['zip'],
337        ];
338
339        return in_array(static::getFileMimeType($filename), $compressedFormats);
340    }
341
342    /**
343     * Check if file is a Word document
344     *
345     * @param  string $filename
346     * @return bool
347     */
348    public static function isWord(string $filename): bool
349    {
350        $wordFormats = [
351            static::$mimeTypes['doc'],
352            static::$mimeTypes['docx'],
353            static::$mimeTypes['rtf'],
354        ];
355
356        return in_array(static::getFileMimeType($filename), $wordFormats);
357    }
358
359    /**
360     * Check if file is a PDF document
361     *
362     * @param  string $filename
363     * @return bool
364     */
365    public static function isPdf(string $filename): bool
366    {
367        return (static::getFileMimeType($filename) == 'application/pdf');
368    }
369
370    /**
371     * Set the basename
372     *
373     * @param  string $basename
374     * @return File
375     */
376    public function setBasename(string $basename): File
377    {
378        $this->basename = $basename;
379        return $this;
380    }
381
382    /**
383     * Get the basename
384     *
385     * @return ?string
386     */
387    public function getBasename(): ?string
388    {
389        return $this->basename;
390    }
391
392    /**
393     * Has the basename
394     *
395     * @return bool
396     */
397    public function hasBasename(): bool
398    {
399        return ($this->basename !== null);
400    }
401
402    /**
403     * Set the filename
404     *
405     * @param  string $filename
406     * @return File
407     */
408    public function setFilename(string $filename): File
409    {
410        $this->filename = $filename;
411        return $this;
412    }
413
414    /**
415     * Get the filename
416     *
417     * @return ?string
418     */
419    public function getFilename(): ?string
420    {
421        return $this->filename;
422    }
423
424    /**
425     * Has the filename
426     *
427     * @return bool
428     */
429    public function hasFilename(): bool
430    {
431        return ($this->filename !== null);
432    }
433
434    /**
435     * Set the extension
436     *
437     * @param  string $extension
438     * @return File
439     */
440    public function setExtension(string $extension): File
441    {
442        $this->extension = $extension;
443        if (array_key_exists(strtolower($extension), self::$mimeTypes)) {
444            $this->setMimeType(self::$mimeTypes[strtolower($extension)]);
445        } else {
446            $this->setDefaultMimeType();
447        }
448        return $this;
449    }
450
451    /**
452     * Get the extension
453     *
454     * @return ?string
455     */
456    public function getExtension(): ?string
457    {
458        return $this->extension;
459    }
460
461    /**
462     * Has the extension
463     *
464     * @return bool
465     */
466    public function hasExtension(): bool
467    {
468        return ($this->extension !== null);
469    }
470
471    /**
472     * Set the path
473     *
474     * @param  string $path
475     * @return File
476     */
477    public function setPath(string $path): File
478    {
479        $this->path = $path;
480        return $this;
481    }
482
483    /**
484     * Get the path
485     *
486     * @return ?string
487     */
488    public function getPath(): ?string
489    {
490        return $this->path;
491    }
492
493    /**
494     * Has the path
495     *
496     * @return bool
497     */
498    public function hasPath(): bool
499    {
500        return ($this->path !== null);
501    }
502
503    /**
504     * Set the size
505     *
506     * @param  int $size
507     * @return File
508     */
509    public function setSize(int $size): File
510    {
511        $this->size = $size;
512        return $this;
513    }
514
515    /**
516     * Get the size
517     *
518     * @return int
519     */
520    public function getSize(): int
521    {
522        return $this->size;
523    }
524
525    /**
526     * Has the size
527     *
528     * @return bool
529     */
530    public function hasSize(): bool
531    {
532        return ($this->size > 0);
533    }
534
535    /**
536     * Format size into human-readable string
537     *
538     * @param  int    $round
539     * @param  ?bool  $case null = UPPER (MB); true = Title (Mb); false = lower (mb)
540     * @param  string $space
541     * @return string
542     */
543    public function formatSize(int $round = 2, ?bool $case = null, string $space = ' '): string
544    {
545        $prefix = '';
546        $byte   = ($case !== null) ? 'b' : 'B';
547
548        if ($this->size >= 1000000000000) {
549            $prefix    = ($case !== false) ? 'T' : 't';
550            $formatted = round($this->size / 1000000000000, $round);
551        } else if (($this->size < 1000000000000) && ($this->size >= 1000000000)) {
552            $prefix    = ($case !== false) ? 'G' : 'g';
553            $formatted = round($this->size / 1000000000, $round);
554        } else if (($this->size < 1000000000) && ($this->size >= 1000000)) {
555            $prefix    = ($case !== false) ? 'M' : 'm';
556            $formatted = round($this->size / 1000000, $round);
557        } else if (($this->size < 1000000) && ($this->size >= 1000)) {
558            $prefix    = ($case !== false) ? 'K' : 'k';
559            $formatted = round($this->size / 1000, $round);
560        } else {
561            $formatted = $this->size;
562        }
563
564        return $formatted . $space . $prefix . $byte;
565    }
566
567    /**
568     * Set the mime type
569     *
570     * @param  string $mimeType
571     * @return File
572     */
573    public function setMimeType(string $mimeType): File
574    {
575        $this->mimeType = $mimeType;
576        return $this;
577    }
578
579    /**
580     * Get the mime type
581     *
582     * @return ?string
583     */
584    public function getMimeType(): ?string
585    {
586        return $this->mimeType;
587    }
588
589    /**
590     * Has the mime type
591     *
592     * @return bool
593     */
594    public function hasMimeType(): bool
595    {
596        return ($this->mimeType !== null);
597    }
598
599    /**
600     * Set the mime type to default mime type
601     *
602     * @return File
603     */
604    public function setDefaultMimeType(): File
605    {
606        $this->mimeType = self::$defaultMimeType;
607        return $this;
608    }
609
610    /**
611     * Get the default mime type
612     *
613     * @return string
614     */
615    public function getDefaultMimeType(): string
616    {
617        return self::$defaultMimeType;
618    }
619
620    /**
621     * Set the default mime type
622     *
623     * @return bool
624     */
625    public function isDefaultMimeType(): bool
626    {
627        return ($this->mimeType == self::$defaultMimeType);
628    }
629
630    /**
631     * Get the all common mime types
632     *
633     * @return array
634     */
635    public function getAllMimeTypes(): array
636    {
637        return self::$mimeTypes;
638    }
639
640    /**
641     * Does the file exist
642     *
643     * @return bool
644     */
645    public function exists(): bool
646    {
647        $fullPath = ($this->hasPath()) ? $this->path . DIRECTORY_SEPARATOR . $this->basename : $this->basename;
648        return file_exists($fullPath);
649    }
650
651    /**
652     * Get the file contents
653     *
654     * @return mixed
655     */
656    public function getContents(): mixed
657    {
658        $fullPath = ($this->hasPath()) ? $this->path . DIRECTORY_SEPARATOR . $this->basename : $this->basename;
659        return file_get_contents($fullPath);
660    }
661
662    /**
663     * Convert file to an array
664     *
665     * @return array
666     */
667    public function toArray(): array
668    {
669        return [
670            'basename'  => $this->basename,
671            'filename'  => $this->filename,
672            'extension' => $this->extension,
673            'path'      => $this->path,
674            'size'      => $this->size,
675            'mime_type' => $this->mimeType,
676        ];
677    }
678
679    /**
680     * To string
681     *
682     * @return string
683     */
684    public function __toString(): string
685    {
686        return ($this->hasPath()) ? $this->path . DIRECTORY_SEPARATOR . $this->basename : $this->basename;
687    }
688
689}