UseStatementParser   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Test Coverage

Coverage 98.28%

Importance

Changes 8
Bugs 2 Features 0
Metric Value
eloc 54
c 8
b 2
f 0
dl 0
loc 130
ccs 57
cts 58
cp 0.9828
rs 9.92
wmc 31

4 Methods

Rating   Name   Duplication   Size   Complexity  
A replaceAliases() 0 18 4
B isTokenIsPartOfUse() 0 11 9
B fromFile() 0 32 9
B normalize() 0 31 9
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 defined;
11
use function file_get_contents;
12
use function is_array;
13
use function is_file;
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
     * Returns a set of `use` statements from the code of the specified file.
27
     *
28
     * @param string $file File to read.
29
     *
30
     * @throws RuntimeException if there is a problem reading file.
31
     *
32
     * @return array<string, string> Use statements data.
33
     */
34 73
    public function fromFile(string $file): array
35
    {
36 73
        if (!is_file($file)) {
37 2
            throw new RuntimeException("File \"{$file}\" does not exist.");
38
        }
39
40 71
        if (!is_readable($file)) {
41 1
            throw new RuntimeException("File \"{$file}\" is not readable.");
42
        }
43
44 70
        $fileContent = file_get_contents($file);
45
46 70
        if ($fileContent === false) {
47
            throw new RuntimeException("Failed to read file \"{$file}\".");
48
        }
49
50 70
        $tokens = token_get_all($fileContent);
51 70
        array_shift($tokens);
52 70
        $uses = [];
53
54 70
        foreach ($tokens as $i => $token) {
55 70
            if (!is_array($token)) {
56 70
                continue;
57
            }
58
59 70
            if ($token[0] === T_USE && isset($tokens[$i + 2]) && $this->isTokenIsPartOfUse($tokens[$i + 2])) {
60 70
                $uses += $this->normalize(array_slice($tokens, $i + 1));
61 70
                continue;
62
            }
63
        }
64
65 70
        return $uses;
66
    }
67
68
    /**
69
     * Checks whether the token is part of the use statement data.
70
     *
71
     * @param array|string $token PHP token.
72
     *
73
     * @return bool Whether the token is part of the use statement data.
74
     */
75 70
    public function isTokenIsPartOfUse($token): bool
76
    {
77 70
        if (!is_array($token)) {
78 70
            return false;
79
        }
80
81 70
        return $token[0] === T_STRING
82 70
            || $token[0] === T_NS_SEPARATOR
83 70
            || (defined('T_NAME_QUALIFIED') && $token[0] === T_NAME_QUALIFIED)
84 70
            || (defined('T_NAME_FULLY_QUALIFIED') && $token[0] === T_NAME_FULLY_QUALIFIED)
85 70
            || (defined('T_NAME_RELATIVE') && $token[0] === T_NAME_RELATIVE);
86
    }
87
88
    /**
89
     * Normalizes raw tokens into uniform use statement data.
90
     *
91
     * @param array<int, array<int, int|string>|string> $tokens Raw tokens.
92
     *
93
     * @return array<string, string> Normalized use statement data.
94
     */
95 70
    private function normalize(array $tokens): array
96
    {
97 70
        $commonNamespace = '\\';
98 70
        $current = '';
99 70
        $uses = [];
100
101 70
        foreach ($tokens as $token) {
102 70
            if ($this->isTokenIsPartOfUse($token)) {
103 70
                $current .= $token[1];
104 70
                continue;
105
            }
106 70
            if ($token === ',' || $token === ';') {
107 70
                if ($current !== '') {
108 70
                    $uses[] = $commonNamespace . $current;
109 70
                    $current = '';
110
                }
111
            }
112 70
            if ($token === ';') {
113 70
                break;
114
            }
115 70
            if ($token === '{') {
116 1
                $commonNamespace .= $current;
117 1
                $current = '';
118 1
                continue;
119
            }
120 70
            if ($token[0] === T_AS) {
121 63
                $current .= '@';
122
            }
123
        }
124
125 70
        return $this->replaceAliases($uses);
126
    }
127
128
    /**
129
     * Replaces aliases for the use statement data.
130
     *
131
     * @param string[] $uses Raw uses.
132
     *
133
     * @return array<string, string> Use statement data with the replaced aliases.
134
     */
135 70
    private function replaceAliases(array $uses): array
136
    {
137 70
        $result = [];
138
139 70
        foreach ($uses as $use) {
140 70
            $delimiterPosition = strpos($use, '@');
141
142 70
            if ($delimiterPosition !== false) {
143 63
                $alias = mb_substr($use, $delimiterPosition + 1);
144 63
                $result[$alias] = mb_substr($use, 0, $delimiterPosition);
145 63
                continue;
146
            }
147
148 68
            $part = strrchr($use, '\\');
149 68
            $result[$part === false ? $use : substr($part, 1)] = $use;
150
        }
151
152 70
        return $result;
153
    }
154
}
155