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