1<?php declare(strict_types=1); 2 3namespace PhpParser\Lexer\TokenEmulator; 4 5use PhpParser\Lexer\Emulative; 6 7final class FlexibleDocStringEmulator extends TokenEmulator 8{ 9 const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX' 10/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n 11(?:.*\r?\n)*? 12(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x 13REGEX; 14 15 public function getPhpVersion(): string 16 { 17 return Emulative::PHP_7_3; 18 } 19 20 public function isEmulationNeeded(string $code) : bool 21 { 22 return strpos($code, '<<<') !== false; 23 } 24 25 public function emulate(string $code, array $tokens): array 26 { 27 // Handled by preprocessing + fixup. 28 return $tokens; 29 } 30 31 public function reverseEmulate(string $code, array $tokens): array 32 { 33 // Not supported. 34 return $tokens; 35 } 36 37 public function preprocessCode(string $code, array &$patches): string { 38 if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) { 39 // No heredoc/nowdoc found 40 return $code; 41 } 42 43 // Keep track of how much we need to adjust string offsets due to the modifications we 44 // already made 45 $posDelta = 0; 46 foreach ($matches as $match) { 47 $indentation = $match['indentation'][0]; 48 $indentationStart = $match['indentation'][1]; 49 50 $separator = $match['separator'][0]; 51 $separatorStart = $match['separator'][1]; 52 53 if ($indentation === '' && $separator !== '') { 54 // Ordinary heredoc/nowdoc 55 continue; 56 } 57 58 if ($indentation !== '') { 59 // Remove indentation 60 $indentationLen = strlen($indentation); 61 $code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen); 62 $patches[] = [$indentationStart + $posDelta, 'add', $indentation]; 63 $posDelta -= $indentationLen; 64 } 65 66 if ($separator === '') { 67 // Insert newline as separator 68 $code = substr_replace($code, "\n", $separatorStart + $posDelta, 0); 69 $patches[] = [$separatorStart + $posDelta, 'remove', "\n"]; 70 $posDelta += 1; 71 } 72 } 73 74 return $code; 75 } 76} 77