Util   B
last analyzed

Coupling/Cohesion

Components 1
Dependencies 2

Complexity

Total Complexity 47

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 47
lcom 1
cbo 2
dl 0
loc 341
ccs 108
cts 108
cp 1
rs 8.439
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A dirname() 0 4 1
A map() 0 14 3
A pathinfo() 0 14 2
A normalizeDirname() 0 4 2
A normalizePath() 0 4 1
B normalizeRelativePath() 0 30 6
A removeFunkyWhiteSpace() 0 9 2
A normalizePrefix() 0 4 1
A contentSize() 0 4 2
A guessMimeType() 0 10 3
A emulateDirectories() 0 17 3
A ensureConfig() 0 16 4
A rewindStream() 0 6 3
A isSeekableStream() 0 6 1
A getStreamSize() 0 6 1
C emulateObjectDirectories() 0 25 7
B basename() 0 28 5

How to fix   Complexity   

Complex Class

Complex classes like Util 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Util, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace League\Flysystem;
4
5
use League\Flysystem\Util\MimeType;
6
use LogicException;
7
8
class Util
9
{
10
    /**
11
     * Get normalized pathinfo.
12
     *
13
     * @param string $path
14
     *
15
     * @return array pathinfo
16
     */
17 129
    public static function pathinfo($path)
18
    {
19 129
        $pathinfo = compact('path');
20
21 129
        if ('' !== $dirname = dirname($path)) {
22 126
            $pathinfo['dirname'] = static::normalizeDirname($dirname);
23 42
        }
24
25 129
        $pathinfo['basename'] = static::basename($path);
0 ignored issues
show
Bug introduced by Chris Leppanen
Since basename() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of basename() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
26
27 129
        $pathinfo += pathinfo($pathinfo['basename']);
28
29 129
        return $pathinfo;
30
    }
31
32
    /**
33
     * Normalize a dirname return value.
34
     *
35
     * @param string $dirname
36
     *
37
     * @return string normalized dirname
38
     */
39 150
    public static function normalizeDirname($dirname)
40
    {
41 150
        return $dirname === '.' ? '' : $dirname;
42
    }
43
44
    /**
45
     * Get a normalized dirname from a path.
46
     *
47
     * @param string $path
48
     *
49
     * @return string dirname
50
     */
51 33
    public static function dirname($path)
52
    {
53 33
        return static::normalizeDirname(dirname($path));
54
    }
55
56
    /**
57
     * Map result arrays.
58
     *
59
     * @param array $object
60
     * @param array $map
61
     *
62
     * @return array mapped result
63
     */
64 6
    public static function map(array $object, array $map)
65
    {
66 6
        $result = [];
67
68 6
        foreach ($map as $from => $to) {
69 6
            if ( ! isset($object[$from])) {
70 3
                continue;
71
            }
72
73 6
            $result[$to] = $object[$from];
74 2
        }
75
76 6
        return $result;
77
    }
78
79
    /**
80
     * Normalize path.
81
     *
82
     * @param string $path
83
     *
84
     * @throws LogicException
85
     *
86
     * @return string
87
     */
88 285
    public static function normalizePath($path)
89
    {
90 285
        return static::normalizeRelativePath($path);
91
    }
92
93
    /**
94
     * Normalize relative directories in a path.
95
     *
96
     * @param string $path
97
     *
98
     * @throws LogicException
99
     *
100
     * @return string
101
     */
102 285
    public static function normalizeRelativePath($path)
103
    {
104 285
        $path = str_replace('\\', '/', $path);
105 285
        $path = static::removeFunkyWhiteSpace($path);
106
107 285
        $parts = [];
108
109 285
        foreach (explode('/', $path) as $part) {
110
            switch ($part) {
111 285
                case '':
112 281
                case '.':
113 66
                break;
114
115 276
            case '..':
116 30
                if (empty($parts)) {
117 15
                    throw new LogicException(
118 15
                        'Path is outside of the defined root, path: [' . $path . ']'
119 5
                    );
120
                }
121 27
                array_pop($parts);
122 27
                break;
123
124 91
            default:
125 273
                $parts[] = $part;
126 279
                break;
127 91
            }
128 94
        }
129
130 270
        return implode('/', $parts);
131
    }
132
133
    /**
134
     * Removes unprintable characters and invalid unicode characters.
135
     *
136
     * @param string $path
137
     *
138
     * @return string $path
139
     */
140 285
    protected static function removeFunkyWhiteSpace($path) {
141
        // We do this check in a loop, since removing invalid unicode characters
142
        // can lead to new characters being created.
143 285
        while (preg_match('#\p{C}+|^\./#u', $path)) {
144 6
            $path = preg_replace('#\p{C}+|^\./#u', '', $path);
145 2
        }
146
147 285
        return $path;
148
    }
149
150
    /**
151
     * Normalize prefix.
152
     *
153
     * @param string $prefix
154
     * @param string $separator
155
     *
156
     * @return string normalized path
157
     */
158 3
    public static function normalizePrefix($prefix, $separator)
159
    {
160 3
        return rtrim($prefix, $separator) . $separator;
161
    }
162
163
    /**
164
     * Get content size.
165
     *
166
     * @param string $contents
167
     *
168
     * @return int content size
169
     */
170 3
    public static function contentSize($contents)
171
    {
172 3
        return defined('MB_OVERLOAD_STRING') ? mb_strlen($contents, '8bit') : strlen($contents);
173
    }
174
175
    /**
176
     * Guess MIME Type based on the path of the file and it's content.
177
     *
178
     * @param string $path
179
     * @param string|resource $content
180
     *
181
     * @return string|null MIME Type or NULL if no extension detected
182
     */
183 27
    public static function guessMimeType($path, $content)
184
    {
185 27
        $mimeType = MimeType::detectByContent($content);
186
187 27
        if ( ! (empty($mimeType) || in_array($mimeType, ['application/x-empty', 'text/plain', 'text/x-asm']))) {
188 3
            return $mimeType;
189
        }
190
191 24
        return MimeType::detectByFilename($path);
192
    }
193
194
    /**
195
     * Emulate directories.
196
     *
197
     * @param array $listing
198
     *
199
     * @return array listing with emulated directories
200
     */
201 3
    public static function emulateDirectories(array $listing)
202
    {
203 3
        $directories = [];
204 3
        $listedDirectories = [];
205
206 3
        foreach ($listing as $object) {
207 3
            list($directories, $listedDirectories) = static::emulateObjectDirectories($object, $directories, $listedDirectories);
208 1
        }
209
210 3
        $directories = array_diff(array_unique($directories), array_unique($listedDirectories));
211
212 3
        foreach ($directories as $directory) {
213 3
            $listing[] = static::pathinfo($directory) + ['type' => 'dir'];
214 1
        }
215
216 3
        return $listing;
217
    }
218
219
    /**
220
     * Ensure a Config instance.
221
     *
222
     * @param null|array|Config $config
223
     *
224
     * @return Config config instance
225
     *
226
     * @throw  LogicException
227
     */
228 147
    public static function ensureConfig($config)
229
    {
230 147
        if ($config === null) {
231 3
            return new Config();
232
        }
233
234 147
        if ($config instanceof Config) {
235 144
            return $config;
236
        }
237
238 6
        if (is_array($config)) {
239 3
            return new Config($config);
240
        }
241
242 3
        throw new LogicException('A config should either be an array or a Flysystem\Config object.');
243
    }
244
245
    /**
246
     * Rewind a stream.
247
     *
248
     * @param resource $resource
249
     */
250 39
    public static function rewindStream($resource)
251
    {
252 39
        if (ftell($resource) !== 0 && static::isSeekableStream($resource)) {
253 21
            rewind($resource);
254 7
        }
255 39
    }
256
257 21
    public static function isSeekableStream($resource)
258
    {
259 21
        $metadata = stream_get_meta_data($resource);
260
261 21
        return $metadata['seekable'];
262
    }
263
264
    /**
265
     * Get the size of a stream.
266
     *
267
     * @param resource $resource
268
     *
269
     * @return int stream size
270
     */
271 3
    public static function getStreamSize($resource)
272
    {
273 3
        $stat = fstat($resource);
274
275 3
        return $stat['size'];
276
    }
277
278
    /**
279
     * Emulate the directories of a single object.
280
     *
281
     * @param array $object
282
     * @param array $directories
283
     * @param array $listedDirectories
284
     *
285
     * @return array
286
     */
287 3
    protected static function emulateObjectDirectories(array $object, array $directories, array $listedDirectories)
288
    {
289 3
        if ($object['type'] === 'dir') {
290 3
            $listedDirectories[] = $object['path'];
291 1
        }
292
293 3
        if (empty($object['dirname'])) {
294 3
            return [$directories, $listedDirectories];
295
        }
296
297 3
        $parent = $object['dirname'];
298
299 3
        while ( ! empty($parent) && ! in_array($parent, $directories)) {
300 3
            $directories[] = $parent;
301 3
            $parent = static::dirname($parent);
302 1
        }
303
304 3
        if (isset($object['type']) && $object['type'] === 'dir') {
305 3
            $listedDirectories[] = $object['path'];
306
307 3
            return [$directories, $listedDirectories];
308
        }
309
310 3
        return [$directories, $listedDirectories];
311
    }
312
313
    /**
314
     * Returns the trailing name component of the path.
315
     *
316
     * @param string $path
317
     *
318
     * @return string
319
     */
320 129
    private static function basename($path)
321
    {
322 129
        $separators = DIRECTORY_SEPARATOR === '/' ? '/' : '\/';
323
324 129
        $path = rtrim($path, $separators);
325
326 129
        $basename = preg_replace('#.*?([^' . preg_quote($separators, '#') . ']+$)#', '$1', $path);
327
328 129
        if (DIRECTORY_SEPARATOR === '/') {
329 129
            return $basename;
330
        }
331
        // @codeCoverageIgnoreStart
332
        // Extra Windows path munging. This is tested via AppVeyor, but code
333
        // coverage is not reported.
334
335
        // Handle relative paths with drive letters. c:file.txt.
336
        while (preg_match('#^[a-zA-Z]{1}:[^\\\/]#', $basename)) {
337
            $basename = substr($basename, 2);
338
        }
339
340
        // Remove colon for standalone drive letter names.
341
        if (preg_match('#^[a-zA-Z]{1}:$#', $basename)) {
342
            $basename = rtrim($basename, ':');
343
        }
344
345
        return $basename;
346
        // @codeCoverageIgnoreEnd
347
    }
348
}
349