Passed
Pull Request — master (#44)
by Evgeniy
03:23 queued 01:01
created

UseStatementParser::normalizeUse()   C

Complexity

Conditions 17
Paths 9

Size

Total Lines 41
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 17.0146

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 17
eloc 27
c 5
b 0
f 0
nc 9
nop 1
dl 0
loc 41
ccs 26
cts 27
cp 0.963
crap 17.0146
rs 5.2166

1 Method

Rating   Name   Duplication   Size   Complexity  
B UseStatementParser::normalize() 0 32 9

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\VarDumper;
6
7
use RuntimeException;
8
9
use function array_merge;
10
use function array_slice;
11
use function file_exists;
12
use function file_get_contents;
13
use function is_array;
14
use function is_readable;
15
use function mb_substr;
16
use function strpos;
17
use function strrchr;
18
use function substr;
19
20
/**
21
 * UseStatementParser given a PHP file, returns a set of `use` statements from the code.
22
 */
23
final class UseStatementParser
24
{
25
    /**
26
     * @param string $file File to read.
27
     *
28
     * @throws RuntimeException if there is a problem reading file.
29
     *
30
     * @return array Use statements data.
31
     * @psalm-return array<string, string>
32
     */
33 40
    public function fromFile(string $file): array
34
    {
35 40
        if (!file_exists($file)) {
36 1
            throw new RuntimeException('File "' . $file . '" does not exist.');
37
        }
38
39 39
        if (!is_readable($file)) {
40
            throw new RuntimeException('File "' . $file . '" is not readable.');
41
        }
42
43 39
        $fileContent = file_get_contents($file);
44
45 39
        if ($fileContent === false) {
46
            throw new RuntimeException('Failed to read file "' . $file . '".');
47
        }
48
49 39
        $tokens = token_get_all($fileContent);
50 39
        array_shift($tokens);
51 39
        $uses = [];
52
53 39
        foreach ($tokens as $i => $token) {
54 39
            if (!is_array($token)) {
55 39
                continue;
56
            }
57
58 39
            if ($token[0] === T_USE && isset($tokens[$i + 2]) && TokenHelper::isPartOfNamespace($tokens[$i + 2])) {
59 39
                $uses = array_merge($uses, $this->normalize(array_slice($tokens, $i + 1)));
60 39
                continue;
61
            }
62
        }
63
64 39
        return $uses;
65
    }
66
67
    /**
68
     * Normalizes raw tokens into uniform use statement data.
69
     *
70
     * @param array $tokens Raw tokens.
71
     *
72
     * @return array Normalized use statement data.
73
     * @psalm-return array<string, string>
74
     */
75 39
    private function normalize(array $tokens): array
76
    {
77 39
        $commonNamespace = '\\';
78 39
        $current = '';
79 39
        $uses = [];
80
81
        /** @psalm-var array<int, int|string>|string $token */
82 39
        foreach ($tokens as $token) {
83 39
            if (TokenHelper::isPartOfNamespace($token)) {
84 39
                $current .= $token[1];
85 39
                continue;
86
            }
87 39
            if ($token === ',' || $token === ';') {
88 39
                if ($current !== '') {
89 39
                    $uses[] = $commonNamespace . $current;
90 39
                    $current = '';
91
                }
92
            }
93 39
            if ($token === ';') {
94 39
                break;
95
            }
96 39
            if ($token === '{') {
97 1
                $commonNamespace .= $current;
98 1
                $current = '';
99 1
                continue;
100
            }
101 39
            if ($token[0] === T_AS) {
102 31
                $current .= '@';
103
            }
104
        }
105
106 39
        return $this->replaceAliases($uses);
107
    }
108
109
    /**
110
     * @param array $uses
111
     * @psalm-param list<string> $uses
112
     *
113
     * @return array<string, string>
114
     */
115 39
    private function replaceAliases(array $uses): array
116
    {
117 39
        $result = [];
118
119 39
        foreach ($uses as $use) {
120 39
            $delimiterPosition = strpos($use, '@');
121
122 39
            if ($delimiterPosition !== false) {
123 31
                $alias = mb_substr($use, $delimiterPosition + 1);
124 31
                $result[$alias] = mb_substr($use, 0, $delimiterPosition);
125 31
                continue;
126
            }
127
128 37
            $part = strrchr($use, '\\');
129 37
            $result[$part === false ? $use : substr($part, 1)] = $use;
130
        }
131
132 39
        return $result;
133
    }
134
}
135