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