1<?php declare(strict_types=1);
2
3namespace PhpParser\Lexer\TokenEmulator;
4
5abstract class KeywordEmulator extends TokenEmulator
6{
7    abstract function getKeywordString(): string;
8    abstract function getKeywordToken(): int;
9
10    public function isEmulationNeeded(string $code): bool
11    {
12        return strpos(strtolower($code), $this->getKeywordString()) !== false;
13    }
14
15    protected function isKeywordContext(array $tokens, int $pos): bool
16    {
17        $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos);
18        return $previousNonSpaceToken === null || $previousNonSpaceToken[0] !== \T_OBJECT_OPERATOR;
19    }
20
21    public function emulate(string $code, array $tokens): array
22    {
23        $keywordString = $this->getKeywordString();
24        foreach ($tokens as $i => $token) {
25            if ($token[0] === T_STRING && strtolower($token[1]) === $keywordString
26                    && $this->isKeywordContext($tokens, $i)) {
27                $tokens[$i][0] = $this->getKeywordToken();
28            }
29        }
30
31        return $tokens;
32    }
33
34    /**
35     * @param mixed[] $tokens
36     * @return mixed[]|null
37     */
38    private function getPreviousNonSpaceToken(array $tokens, int $start)
39    {
40        for ($i = $start - 1; $i >= 0; --$i) {
41            if ($tokens[$i][0] === T_WHITESPACE) {
42                continue;
43            }
44
45            return $tokens[$i];
46        }
47
48        return null;
49    }
50
51    public function reverseEmulate(string $code, array $tokens): array
52    {
53        $keywordToken = $this->getKeywordToken();
54        foreach ($tokens as $i => $token) {
55            if ($token[0] === $keywordToken) {
56                $tokens[$i][0] = \T_STRING;
57            }
58        }
59
60        return $tokens;
61    }
62}
63