Passed
Push — develop ( a949e6...03023c )
by Eric
02:16
created

Generator::generateMapping()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 4
nop 0
dl 0
loc 25
ccs 15
cts 15
cp 1
crap 4
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Esi\Mimey.
7
 *
8
 * (c) Eric Sizemore <[email protected]>
9
 * (c) Ricardo Boss <[email protected]>
10
 * (c) Ralph Khattar <[email protected]>
11
 *
12
 * This source file is subject to the MIT license. For the full copyright,
13
 * license information, and credits/acknowledgements, please view the LICENSE
14
 * and README files that were distributed with this source code.
15
 */
16
/**
17
 * Esi\Mimey is a fork of Elephox\Mimey (https://github.com/elephox-dev/mimey) which is:
18
 *     Copyright (c) 2022 Ricardo Boss
19
 * Elephox\Mimey is a fork of ralouphie/mimey (https://github.com/ralouphie/mimey) which is:
20
 *     Copyright (c) 2016 Ralph Khattar
21
 */
22
23
namespace Esi\Mimey\Mapping;
24
25
use Esi\Mimey\Interface\MimeTypeInterface;
26
use JsonException;
27
use RuntimeException;
28
29
use function array_filter;
30
use function array_unique;
31
use function array_values;
32
use function explode;
33
use function file_get_contents;
34
use function json_encode;
35
use function preg_replace;
36
use function sprintf;
37
use function str_pad;
38
use function str_replace;
39
use function trim;
40
use function ucfirst;
41
use function ucwords;
42
43
use const JSON_PRETTY_PRINT;
44
use const JSON_THROW_ON_ERROR;
45
use const STR_PAD_LEFT;
46
47
/**
48
 * Generates a mapping for use in the MimeTypes class.
49
 *
50
 * Reads text in the format of httpd's mime.types and generates a PHP array containing the mappings.
51
 *
52
 * The psalm-type looks gnarly, but it covers just about everything.
53
 *
54
 * @phpstan-type MimeTypeMap = array{
55
 *    mimes?: array<
56
 *        non-empty-string|string, list<non-empty-string>|array<int<0, max>, string>
57
 *    >|non-empty-array<
58
 *        non-falsy-string|string, non-empty-array<0, non-falsy-string>|array<int<0, max>, string>
59
 *    >,
60
 *    extensions?: array<
61
 *        non-empty-string, list<non-empty-string>
62
 *     >|non-empty-array<
63
 *        string|non-falsy-string, array<int<0, max>, string>|non-empty-array<0, non-falsy-string>
64
 *    >|array<
65
 *        string, array<int<0, max>, string>
66
 *    >
67
 * }
68
 */
69
class Generator
70
{
71
    /**
72
     * @var MimeTypeMap
73
     */
74
    protected array $mapCache = [];
75
76
    /**
77
     * Create a new generator instance with the given mime.types text.
78
     *
79
     * @param non-empty-string $mimeTypesText The text from the mime.types file.
80
     */
81 4
    public function __construct(protected string $mimeTypesText) {}
82
83
    /**
84
     * Generate the JSON from the mapCache.
85
     *
86
     * @param bool $minify Whether to minify the generated JSON.
87
     *
88
     * @throws JsonException
89
     *
90
     * @return non-empty-string
91
     */
92 1
    public function generateJson(bool $minify = true): string
93
    {
94 1
        return json_encode($this->generateMapping(), flags: JSON_THROW_ON_ERROR | ($minify ? 0 : JSON_PRETTY_PRINT));
95
    }
96
97
    /**
98
     * Read the given mime.types text and return a mapping compatible with the MimeTypes class.
99
     *
100
     * @return MimeTypeMap The mapping.
101
     */
102 4
    public function generateMapping(): array
103
    {
104 4
        if ($this->mapCache !== []) {
105 1
            return $this->mapCache;
106
        }
107
108 4
        $lines = explode("\n", $this->mimeTypesText);
109
110 4
        foreach ($lines as $line) {
111 4
            $line = preg_replace('~#.*~', '', $line) ?? $line;
112 4
            $line = trim($line);
113
114 4
            if ($line === '') {
115 4
                continue;
116
            }
117
118 3
            $parts = array_values(array_filter(
119 3
                explode("\t", $line),
120 3
                static fn (string $value): bool => trim($value) !== ''
121 3
            ));
122
123 3
            $this->generateMapCache($parts);
124
        }
125
126 4
        return $this->mapCache;
127
    }
128
129
    /**
130
     * Generates the PHP Enum found in `dist`.
131
     *
132
     * @param non-empty-string $classname
133
     * @param non-empty-string $namespace
134
     *
135
     * @throws RuntimeException
136
     *
137
     * @return string
138
     */
139 2
    public function generatePhpEnum(string $classname = 'MimeType', string $namespace = 'Esi\Mimey'): string
140
    {
141 2
        $values = [
142 2
            'namespace'       => $namespace,
143 2
            'classname'       => $classname,
144 2
            'interface_usage' => $namespace !== __NAMESPACE__ ? ('use ' . MimeTypeInterface::class . ";\n") : '',
145 2
            'cases'           => '',
146 2
            'type2ext'        => '',
147 2
            'ext2type'        => '',
148 2
        ];
149
150 2
        $stubContents = (string) file_get_contents(\dirname(__DIR__, 2) . '/stubs/mimeType.php.stub');
151
152 2
        $mapping = $this->generateMapping();
153
154 2
        $nameMap = [];
155
156 2
        if (!isset($mapping['mimes'], $mapping['extensions'])) {
157 1
            throw new RuntimeException('Unable to generate mapping. Possibly passed malformed $mimeTypesText');
158
        }
159
160 1
        foreach ($mapping['extensions'] as $mime => $extensions) {
161 1
            $nameMap[$mime] = $this->convertMimeTypeToCaseName($mime);
162
163 1
            $values['cases'] .= sprintf(Generator::spaceIndent(4, "case %s = '%s';\n"), $nameMap[$mime], $mime);
164 1
            $values['type2ext'] .= sprintf(Generator::spaceIndent(12, "self::%s => '%s',\n"), $nameMap[$mime], $extensions[0]);
165
        }
166
167 1
        foreach ($mapping['mimes'] as $extension => $mimes) {
168 1
            $values['ext2type'] .= sprintf(Generator::spaceIndent(12, "'%s' => self::%s,\n"), $extension, $nameMap[$mimes[0]]);
169
        }
170
171 1
        foreach ($values as $name => $value) {
172 1
            $stubContents = str_replace("%$name%", $value, $stubContents);
173
        }
174
175 1
        return $stubContents;
176
    }
177
178
    /**
179
     * @param string $mimeType
180
     */
181 1
    protected function convertMimeTypeToCaseName(string $mimeType): string
182
    {
183 1
        if ($mimeType !== '') {
184 1
            $mimeType = preg_replace('/([\/\-_+.]+)/', '', ucfirst(ucwords($mimeType, '/-_+.'))) ?? $mimeType;
185
        }
186
187 1
        return $mimeType;
188
    }
189
190
    /**
191
     * Helper function for self::generateMapping()
192
     *
193
     * @param list<string> $parts
1 ignored issue
show
Bug introduced by
The type Esi\Mimey\Mapping\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
194
     */
195 3
    protected function generateMapCache(array $parts): void
196
    {
197 3
        if (\count($parts) === 2) {
198 3
            $mime       = trim($parts[0]);
199 3
            $extensions = explode(' ', $parts[1]);
200
201 3
            foreach ($extensions as $extension) {
202 3
                $extension = trim($extension);
203
204 3
                if ($mime !== '' && $extension !== '') {
205 3
                    $this->mapCache['mimes'][$extension][] = $mime;
206 3
                    $this->mapCache['extensions'][$mime][] = $extension;
207 3
                    $this->mapCache['mimes'][$extension]   = array_unique($this->mapCache['mimes'][$extension]);
208 3
                    $this->mapCache['extensions'][$mime]   = array_unique($this->mapCache['extensions'][$mime]);
209
                }
210
            }
211
        }
212
    }
213
214
    /**
215
     * Helper function for self::generatePhpEnum().
216
     */
217 2
    protected static function spaceIndent(int $spaces, string $string): string
218
    {
219 2
        if ($spaces <= 0) {
220 1
            $spaces = 4;
221
        }
222
223 2
        $spaces += \strlen($string);
224
225 2
        return str_pad($string, $spaces, ' ', STR_PAD_LEFT);
226
    }
227
}
228