Passed
Push — develop ( 044541...f3e33e )
by Eric
12:20
created

Generator::spaceIndent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 2
dl 0
loc 9
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Mimey - PHP package for converting file extensions to MIME types and vice versa.
7
 *
8
 * @author    Eric Sizemore <[email protected]>
9
 * @version   2.0.0
10
 * @copyright (C) 2023-2024 Eric Sizemore
11
 * @license   The MIT License (MIT)
12
 *
13
 * Copyright (C) 2023-2024 Eric Sizemore<https://www.secondversion.com/>.
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining a copy
16
 * of this software and associated documentation files (the "Software"), to
17
 * deal in the Software without restriction, including without limitation the
18
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
19
 * sell copies of the Software, and to permit persons to whom the Software is
20
 * furnished to do so, subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be included in
23
 * all copies or substantial portions of the Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
 * THE SOFTWARE.
32
 */
33
34
/**
35
 * Esi\Mimey is a fork of Elephox\Mimey (https://github.com/elephox-dev/mimey) which is:
36
 *     Copyright (c) 2022 Ricardo Boss
37
 * Elephox\Mimey is a fork of ralouphie/mimey (https://github.com/ralouphie/mimey) which is:
38
 *     Copyright (c) 2016 Ralph Khattar
39
 */
40
41
namespace Esi\Mimey\Mapping;
42
43
use Esi\Mimey\Interface\MimeType;
44
45
// Exceptions
46
use JsonException;
47
48
// Functions & constants
49
use function array_filter;
50
use function array_unique;
51
use function array_values;
52
use function count;
53
use function dirname;
54
use function explode;
55
use function file_get_contents;
56
use function json_encode;
57
use function preg_replace;
58
use function sprintf;
59
use function str_pad;
60
use function str_replace;
61
use function strlen;
62
use function trim;
63
use function ucfirst;
64
use function ucwords;
65
66
use const JSON_PRETTY_PRINT;
67
use const JSON_THROW_ON_ERROR;
68
use const STR_PAD_LEFT;
69
70
/**
71
 * Generates a mapping for use in the MimeTypes class.
72
 *
73
 * Reads text in the format of httpd's mime.types and generates a PHP array containing the mappings.
74
 *
75
 * The psalm-type looks gnarly, but it covers just about everything.
76
 *
77
 * @psalm-type MimeTypeMap = array{
78
 *    mimes?: array<
79
 *        non-empty-string|string, list<non-empty-string>|array<int<0, max>, string>
80
 *    >|non-empty-array<
81
 *        non-falsy-string|string, non-empty-array<0, non-falsy-string>|array<int<0, max>, string>
82
 *    >,
83
 *    extensions?: array<
84
 *        non-empty-string, list<non-empty-string>
85
 *     >|non-empty-array<
86
 *        string|non-falsy-string, array<int<0, max>, string>|non-empty-array<0, non-falsy-string>
87
 *    >|array<
88
 *        string, array<int<0, max>, string>
89
 *    >
90
 * }
91
 */
92
class Generator
93
{
94
    /**
95
     * @var  MimeTypeMap|array{}  $mapCache
96
     */
97
    protected array $mapCache = [];
98
99
    /**
100
     * Create a new generator instance with the given mime.types text.
101
     *
102
     * @param  non-empty-string  $mimeTypesText  The text from the mime.types file.
103
     */
104 3
    public function __construct(protected string $mimeTypesText) {}
105
106
    /**
107
     * Read the given mime.types text and return a mapping compatible with the MimeTypes class.
108
     *
109
     * @return  MimeTypeMap|array{}  The mapping.
110
     */
111 3
    public function generateMapping(): array
112
    {
113 3
        if ($this->mapCache !== []) {
114 1
            return $this->mapCache;
115
        }
116
117 3
        $lines = explode("\n", $this->mimeTypesText);
118
119 3
        foreach ($lines as $line) {
120 3
            $line = (string) preg_replace('~#.*~', '', $line);
121 3
            $line = trim($line);
122
123 3
            $parts = $line !== '' ? array_values(array_filter(explode("\t", $line))) : [];
124
125 3
            if (count($parts) === 2) {
126 3
                $mime       = trim($parts[0]);
127 3
                $extensions = explode(' ', $parts[1]);
128
129 3
                foreach ($extensions as $extension) {
130 3
                    $extension = trim($extension);
131
132 3
                    if ($mime !== '' && $extension !== '') {
133 3
                        $this->mapCache['mimes'][$extension][] = $mime;
134 3
                        $this->mapCache['extensions'][$mime][] = $extension;
135 3
                        $this->mapCache['mimes'][$extension]   = array_unique($this->mapCache['mimes'][$extension]);
136 3
                        $this->mapCache['extensions'][$mime]   = array_unique($this->mapCache['extensions'][$mime]);
137
                    }
138
                }
139
            }
140
        }
141
142 3
        return $this->mapCache;
143
    }
144
145
    /**
146
     * Generate the JSON from the mapCache.
147
     *
148
     * @param   bool              $minify  Whether to minify the generated JSON.
149
     * @return  non-empty-string
150
     *
151
     * @throws JsonException
152
     */
153 1
    public function generateJson(bool $minify = true): string
154
    {
155 1
        return json_encode($this->generateMapping(), flags: JSON_THROW_ON_ERROR | ($minify ? 0 : JSON_PRETTY_PRINT));
156
    }
157
158
    /**
159
     * Generates the PHP Enum found in `dist`.
160
     *
161
     * @param   non-empty-string         $classname
162
     * @param   non-empty-string         $namespace
163
     * @return  non-empty-string|string
164
     */
165 1
    public function generatePhpEnum(string $classname = 'MimeType', string $namespace = 'Esi\Mimey'): string
166
    {
167 1
        $values = [
168 1
            'namespace'       => $namespace,
169 1
            'classname'       => $classname,
170 1
            'interface_usage' => $namespace !== __NAMESPACE__ ? ('use ' . MimeType::class . " as MimeTypeInterface;\n") : '',
171 1
            'cases'           => '',
172 1
            'type2ext'        => '',
173 1
            'ext2type'        => '',
174 1
        ];
175
176 1
        $stub = (string) file_get_contents(dirname(__DIR__, 2) . '/stubs/mimeType.php.stub');
177
178 1
        $mapping = $this->generateMapping();
179 1
        $nameMap = [];
180
181 1
        foreach ($mapping['extensions'] as $mime => $extensions) { // @phpstan-ignore-line
182 1
            $nameMap[$mime] = $this->convertMimeTypeToCaseName($mime);
183
184 1
            $values['cases'] .= sprintf(Generator::spaceIndent(4, "case %s = '%s';\n"), $nameMap[$mime], $mime);
185 1
            $values['type2ext'] .= sprintf(Generator::spaceIndent(12, "self::%s => '%s',\n"), $nameMap[$mime], $extensions[0]);
186
        }
187
188 1
        foreach ($mapping['mimes'] as $extension => $mimes) { // @phpstan-ignore-line
189 1
            $values['ext2type'] .= sprintf(Generator::spaceIndent(12, "'%s' => self::%s,\n"), $extension, $nameMap[$mimes[0]]);
190
        }
191
192 1
        foreach ($values as $name => $value) {
193 1
            $stub = str_replace("%$name%", $value, $stub);
194
        }
195
196 1
        return $stub;
197
    }
198
199
    /**
200
     * @param non-empty-string|string $mimeType
201
     */
202 1
    protected function convertMimeTypeToCaseName(string $mimeType): string
203
    {
204 1
        return (string) preg_replace('/([\/\-_+.]+)/', '', ucfirst(ucwords($mimeType, '/-_+.')));
205
    }
206
207 1
    protected static function spaceIndent(int $spaces, string $string): string
208
    {
209 1
        if ($spaces <= 0) {
210
            $spaces = 4;
211
        }
212
213 1
        $spaces += strlen($string);
214
215 1
        return str_pad($string, $spaces, ' ', STR_PAD_LEFT);
216
    }
217
}
218