Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
6 / 6 |
CRAP | |
100.00% |
52 / 52 |
PHP8 | |
100.00% |
1 / 1 |
|
100.00% |
6 / 6 |
16 | |
100.00% |
52 / 52 |
getTokens | |
100.00% |
1 / 1 |
1 | |
100.00% |
7 / 7 |
|||
getFileTokens | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
organizeTokens | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
processObjectToken | |
100.00% |
1 / 1 |
6 | |
100.00% |
22 / 22 |
|||
processStringToken | |
100.00% |
1 / 1 |
1 | |
100.00% |
7 / 7 |
|||
findCorrectLine | |
100.00% |
1 / 1 |
4 | |
100.00% |
7 / 7 |
1 | <?php declare(strict_types=1); |
2 | |
3 | namespace Aviat\Kilo\Tokens; |
4 | |
5 | use PhpToken; |
6 | |
7 | use function Aviat\Kilo\str_has; |
8 | use function Aviat\Kilo\tabs_to_spaces; |
9 | use const Aviat\Kilo\T_RAW; |
10 | |
11 | class PHP8 extends PhpToken { |
12 | private array $rawLines = []; |
13 | |
14 | private array $tokens = []; |
15 | |
16 | /** |
17 | * Use 'token_get_all' to get the tokens for a file, |
18 | * organized by row number |
19 | * |
20 | * @param string $code |
21 | * @return array |
22 | */ |
23 | public static function getTokens(string $code): array |
24 | { |
25 | // Make the lines/tokens 1-indexed |
26 | $lines = explode("\n", $code); |
27 | array_unshift($lines, ''); |
28 | unset($lines[0]); |
29 | |
30 | $self = (new self(0, $code)); |
31 | |
32 | $self->rawLines = $lines; |
33 | $self->tokens = array_fill(1, count($lines), []); |
34 | |
35 | return $self->organizeTokens($code); |
36 | } |
37 | |
38 | /** |
39 | * Return tokens for the current $filename, organized |
40 | * by row number |
41 | * |
42 | * @param string $filename |
43 | * @return array |
44 | */ |
45 | public static function getFileTokens(string $filename): array |
46 | { |
47 | $code = @file_get_contents($filename); |
48 | |
49 | if ($code === FALSE) |
50 | { |
51 | return []; |
52 | } |
53 | |
54 | return self::getTokens($code); |
55 | } |
56 | |
57 | protected function organizeTokens(string $code): array |
58 | { |
59 | $rawTokens = self::tokenize($code); |
60 | foreach ($rawTokens as $t) |
61 | { |
62 | $this->processObjectToken($t); |
63 | } |
64 | |
65 | ksort($this->tokens); |
66 | |
67 | return $this->tokens; |
68 | } |
69 | |
70 | protected function processObjectToken(\PhpToken $token): void |
71 | { |
72 | $currentLine = $token->line; |
73 | $char = tabs_to_spaces($token->text); |
74 | |
75 | $current = [ |
76 | 'type' => $token->id, |
77 | 'typeName' => $token->getTokenName(), |
78 | 'char' => $char, |
79 | 'line' => $currentLine, |
80 | ]; |
81 | |
82 | // Single new line, or starts with a new line with other whitespace |
83 | if (str_starts_with($char, "\n") && trim($char) === '') |
84 | { |
85 | $this->tokens[$currentLine][] = $current; |
86 | |
87 | return; |
88 | } |
89 | |
90 | // Only return the first line of a multi-line token for this line array |
91 | if (str_has($char, "\n")) |
92 | { |
93 | $chars = explode("\n", $char); |
94 | $current['original'] = [ |
95 | 'string' => $char, |
96 | 'lines' => $chars, |
97 | ]; |
98 | $current['char'] = array_shift($chars); |
99 | |
100 | // Add new lines for additional newline characters |
101 | $nextLine = $currentLine; |
102 | foreach ($chars as $char) |
103 | { |
104 | $nextLine++; |
105 | |
106 | if ( ! empty($char)) |
107 | { |
108 | $this->processStringToken($char, $nextLine); |
109 | } |
110 | } |
111 | } |
112 | |
113 | $this->tokens[$currentLine][] = $current; |
114 | } |
115 | |
116 | protected function processStringToken(string $token, int $startLine): void |
117 | { |
118 | $char = tabs_to_spaces($token); |
119 | $lineNumber = $this->findCorrectLine($char, $startLine) ?? $startLine; |
120 | |
121 | // Simple characters, usually delimiters or single character operators |
122 | $this->tokens[$lineNumber][] = [ |
123 | 'type' => T_RAW, |
124 | 'typeName' => 'RAW', |
125 | 'char' => tabs_to_spaces($token), |
126 | 'line' => $lineNumber, |
127 | ]; |
128 | } |
129 | |
130 | private function findCorrectLine(string $search, int $rowOffset, int $searchLength = 5): ?int |
131 | { |
132 | $end = $rowOffset + $searchLength; |
133 | if ($end > count($this->rawLines)) |
134 | { |
135 | $end = count($this->rawLines); |
136 | } |
137 | |
138 | for ($i = $rowOffset; $i < $end; $i++) |
139 | { |
140 | if (str_has($this->rawLines[$i], $search)) |
141 | { |
142 | return $i; |
143 | } |
144 | } |
145 | |
146 | return NULL; |
147 | } |
148 | } |