diff --git a/.idea/php.xml b/.idea/php.xml index b1dd65b..1f01db2 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -111,7 +111,7 @@ - + @@ -169,7 +169,7 @@ - + /etc/php/8.2/cli/conf.d/10-opcache.ini, /etc/php/8.2/cli/conf.d/10-pdo.ini, /etc/php/8.2/cli/conf.d/15-xml.ini, /etc/php/8.2/cli/conf.d/20-apcu.ini, /etc/php/8.2/cli/conf.d/20-calendar.ini, /etc/php/8.2/cli/conf.d/20-ctype.ini, /etc/php/8.2/cli/conf.d/20-curl.ini, /etc/php/8.2/cli/conf.d/20-dom.ini, /etc/php/8.2/cli/conf.d/20-ev.ini, /etc/php/8.2/cli/conf.d/20-exif.ini, /etc/php/8.2/cli/conf.d/20-ffi.ini, /etc/php/8.2/cli/conf.d/20-fileinfo.ini, /etc/php/8.2/cli/conf.d/20-ftp.ini, /etc/php/8.2/cli/conf.d/20-gd.ini, /etc/php/8.2/cli/conf.d/20-gettext.ini, /etc/php/8.2/cli/conf.d/20-gmp.ini, /etc/php/8.2/cli/conf.d/20-iconv.ini, /etc/php/8.2/cli/conf.d/20-igbinary.ini, /etc/php/8.2/cli/conf.d/20-imagick.ini, /etc/php/8.2/cli/conf.d/20-intl.ini, /etc/php/8.2/cli/conf.d/20-mbstring.ini, /etc/php/8.2/cli/conf.d/20-msgpack.ini, /etc/php/8.2/cli/conf.d/20-phar.ini, /etc/php/8.2/cli/conf.d/20-posix.ini, /etc/php/8.2/cli/conf.d/20-raphf.ini, /etc/php/8.2/cli/conf.d/20-readline.ini, /etc/php/8.2/cli/conf.d/20-redis.ini, /etc/php/8.2/cli/conf.d/20-shmop.ini, /etc/php/8.2/cli/conf.d/20-simplexml.ini, /etc/php/8.2/cli/conf.d/20-sockets.ini, /etc/php/8.2/cli/conf.d/20-ssh2.ini, /etc/php/8.2/cli/conf.d/20-sysvmsg.ini, /etc/php/8.2/cli/conf.d/20-sysvsem.ini, /etc/php/8.2/cli/conf.d/20-sysvshm.ini, /etc/php/8.2/cli/conf.d/20-tokenizer.ini, /etc/php/8.2/cli/conf.d/20-uuid.ini, /etc/php/8.2/cli/conf.d/20-xdebug.ini, /etc/php/8.2/cli/conf.d/20-xmlreader.ini, /etc/php/8.2/cli/conf.d/20-xmlwriter.ini, /etc/php/8.2/cli/conf.d/20-xsl.ini, /etc/php/8.2/cli/conf.d/20-yaml.ini, /etc/php/8.2/cli/conf.d/25-http.ini, /etc/php/8.2/cli/conf.d/25-memcached.ini, /etc/php/8.2/cli/conf.d/30-ds.ini /etc/php/8.2/cli/php.ini diff --git a/src/Template/Parser/StreamingCompiler.php b/src/Template/Parser/StreamingCompiler.php index 00a4529..2e53a67 100644 --- a/src/Template/Parser/StreamingCompiler.php +++ b/src/Template/Parser/StreamingCompiler.php @@ -112,6 +112,9 @@ private function renderCharacterReference($document): Document private function escapeData(int $selectionStart, Document $document): Closure { + if($this->blockAttributes) { + return static fn(Closure $x) => $x($document); + } $end = $document->mark() - 1; $originalData = substr($document->code, $selectionStart, $end - $selectionStart); $data = $this->blobber->replaceBlobs($originalData, $this->escaper->escapeHtml(...)); @@ -429,12 +432,18 @@ private function renderOpenTagName(Document $document): Document case 'title': case 'textarea': $this->mustMatch = $tag; + if($this->blockAttributes) { + return $this->renderRCData($document); + } $now = $document->mark(); return $this->renderRCData($document) ->snip($now, $this->lastTagCloseOpen, $output) ->insert($this->blobber->replaceBlobs($output, $this->escaper->escapeHtml(...)), $now); case 'style': $this->mustMatch = $tag; + if($this->blockAttributes) { + return $this->renderRawText($document); + } $now = $document->mark(); return $this ->renderRawText($document) @@ -447,12 +456,18 @@ private function renderOpenTagName(Document $document): Document case 'plaintext': case 'noframes': $this->mustMatch = $tag; + if($this->blockAttributes) { + return $this->renderRawText($document); + } $now = $document->mark(); return $this->renderRawText($document) ->snip($now, $this->lastTagCloseOpen, $output) ->insert($this->blobber->replaceBlobs($output, $this->escaper->escapeHtml(...)), $now); case 'script': $this->mustMatch = $tag; + if($this->blockAttributes) { + return $this->renderScriptData($document); + } $now = $document->mark(); return $this ->renderScriptData($document) @@ -1180,13 +1195,15 @@ private function renderAfterAttributeValueQuoted(Document $document): Document private function processAttributes(Document $document): Document { - $value = $this->blobber->replaceBlobs( - $this->attributeValue, - $this->escaper->escapeHtmlAttr(...) - ); + if($this->blockAttributes) { + return $document; + } + + $originalValue = $this->blobber->replaceBlobs($this->attributeValue, fn($_) => $_); + $value = $this->escaper->escapeHtmlAttr($originalValue); // escaper doesn't escape single quotes, so we do that here. $value = str_replace("'", ''', $value); - $this->setAttribute($this->attributeName, $value); + $this->setAttribute($this->attributeName, $originalValue); if ($value !== $this->attributeValue) { // we need to update the rendered html too... $here = $document->mark(); diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php index eb0e9f6..12481ae 100644 --- a/tests/StreamingTest.php +++ b/tests/StreamingTest.php @@ -213,3 +213,78 @@ public function render(): string )->getHeader('Set-Cookie')[0] )->toStartWith('csrf_token='); }); + +test('data providers do not escape their children', function () { + $container = containerWithComponents([ + 'provider' => new class implements \Bottledcode\SwytchFramework\Template\Functional\DataProvider { + + public function provideAttributes(): array + { + return ['test' => 'test']; + } + + public function provideValues(string $value): mixed + { + return $value; + } + + public function render(string $test = ''): string + { + if ($test) { + return 'oh no'; + } + return ''; + } + }, + 'echo' => new class { + public function render(string $test = ''): string + { + return $test; + } + }, + 'empty' => new class { + public function render() + { + return ''; + } + } + ]); + $streamer = $container->get(StreamingCompiler::class); + $document = << + + + + +HTML; + + $result = $streamer->compile($document); + expect($result)->toMatchHtmlSnapshot(); +}); + +it('passes variables correctly', function () { + $container = containerWithComponents([ + 'echo' => new class { + public function render(string $test = ''): string + { + return $test; + } + }, + 'child' => new class { + public function render(string $test = ''): string + { + return "
{{$test}}
"; + } + } + ]); + + $streamer = $container->get(StreamingCompiler::class); + $document = << + + + +HTML; + $result = $streamer->compile($document); + expect($result)->toMatchHtmlSnapshot(); +}); diff --git a/tests/__snapshots__/StreamingTest__data_providers_do_not_escape_their_children__1.html b/tests/__snapshots__/StreamingTest__data_providers_do_not_escape_their_children__1.html new file mode 100644 index 0000000..b8c597b --- /dev/null +++ b/tests/__snapshots__/StreamingTest__data_providers_do_not_escape_their_children__1.html @@ -0,0 +1,5 @@ +

test + +not overridden + +

diff --git a/tests/__snapshots__/StreamingTest__it_passes_variables_correctly__1.html b/tests/__snapshots__/StreamingTest__it_passes_variables_correctly__1.html new file mode 100644 index 0000000..139edc9 --- /dev/null +++ b/tests/__snapshots__/StreamingTest__it_passes_variables_correctly__1.html @@ -0,0 +1,8 @@ + +

some text +

+
outer text +inner text +
+ +