NullsafeTokenEmulator.php 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. <?php declare(strict_types=1);
  2. namespace PhpParser\Lexer\TokenEmulator;
  3. use PhpParser\PhpVersion;
  4. use PhpParser\Token;
  5. final class NullsafeTokenEmulator extends TokenEmulator {
  6. public function getPhpVersion(): PhpVersion {
  7. return PhpVersion::fromComponents(8, 0);
  8. }
  9. public function isEmulationNeeded(string $code): bool {
  10. return strpos($code, '?->') !== false;
  11. }
  12. public function emulate(string $code, array $tokens): array {
  13. // We need to manually iterate and manage a count because we'll change
  14. // the tokens array on the way
  15. for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
  16. $token = $tokens[$i];
  17. if ($token->text === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id === \T_OBJECT_OPERATOR) {
  18. array_splice($tokens, $i, 2, [
  19. new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos),
  20. ]);
  21. $c--;
  22. continue;
  23. }
  24. // Handle ?-> inside encapsed string.
  25. if ($token->id === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1])
  26. && $tokens[$i - 1]->id === \T_VARIABLE
  27. && preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $token->text, $matches)
  28. ) {
  29. $replacement = [
  30. new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos),
  31. new Token(\T_STRING, $matches[1], $token->line, $token->pos + 3),
  32. ];
  33. $matchLen = \strlen($matches[0]);
  34. if ($matchLen !== \strlen($token->text)) {
  35. $replacement[] = new Token(
  36. \T_ENCAPSED_AND_WHITESPACE,
  37. \substr($token->text, $matchLen),
  38. $token->line, $token->pos + $matchLen
  39. );
  40. }
  41. array_splice($tokens, $i, 1, $replacement);
  42. $c += \count($replacement) - 1;
  43. continue;
  44. }
  45. }
  46. return $tokens;
  47. }
  48. public function reverseEmulate(string $code, array $tokens): array {
  49. // ?-> was not valid code previously, don't bother.
  50. return $tokens;
  51. }
  52. }