1<?php declare(strict_types=1); 2 3namespace PhpParser\Lexer\TokenEmulator; 4 5use PhpParser\Token; 6 7abstract 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