Passed
Push — master ( 896efe...707faa )
by Alexander
02:16
created

UseStatementParser::normalize()   B

Complexity

Conditions 9
Paths 8

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 9

Importance

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