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

Cecilapp / Cecil / 17038257680

18 Aug 2025 10:41AM UTC coverage: 77.377% (-3.4%) from 80.753%
17038257680

push

github

ArnaudLigny
fix: regex pattern for page front matter parsing

Replaces the previous PATTERN constant with a new regex that allows for more flexible whitespace handling around front matter delimiters. This improves compatibility with various file formats and resolves issues with inconsistent spacing.

2979 of 3850 relevant lines covered (77.38%)

0.77 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✔
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