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->getPreviousNonIgnorableToken($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 getPreviousNonIgnorableToken(array $tokens, int $start): ?Token {
39: for ($i = $start - 1; $i >= 0; --$i) {
40: $token = $tokens[$i];
41: if ($token->id === T_WHITESPACE || $token->id == T_COMMENT || $token->id === T_DOC_COMMENT) {
42: continue;
43: }
44:
45: return $token;
46: }
47:
48: return null;
49: }
50:
51: public function reverseEmulate(string $code, array $tokens): array {
52: $keywordToken = $this->getKeywordToken();
53: foreach ($tokens as $token) {
54: if ($token->id === $keywordToken) {
55: $token->id = \T_STRING;
56: }
57: }
58:
59: return $tokens;
60: }
61: }
62: