File   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Test Coverage

Coverage 72.62%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 77
dl 0
loc 177
ccs 61
cts 84
cp 0.7262
rs 10
c 1
b 0
f 0
wmc 28

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getFS() 0 7 2
A isRemote() 0 3 1
A isRemoteExists() 0 15 5
A getMediaType() 0 15 4
A readExif() 0 26 5
A getExtension() 0 20 4
A getRealPath() 0 13 3
A fileGetContents() 0 28 4
1
<?php
2
3
/**
4
 * This file is part of Cecil.
5
 *
6
 * (c) Arnaud Ligny <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Cecil\Util;
15
16
use Cecil\Exception\RuntimeException;
17
use Symfony\Component\Filesystem\Filesystem;
18
use Symfony\Component\Mime\MimeTypes;
19
20
/**
21
 * File utility class.
22
 *
23
 * This class provides various utility methods for file handling,
24
 * including reading file contents, getting media types and extensions,
25
 * reading EXIF data, and checking if files are remote.
26
 */
27
class File
28
{
29
    /** @var Filesystem */
30
    protected static $fs;
31
32
    /**
33
     * Returns a Symfony\Component\Filesystem instance.
34
     */
35 1
    public static function getFS(): Filesystem
36
    {
37 1
        if (!self::$fs instanceof Filesystem) {
38 1
            self::$fs = new Filesystem();
39
        }
40
41 1
        return self::$fs;
42
    }
43
44
    /**
45
     * file_get_contents() function with error handler.
46
     */
47 1
    public static function fileGetContents(string $filename, ?string $userAgent = null): string|false
48
    {
49 1
        if (empty($filename)) {
50
            return false;
51
        }
52
53 1
        set_error_handler(
54 1
            function ($severity, $message, $file, $line) {
55 1
                throw new \ErrorException($message, 0, $severity, $file, $line, null);
56 1
            }
57 1
        );
58
59
        try {
60 1
            $options = [
61 1
                'http' => [
62 1
                    'method'          => 'GET',
63 1
                    'follow_location' => true,
64 1
                ],
65 1
            ];
66 1
            if (!empty($userAgent)) {
67
                $options['http']['header'] = "User-Agent: $userAgent";
68
            }
69
70 1
            return file_get_contents($filename, false, stream_context_create($options));
71 1
        } catch (\ErrorException) {
72 1
            return false;
73
        } finally {
74 1
            restore_error_handler();
75
        }
76
    }
77
78
    /**
79
     * Returns the media type and subtype of a file.
80
     *
81
     * ie: ['text', 'text/plain']
82
     */
83 1
    public static function getMediaType(string $filename): array
84
    {
85
        try {
86 1
            if (false !== $subtype = mime_content_type($filename)) {
87 1
                return [explode('/', $subtype)[0], $subtype];
88
            }
89
            $mimeTypes = new MimeTypes();
90
            $subtype = $mimeTypes->guessMimeType($filename);
91
            if ($subtype === null) {
92
                throw new RuntimeException('Unable to guess the media type.');
93
            }
94
95
            return [explode('/', $subtype)[0], $subtype];
96
        } catch (\Exception $e) {
97
            throw new RuntimeException(\sprintf('Unable to get media type of "%s" (%s).', $filename, $e->getMessage()));
98
        }
99
    }
100
101
    /**
102
     * Returns the extension of a file.
103
     */
104 1
    public static function getExtension(string $filename): string
105
    {
106
        try {
107 1
            $ext = pathinfo($filename, \PATHINFO_EXTENSION);
108 1
            if (!empty($ext)) {
109 1
                return $ext;
110
            }
111
            // guess the extension
112 1
            $mimeTypes = new MimeTypes();
113 1
            $mimeType = $mimeTypes->guessMimeType($filename);
114 1
            if ($mimeType === null) {
115
                throw new RuntimeException('Unable to guess the media type.');
116
            }
117 1
            $exts = $mimeTypes->getExtensions($mimeType);
118
119 1
            return $exts[0];
120
        } catch (\Exception $e) {
121
            throw new RuntimeException(
122
                \sprintf('Unable to get extension of "%s".', $filename),
123
                previous: $e,
124
            );
125
        }
126
    }
127
128
    /**
129
     * exif_read_data() function with error handler.
130
     */
131 1
    public static function readExif(string $filename): array
132
    {
133 1
        if (empty($filename)) {
134
            return [];
135
        }
136
137 1
        set_error_handler(
138 1
            function ($severity, $message, $file, $line) {
139
                throw new \ErrorException($message, 0, $severity, $file, $line, null);
140 1
            }
141 1
        );
142
143
        try {
144 1
            if (!\function_exists('exif_read_data')) {
145
                throw new \ErrorException('`exif` extension is not available.');
146
            }
147 1
            $exif = exif_read_data($filename, null, true);
148 1
            if ($exif === false) {
149
                return [];
150
            }
151
152 1
            return $exif;
153
        } catch (\ErrorException) {
154
            return [];
155
        } finally {
156 1
            restore_error_handler();
157
        }
158
    }
159
160
    /**
161
     * Returns the real path of a relative file path.
162
     */
163 1
    public static function getRealPath(string $path): string
164
    {
165
        // if file exists
166 1
        $filePath = realpath(\Cecil\Util::joinFile(__DIR__, '/../', $path));
167 1
        if ($filePath !== false) {
168 1
            return $filePath;
169
        }
170
        // if Phar
171 1
        if (Platform::isPhar()) {
172
            return \Cecil\Util::joinPath(Platform::getPharPath(), str_replace('../', '/', $path));
173
        }
174
175 1
        throw new RuntimeException(\sprintf('Unable to get the real path of file "%s".', $path));
176
    }
177
178
    /**
179
     * Tests if a file path is remote.
180
     */
181 1
    public static function isRemote(string $path): bool
182
    {
183 1
        return (bool) preg_match('~^(?:f|ht)tps?://~i', $path);
184
    }
185
186
    /**
187
     * Tests if a remote file exists.
188
     */
189 1
    public static function isRemoteExists(string $path): bool
190
    {
191 1
        if (self::isRemote($path)) {
192 1
            $handle = @fopen($path, 'r');
193 1
            if (!empty($http_response_header)) {
194 1
                if (400 < (int) explode(' ', $http_response_header[0])[1]) {
195 1
                    return false;
196
                }
197
            }
198 1
            if (\is_resource($handle)) {
199 1
                return true;
200
            }
201
        }
202
203
        return false;
204
    }
205
}
206