Passed
Pull Request — master (#45)
by Evgeniy
01:57
created

UseStatementParser::isTokenIsPartOfUse()   B

Complexity

Conditions 9
Paths 24

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
eloc 7
c 0
b 0
f 0
nc 24
nop 1
dl 0
loc 11
ccs 8
cts 8
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 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 56
    public function fromFile(string $file): array
35
    {
36 56
        if (!is_file($file)) {
37 2
            throw new RuntimeException("File \"{$file}\" does not exist.");
38
        }
39
40 54
        if (!is_readable($file)) {
41 1
            throw new RuntimeException("File \"{$file}\" is not readable.");
42
        }
43
44 53
        $fileContent = file_get_contents($file);
45
46 53
        if ($fileContent === false) {
47
            throw new RuntimeException("Failed to read file \"{$file}\".");
48
        }
49
50 53
        $tokens = token_get_all($fileContent);
51 53
        array_shift($tokens);
52 53
        $uses = [];
53
54 53
        foreach ($tokens as $i => $token) {
55 53
            if (!is_array($token)) {
56 53
                continue;
57
            }
58
59 53
            if ($token[0] === T_USE && isset($tokens[$i + 2]) && $this->isTokenIsPartOfUse($tokens[$i + 2])) {
60 53
                $uses = $uses + $this->normalize(array_slice($tokens, $i + 1));
61 53
                continue;
62
            }
63
        }
64
65 53
        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 53
    public function isTokenIsPartOfUse($token): bool
76
    {
77 53
        if (!is_array($token)) {
78 53
            return false;
79
        }
80
81 53
        return $token[0] === T_STRING
82 53
            || $token[0] === T_NS_SEPARATOR
83 53
            || (defined('T_NAME_QUALIFIED') && $token[0] === T_NAME_QUALIFIED)
84 53
            || (defined('T_NAME_FULLY_QUALIFIED') && $token[0] === T_NAME_FULLY_QUALIFIED)
85 53
            || (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 53
    private function normalize(array $tokens): array
96
    {
97 53
        $commonNamespace = '\\';
98 53
        $current = '';
99 53
        $uses = [];
100
101 53
        foreach ($tokens as $token) {
102 53
            if ($this->isTokenIsPartOfUse($token)) {
103 53
                $current .= $token[1];
104 53
                continue;
105
            }
106 53
            if ($token === ',' || $token === ';') {
107 53
                if ($current !== '') {
108 53
                    $uses[] = $commonNamespace . $current;
109 53
                    $current = '';
110
                }
111
            }
112 53
            if ($token === ';') {
113 53
                break;
114
            }
115 53
            if ($token === '{') {
116 1
                $commonNamespace .= $current;
117 1
                $current = '';
118 1
                continue;
119
            }
120 53
            if ($token[0] === T_AS) {
121 43
                $current .= '@';
122
            }
123
        }
124
125 53
        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 53
    private function replaceAliases(array $uses): array
136
    {
137 53
        $result = [];
138
139 53
        foreach ($uses as $use) {
140 53
            $delimiterPosition = strpos($use, '@');
141
142 53
            if ($delimiterPosition !== false) {
143 43
                $alias = mb_substr($use, $delimiterPosition + 1);
144 43
                $result[$alias] = mb_substr($use, 0, $delimiterPosition);
145 43
                continue;
146
            }
147
148 51
            $part = strrchr($use, '\\');
149 51
            $result[$part === false ? $use : substr($part, 1)] = $use;
150
        }
151
152 53
        return $result;
153
    }
154
}
155