diff --git a/packages/view/src/Parser/TempestViewLexer.php b/packages/view/src/Parser/TempestViewLexer.php index 9b38a66c3..f64ae849f 100644 --- a/packages/view/src/Parser/TempestViewLexer.php +++ b/packages/view/src/Parser/TempestViewLexer.php @@ -4,7 +4,7 @@ final class TempestViewLexer { - private const string WHITESPACE = PHP_EOL . "\n\t\f "; + private const string WHITESPACE = "\r\n\t\f "; private int $position = 0; @@ -35,7 +35,7 @@ public function lex(): TokenCollection $tokens = [...$tokens, ...$this->lexCharacterData()]; } elseif ($this->comesNext('<')) { $tokens = [...$tokens, ...$this->lexTag()]; - } elseif ($this->comesNext(' ') || $this->comesNext(PHP_EOL)) { + } elseif ($this->isWhitespace($this->current)) { $tokens[] = $this->lexWhitespace(); } else { $tokens[] = $this->lexContent(); @@ -61,6 +61,15 @@ private function seek(int $length = 1, int $offset = 0): ?string return $seek; } + private function isWhitespace(?string $value): bool + { + if ($value === null) { + return false; + } + + return str_contains(self::WHITESPACE, $value); + } + private function seekIgnoringWhitespace(int $length = 1): ?string { $offset = strspn($this->html, self::WHITESPACE, $this->position); @@ -220,13 +229,7 @@ private function lexWhitespace(): Token { $buffer = ''; - while ($this->current !== null) { - $seek = $this->seek(); - - if ($seek !== ' ' && $seek !== PHP_EOL) { - break; - } - + while ($this->isWhitespace($this->seek())) { $buffer .= $this->consume(); } diff --git a/packages/view/tests/TempestViewLexerTest.php b/packages/view/tests/TempestViewLexerTest.php index 0ba62e288..483dfe007 100644 --- a/packages/view/tests/TempestViewLexerTest.php +++ b/packages/view/tests/TempestViewLexerTest.php @@ -2,6 +2,7 @@ namespace Tempest\View\Tests; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Tempest\View\Parser\TempestViewLexer; @@ -316,6 +317,22 @@ public function test_attribute_with_new_line(): void ); } + #[Test] + public function lexer_handles_crlf_attribute_boundaries(): void + { + $tokens = new TempestViewLexer("
")->lex(); + + $this->assertTokens( + expected: [ + new Token('", TokenType::OPEN_TAG_END), + new Token('', TokenType::CLOSING_TAG), + ], + actual: $tokens, + ); + } + public function test_unclosed_php_tag(): void { $tokens = new TempestViewLexer('lex();