Passed
Push — master ( c5eccb...bdfd32 )
by Alexander
04:40 queued 02:23
created

UseStatementParser::fromFile()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7.2269

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 17
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 32
ccs 15
cts 18
cp 0.8333
crap 7.2269
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\VarDumper;
6
7
use RuntimeException;
8
9
use function defined;
10
11
/**
12
 * UseStatementParser given a PHP file, returns a set of `use` statements from the code.
13
 */
14
final class UseStatementParser
15
{
16
    /**
17
     * @param string $file File to read.
18
     *
19
     * @throws RuntimeException if there is a problem reading file.
20
     *
21
     * @return array Use statements data.
22
     * @psalm-return array<string, string>
23
     */
24 40
    public function fromFile(string $file): array
25
    {
26 40
        if (!file_exists($file)) {
27 1
            throw new RuntimeException('File "' . $file . '" does not exist.');
28
        }
29
30 39
        if (!is_readable($file)) {
31
            throw new RuntimeException('File "' . $file . '" is not readable.');
32
        }
33
34 39
        $fileContent = file_get_contents($file);
35
36 39
        if ($fileContent === false) {
37
            throw new RuntimeException('Failed to read file "' . $file . '".');
38
        }
39
40 39
        $tokens = token_get_all($fileContent);
41 39
        array_shift($tokens);
42
43 39
        $uses = [];
44 39
        foreach ($tokens as $i => $token) {
45 39
            if (!isset($token[0])) {
46
                continue;
47
            }
48
49 39
            if ($token[0] === T_USE) {
50 39
                $uses = array_merge($uses, $this->normalizeUse(array_slice($tokens, $i + 1)));
51 39
                continue;
52
            }
53
        }
54
55 39
        return $uses;
56
    }
57
58
    /**
59
     * Normalizes raw tokens into uniform use statement data.
60
     *
61
     * @param array $tokens Raw tokens.
62
     *
63
     * @return array Normalized use statement data.
64
     * @psalm-return array<string, string>
65
     */
66 39
    private function normalizeUse(array $tokens): array
67
    {
68 39
        $commonNamespace = '\\';
69 39
        $current = '';
70 39
        $uses = [];
71
72
        /** @psalm-var array<int, int|string>|string $token */
73 39
        foreach ($tokens as $token) {
74 39
            if (!isset($token[0])) {
75
                continue;
76
            }
77
            if (
78 39
                $token[0] === T_STRING
79 39
                || $token[0] === T_NS_SEPARATOR
80 39
                || (defined('T_NAME_QUALIFIED') && $token[0] === T_NAME_QUALIFIED)
81 39
                || (defined('T_NAME_FULLY_QUALIFIED') && $token[0] === T_NAME_FULLY_QUALIFIED)
82 39
                || (defined('T_NAME_RELATIVE') && $token[0] === T_NAME_RELATIVE)
83
            ) {
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 39
        foreach ($uses as $use) {
119 39
            $delimiterPosition = strpos($use, '@');
120 39
            if ($delimiterPosition !== false) {
121 31
                $alias = mb_substr($use, $delimiterPosition + 1);
122 31
                $result[$alias] = mb_substr($use, 0, $delimiterPosition);
123
            } else {
124 37
                $part = strrchr($use, '\\');
125 37
                $result[$part === false ? $use : substr($part, 1)] = $use;
126
            }
127
        }
128
129 39
        return $result;
130
    }
131
}
132