• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Cecilapp / Cecil / 16854066639

09 Aug 2025 09:50PM UTC coverage: 81.764%. Remained the same
16854066639

Pull #2207

github

web-flow
Merge ca86389f2 into f23f6dc0a
Pull Request #2207: refactor: Refactor asset and image classes, update namespaces

6 of 6 new or added lines in 3 files covered. (100.0%)

1 existing line in 1 file now uncovered.

3143 of 3844 relevant lines covered (81.76%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.74
/src/Util/File.php
1
<?php
2

3
/**
4
 * This file is part of Cecil.
5
 *
6
 * (c) Arnaud Ligny <arnaud@ligny.fr>
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
    public static function getFS(): Filesystem
36
    {
37
        if (!self::$fs instanceof Filesystem) {
1✔
38
            self::$fs = new Filesystem();
1✔
39
        }
40

41
        return self::$fs;
1✔
42
    }
43

44
    /**
45
     * file_get_contents() function with error handler.
46
     *
47
     * @return string|false
48
     */
49
    public static function fileGetContents(string $filename, ?string $userAgent = null)
50
    {
51
        if (empty($filename)) {
1✔
52
            return false;
×
53
        }
54

55
        set_error_handler(
1✔
56
            function ($severity, $message, $file, $line) {
1✔
57
                throw new \ErrorException($message, 0, $severity, $file, $line, null);
1✔
58
            }
1✔
59
        );
1✔
60

61
        try {
62
            $options = [
1✔
63
                'http' => [
1✔
64
                    'method'          => 'GET',
1✔
65
                    'follow_location' => true,
1✔
66
                ],
1✔
67
            ];
1✔
68
            if (!empty($userAgent)) {
1✔
69
                $options['http']['header'] = "User-Agent: $userAgent";
×
70
            }
71

72
            return file_get_contents($filename, false, stream_context_create($options));
1✔
73
        } catch (\ErrorException) {
1✔
74
            return false;
1✔
75
        } finally {
76
            restore_error_handler();
1✔
77
        }
78
    }
79

80
    /**
81
     * Returns the media type and subtype of a file.
82
     *
83
     * ie: ['text', 'text/plain']
84
     */
85
    public static function getMediaType(string $filename): array
86
    {
87
        try {
88
            if (false !== $subtype = mime_content_type($filename)) {
1✔
89
                return [explode('/', $subtype)[0], $subtype];
1✔
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
    public static function getExtension(string $filename): string
107
    {
108
        try {
109
            $ext = pathinfo($filename, PATHINFO_EXTENSION);
1✔
110
            if (!empty($ext)) {
1✔
111
                return $ext;
1✔
112
            }
113
            // guess the extension
114
            $mimeTypes = new MimeTypes();
1✔
115
            $mimeType = $mimeTypes->guessMimeType($filename);
1✔
116
            if ($mimeType === null) {
1✔
117
                throw new RuntimeException('Can\'t guess the media type.');
×
118
            }
119
            $exts = $mimeTypes->getExtensions($mimeType);
1✔
120

121
            return $exts[0];
1✔
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
    public static function readExif(string $filename): array
134
    {
135
        if (empty($filename)) {
1✔
136
            return [];
×
137
        }
138

139
        set_error_handler(
1✔
140
            function ($severity, $message, $file, $line) {
1✔
141
                throw new \ErrorException($message, 0, $severity, $file, $line, null);
×
142
            }
1✔
143
        );
1✔
144

145
        try {
146
            if (!\function_exists('exif_read_data')) {
1✔
147
                throw new \ErrorException('`exif` extension is not available.');
×
148
            }
149
            $exif = exif_read_data($filename, null, true);
1✔
150
            if ($exif === false) {
1✔
151
                return [];
×
152
            }
153

154
            return $exif;
1✔
155
        } catch (\ErrorException) {
×
156
            return [];
×
157
        } finally {
158
            restore_error_handler();
1✔
159
        }
160
    }
161

162
    /**
163
     * Returns the real path of a relative file path.
164
     */
165
    public static function getRealPath(string $path): string
166
    {
167
        // if file exists
168
        $filePath = realpath(\Cecil\Util::joinFile(__DIR__, '/../', $path));
1✔
169
        if ($filePath !== false) {
1✔
170
            return $filePath;
1✔
171
        }
172
        // if Phar
173
        if (Platform::isPhar()) {
1✔
174
            return \Cecil\Util::joinPath(Platform::getPharPath(), str_replace('../', '/', $path));
×
175
        }
176

177
        throw new RuntimeException(\sprintf('Can\'t get the real path of file "%s".', $path));
1✔
178
    }
179

180
    /**
181
     * Tests if a file path is remote.
182
     */
183
    public static function isRemote(string $path): bool
184
    {
185
        return (bool) preg_match('~^(?:f|ht)tps?://~i', $path);
1✔
186
    }
187

188
    /**
189
     * Tests if a remote file exists.
190
     */
191
    public static function isRemoteExists(string $path): bool
192
    {
193
        if (self::isRemote($path)) {
1✔
194
            $handle = @fopen($path, 'r');
1✔
195
            if (!empty($http_response_header)) {
1✔
196
                if (400 < (int) explode(' ', $http_response_header[0])[1]) {
1✔
UNCOV
197
                    return false;
×
198
                }
199
            }
200
            if (\is_resource($handle)) {
1✔
201
                return true;
1✔
202
            }
203
        }
204

205
        return false;
1✔
206
    }
207
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc