Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
52 / 52
PHP8
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
6 / 6
16
100.00% covered (success)
100.00%
52 / 52
 getTokens
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 getFileTokens
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
 organizeTokens
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 processObjectToken
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
22 / 22
 processStringToken
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 findCorrectLine
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
7 / 7
1<?php declare(strict_types=1);
2
3namespace Aviat\Kilo\Tokens;
4
5use PhpToken;
6
7use function Aviat\Kilo\str_has;
8use function Aviat\Kilo\tabs_to_spaces;
9use const Aviat\Kilo\T_RAW;
10
11class 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}