StaticSiteMimeProcessor   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 101
c 3
b 2
f 0
dl 0
loc 341
rs 5.04
wmc 57

13 Methods

Rating   Name   Duplication   Size   Complexity  
A isOfFileOrImage() 0 11 4
C get_mime_for_ss_type() 0 57 13
A cleanse() 0 7 2
A isOfFile() 0 15 5
A __construct() 0 6 2
A get_mimetypes_from_text() 0 9 2
A isOfHtml() 0 15 5
C ext_to_mime_compare() 0 31 12
A getMimes() 0 3 1
A ss_type_to_suffix_map() 0 37 3
A isOfImage() 0 15 5
A setMimes() 0 3 1
A isBadMimeType() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like StaticSiteMimeProcessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StaticSiteMimeProcessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpTek\Exodus\Tool;
4
5
use SilverStripe\Core\Injector\Injectable;
6
use SilverStripe\Core\Config\Configurable;
7
use SilverStripe\Assets\File;
8
use SilverStripe\Control\HTTP;
9
use SilverStripe\Core\Config\Config;
10
11
/**
12
 *
13
 * Utility class for Mime-Type processing.
14
 *
15
 * @author Russell Michell <[email protected]>
16
 * @package phptek/silverstripe-exodus
17
 */
18
class StaticSiteMimeProcessor
19
{
20
    use Injectable;
21
    use Configurable;
22
23
    /**
24
     *
25
     * @var array internal "cache" of mime-types
26
     */
27
    public $mimeTypes;
28
29
    /**
30
     *
31
     * @return void
32
     */
33
    public function __construct()
34
    {
35
        $args = func_get_args();
36
37
        if (isset($args[0])) {
38
            $this->setMimes($args[0]);
39
        }
40
    }
41
42
    /**
43
     * Based on one of three SilverStripe core classes, returns an array of suitable mime-types
44
     * from SilverStripe config.
45
     * Used to represent matching content or all associated mimes if no type is passed.
46
     *
47
     * @param $nativeType one of: SiteTree, File, Image
0 ignored issues
show
Bug introduced by
The type PhpTek\Exodus\Tool\one 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...
48
     * @return array
49
     */
50
    public static function get_mime_for_ss_type($nativeType = null): array
51
    {
52
        $httpMimeTypes = Config::inst()->get(HTTP::class, 'MimeTypes');
53
        $ssTypeMimeMap = self::ss_type_to_suffix_map();
54
55
        // This config file not guaranteed to always be present
56
        if (!$httpMimeTypes or !is_array($httpMimeTypes)) {
57
            return [];
58
        }
59
60
        $mimes = [
61
            'sitetree' => [],
62
            'file' => [],
63
            'image' => [],
64
        ];
65
66
        // Only support specific classes
67
        if ($nativeType && !in_array(strtolower($nativeType), array_keys($mimes))) {
68
            return [];
69
        }
70
71
        foreach ($httpMimeTypes as $mimeKey => $mimeType) {
72
            // SiteTree
73
            if (in_array($mimeKey, $ssTypeMimeMap['sitetree'])) {
74
                $mimes['sitetree'][] = $mimeType;
75
            }
76
77
            // File
78
            if (in_array($mimeKey, $ssTypeMimeMap['file'])) {
79
                // Separate treatment for csv which can either be text/plain (official) or text/csv
80
                $mimeType = ($mimeKey == 'csv' ? 'text/csv' : $mimeType);
81
                $mimes['file'][] = $mimeType;
82
            }
83
84
            // Image
85
            if (in_array($mimeKey, $ssTypeMimeMap['image'])) {
86
                $mimes['image'][] = $mimeType;
87
            }
88
        }
89
90
        // Not included in Silverstripe's file-based Mime-Types
91
        array_push($mimes['file'], 'text/plain');
92
93
        // Included in Silverstripe's file-based Mime-Types which we don't want
94
        $mimes['file'] = array_filter($mimes['file'], function ($k, $v) {
0 ignored issues
show
Unused Code introduced by
The parameter $v is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

94
        $mimes['file'] = array_filter($mimes['file'], function ($k, /** @scrutinizer ignore-unused */ $v) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
95
            return stristr($k, 'htm') ? false : true;
96
        }, ARRAY_FILTER_USE_BOTH);
97
98
        if ($nativeType) {
99
            $nativeType = strtolower($nativeType);
100
101
            if (isset($mimes[$nativeType])) {
102
                return array_unique($mimes[$nativeType]);
103
            }
104
        }
105
106
        return $mimes;
107
    }
108
109
    /**
110
     * Return a mapping of SS types (File, SiteTree etc) to suitable file-extensions
111
     * out of the File class.
112
     *
113
     * @param string $nativeType
114
     * @return array
115
     */
116
    public static function ss_type_to_suffix_map($nativeType = null)
117
    {
118
        $mimeCategories = File::config()->get('app_categories');
119
120
        /*
121
         * Imported files and images are going to be passed through to Upload#load()
122
         * and checked aginst File::$app_categories so use this method to
123
         * filter calls to DataObject#validate()
124
         */
125
126
        // Get SilverStripe supported SiteTree-ish mime categories
127
        $mimeKeysForSiteTree = ['html', 'htm', 'xhtml'];
128
129
        // Get SilverStripe supported File-ish mime categories
130
        // File contains values of $mimeKeysForSiteTree which we don't want
131
        $mimeKeysForFile = $mimeCategories['document'];
132
133
        // Get SilverStripe supported Image-ish mime categories
134
        $mimeKeysForImage = $mimeCategories['image'];
135
        $map = [
136
            'sitetree'  => $mimeKeysForSiteTree,
137
            'file'      => $mimeKeysForFile,
138
            'image'     => $mimeKeysForImage
139
        ];
140
141
        if ($nativeType) {
142
            $nativeType = strtolower($nativeType);
143
144
            // Only support specific classes
145
            if (!in_array($nativeType, array_keys($mimeCategories))) {
146
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
147
            }
148
149
            return $map[$nativeType];
150
        }
151
152
        return $map;
153
    }
154
155
    /**
156
     * Compares a file-extension with a mime type. Returns true if the passed extension
157
     * matches the passed mime.
158
     *
159
     * @param string $ext The file extension to compare e.g. ".doc"
160
     * @param string $mime The Mime-Type to compare e.g. application/msword
161
     * @param boolean $fix Whether or not to try and "fix" borked file-extensions
162
     * coming through from third-parties.
163
     *
164
     * - If true, the matched extension is returned (if found, otherwise false) instead of boolean false
165
     * - This is a pretty sketchy way of doing things and relies on the file-extension string comprising the mime-type
166
     * - e.g. "pdf" can be found in "application/pdf" but "doc" cannot be found in "application/msword"
167
     *
168
     * @return mixed boolean or string $ext | $coreExt if the $fix param is set to true, no extra processing is required
169
     */
170
    public static function ext_to_mime_compare($ext, $mime, $fix = false)
171
    {
172
        $httpMimeTypes = HTTP::config()->get('MimeTypes');
173
        $mimeCategories = File::config()->get('app_categories');
174
        list($ext, $mime) = [strtolower($ext), strtolower($mime)];
175
176
        $notAuthoratative = !isset($httpMimeTypes[$ext]);                   // We've found ourselves a weird extension
177
        $notMatch = (!$notAuthoratative && $httpMimeTypes[$ext] !== $mime); // No match found for passed extension in our ext=>mime mapping from config
178
179
        if ($notAuthoratative || $notMatch) {
180
            if (!$fix) {
181
                return false;
182
            }
183
184
            // Attempt to "fix" broken or badly encoded file-extensions by guessing what it should be, based on $mime
185
            $coreExts = array_merge($mimeCategories['document'], $mimeCategories['image']);
186
            foreach ($coreExts as $coreExt) {
187
                // Make sure we check the correct category so we don't find a match for ms-excel in the image \File category (.cel) !!
188
                $isFile = in_array($coreExt, $mimeCategories['document']) && singleton(__CLASS__)->isOfFile($mime);     // dirty
189
                $isImge = in_array($coreExt, $mimeCategories['image']) && singleton(__CLASS__)->isOfImage($mime);   // more dirt
190
191
                if (($isFile || $isImge) && stristr($mime, $coreExt) !== false) {
192
                    // "Manually" force "jpg" as the file-suffix to be returned
193
                    return $coreExt == 'jpeg' ? 'jpg' : $coreExt;
194
                }
195
            }
196
197
            return false;
198
        }
199
200
        return false;
201
    }
202
203
    /**
204
     * Post-proces user-inputted mime-types. Allows space, comma or newline
205
     * delimited mime-types input into a TextareaField
206
     *
207
     * @param string $mimeTypes
208
     * @return array - returns an array of mimetypes
209
     */
210
    public static function get_mimetypes_from_text($mimeTypes)
211
    {
212
        $mimes = preg_split("#[\r\n\s,]+#", trim($mimeTypes));
213
        $_mimes = [];
214
        foreach ($mimes as $mimeType) {
215
            // clean 'em up a little
216
            $_mimes[] = self::cleanse($mimeType);
217
        }
218
        return $_mimes;
219
    }
220
221
    /**
222
     * Simple cleanup utility
223
     *
224
     * @param string $mimeType
225
     * @return string
226
     */
227
    public static function cleanse($mimeType): string
228
    {
229
        if (!$mimeType) {
230
            return '';
231
        }
232
233
        return strtolower(trim($mimeType));
234
    }
235
236
    /**
237
     * Takes an array of mime-type strings and simply returns true after the first Image-ish mime-type is found
238
     *
239
     * @param mixed $mimeTypes
240
     * @return boolean
241
     */
242
    public function isOfImage($mimeTypes): bool
243
    {
244
        if (!is_array($mimeTypes)) {
245
            $mimeTypes = [self::cleanse($mimeTypes)];
246
        }
247
248
        foreach ($mimeTypes as $mime) {
249
            $imgMime = self::get_mime_for_ss_type('image');
250
251
            if ($imgMime && in_array($mime, $imgMime)) {
252
                return true;
253
            }
254
        }
255
256
        return false;
257
    }
258
259
    /**
260
     * Takes an array of mime-type strings and simply returns true after the first File-ish mime-type is found
261
     *
262
     * @param mixed $mimeTypes
263
     * @return boolean
264
     */
265
    public function isOfFile($mimeTypes): bool
266
    {
267
        if (!is_array($mimeTypes)) {
268
            $mimeTypes = [self::cleanse($mimeTypes)];
269
        }
270
271
        foreach ($mimeTypes as $mime) {
272
            $fileMime = self::get_mime_for_ss_type('file');
273
274
            if ($fileMime && in_array($mime, $fileMime)) {
275
                return true;
276
            }
277
        }
278
279
        return false;
280
    }
281
282
    /**
283
     * Takes an array of mime-type strings and simply returns true after the first SiteTree-ish mime-type is found
284
     *
285
     * @param mixed $mimeTypes
286
     * @return boolean
287
     */
288
    public function isOfHtml($mimeTypes): bool
289
    {
290
        if (!is_array($mimeTypes)) {
291
            $mimeTypes = [self::cleanse($mimeTypes)];
292
        }
293
294
        foreach ($mimeTypes as $mime) {
295
            $htmlMime = self::get_mime_for_ss_type('sitetree');
296
297
            if ($htmlMime && in_array($mime, $htmlMime)) {
298
                return true;
299
            }
300
        }
301
302
        return false;
303
    }
304
305
    /**
306
     * Simple "shortcut" to isOfFile() and isOfImage()
307
     *
308
     * @param mixed $mimeTypes
309
     * @return boolean
310
     */
311
    public function isOfFileOrImage($mimeTypes): bool
312
    {
313
        if (!is_array($mimeTypes)) {
314
            $mimeTypes = [self::cleanse($mimeTypes)];
315
        }
316
317
        if ($this->isOfFile($mimeTypes) || $this->isOfImage($mimeTypes)) {
318
            return true;
319
        }
320
321
        return false;
322
    }
323
324
    /**
325
     * Ascertain passed $mime is not something we can do anything useful with
326
     *
327
     * @param string $mime
328
     * @return boolean
329
     */
330
    public function isBadMimeType($mime): bool
331
    {
332
        return (!$this->isOfFileOrImage($mime) && !$this->isOfHtml($mime));
333
    }
334
335
    /*
336
     *
337
     * Getters & Setters
338
     * -----------------
339
     *
340
     */
341
342
    /**
343
     *
344
     * @param array $mimes
345
     * @return void
346
     */
347
    public function setMimes($mimeTypes)
348
    {
349
        $this->mimeTypes = $mimeTypes;
350
    }
351
352
    /**
353
     *
354
     * @return array
355
     */
356
    public function getMimes()
357
    {
358
        return $this->mimeTypes;
359
    }
360
}
361