Util   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 342
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

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

17 Methods

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