File   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Test Coverage

Coverage 72.62%

Importance

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

8 Methods

Rating   Name   Duplication   Size   Complexity  
A isRemote() 0 3 1
A getFS() 0 7 2
A isRemoteExists() 0 15 5
A getMediaType() 0 15 4
A fileGetContents() 0 28 4
A readExif() 0 26 5
A getExtension() 0 20 4
A getRealPath() 0 13 3
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
     * @return string|false
48
     */
49 1
    public static function fileGetContents(string $filename, ?string $userAgent = null)
50
    {
51 1
        if (empty($filename)) {
52
            return false;
53
        }
54
55 1
        set_error_handler(
56 1
            function ($severity, $message, $file, $line) {
57 1
                throw new \ErrorException($message, 0, $severity, $file, $line, null);
58 1
            }
59 1
        );
60
61
        try {
62 1
            $options = [
63 1
                'http' => [
64 1
                    'method'          => 'GET',
65 1
                    'follow_location' => true,
66 1
                ],
67 1
            ];
68 1
            if (!empty($userAgent)) {
69
                $options['http']['header'] = "User-Agent: $userAgent";
70
            }
71
72 1
            return file_get_contents($filename, false, stream_context_create($options));
73 1
        } catch (\ErrorException) {
74 1
            return false;
75
        } finally {
76 1
            restore_error_handler();
77
        }
78
    }
79
80
    /**
81
     * Returns the media type and subtype of a file.
82
     *
83
     * ie: ['text', 'text/plain']
84
     */
85 1
    public static function getMediaType(string $filename): array
86
    {
87
        try {
88 1
            if (false !== $subtype = mime_content_type($filename)) {
89 1
                return [explode('/', $subtype)[0], $subtype];
90
            }
91
            $mimeTypes = new MimeTypes();
92
            $subtype = $mimeTypes->guessMimeType($filename);
93
            if ($subtype === null) {
94
                throw new RuntimeException('Can\'t guess the media type.');
95
            }
96
97
            return [explode('/', $subtype)[0], $subtype];
98
        } catch (\Exception $e) {
99
            throw new RuntimeException(\sprintf('Can\'t get media type of "%s" (%s).', $filename, $e->getMessage()));
100
        }
101
    }
102
103
    /**
104
     * Returns the extension of a file.
105
     */
106 1
    public static function getExtension(string $filename): string
107
    {
108
        try {
109 1
            $ext = pathinfo($filename, \PATHINFO_EXTENSION);
110 1
            if (!empty($ext)) {
111 1
                return $ext;
112
            }
113
            // guess the extension
114 1
            $mimeTypes = new MimeTypes();
115 1
            $mimeType = $mimeTypes->guessMimeType($filename);
116 1
            if ($mimeType === null) {
117
                throw new RuntimeException('Can\'t guess the media type.');
118
            }
119 1
            $exts = $mimeTypes->getExtensions($mimeType);
120
121 1
            return $exts[0];
122
        } catch (\Exception $e) {
123
            throw new RuntimeException(
124
                \sprintf('Can\'t get extension of "%s".', $filename),
125
                previous: $e,
126
            );
127
        }
128
    }
129
130
    /**
131
     * exif_read_data() function with error handler.
132
     */
133 1
    public static function readExif(string $filename): array
134
    {
135 1
        if (empty($filename)) {
136
            return [];
137
        }
138
139 1
        set_error_handler(
140 1
            function ($severity, $message, $file, $line) {
141
                throw new \ErrorException($message, 0, $severity, $file, $line, null);
142 1
            }
143 1
        );
144
145
        try {
146 1
            if (!\function_exists('exif_read_data')) {
147
                throw new \ErrorException('`exif` extension is not available.');
148
            }
149 1
            $exif = exif_read_data($filename, null, true);
150 1
            if ($exif === false) {
151
                return [];
152
            }
153
154 1
            return $exif;
155
        } catch (\ErrorException) {
156
            return [];
157
        } finally {
158 1
            restore_error_handler();
159
        }
160
    }
161
162
    /**
163
     * Returns the real path of a relative file path.
164
     */
165 1
    public static function getRealPath(string $path): string
166
    {
167
        // if file exists
168 1
        $filePath = realpath(\Cecil\Util::joinFile(__DIR__, '/../', $path));
169 1
        if ($filePath !== false) {
170 1
            return $filePath;
171
        }
172
        // if Phar
173 1
        if (Platform::isPhar()) {
174
            return \Cecil\Util::joinPath(Platform::getPharPath(), str_replace('../', '/', $path));
175
        }
176
177 1
        throw new RuntimeException(\sprintf('Can\'t get the real path of file "%s".', $path));
178
    }
179
180
    /**
181
     * Tests if a file path is remote.
182
     */
183 1
    public static function isRemote(string $path): bool
184
    {
185 1
        return (bool) preg_match('~^(?:f|ht)tps?://~i', $path);
186
    }
187
188
    /**
189
     * Tests if a remote file exists.
190
     */
191 1
    public static function isRemoteExists(string $path): bool
192
    {
193 1
        if (self::isRemote($path)) {
194 1
            $handle = @fopen($path, 'r');
195 1
            if (!empty($http_response_header)) {
196 1
                if (400 < (int) explode(' ', $http_response_header[0])[1]) {
197 1
                    return false;
198
                }
199
            }
200 1
            if (\is_resource($handle)) {
201 1
                return true;
202
            }
203
        }
204
205
        return false;
206
    }
207
}
208