Skip to content

Commit

Permalink
[FEATURE] Fluid code in comments no longer interpreted (#956)
Browse files Browse the repository at this point in the history
Previously, the behavior of the `<f:comment>` ViewHelper was confusing:
Even if invalid Fluid code (e. g. syntax errors or a non-existent ViewHelper)
was wrapped with `<f:comment>`, it would still be interpreted by the
Fluid parser and an exception would be thrown.

This patch resolves this similarly to CDATA handling. It introduces a new
TemplateProcessor, which is executed before any other parsing and strips
out the contents of Fluid comments. The `<f:comment>` ViewHelpers are
kept intact and are filled with empty lines. This can be helpful when
debugging Fluid templates because the line numbers in exceptions are
consistent to the original template file.

This new TemplateProcessor is then registered in the default
RenderingContext implementation. For systems that set their own
TemplateProcessors, this would probably need adjustments. However,
this change is not breaking since the old functionality is still there.

Fluid's own tests however now assume that this new TemplateProcessor
is registered and are adjusted properly in this patch.
  • Loading branch information
s2b authored Aug 12, 2024
1 parent 2489021 commit faad81a
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Documentation/ViewHelpers/Fluid.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public function preProcessSource(string $templateSource): string
* Replaces all cdata sections with empty lines to exclude it from further
* processing in the templateParser while maintaining the line-count
* of the template string for the exception handler to reference to.
*
* @todo It should be evaluated if this is really necessary. If it is, it should
* be moved to a separate TemplateProcessor (which would be a breaking change)
*/
public function replaceCdataSectionsByEmptyLines(string $templateSource): string
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

/*
* This file belongs to the package "TYPO3 Fluid".
* See LICENSE.txt that was shipped with this package.
*/

namespace TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor;

use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessorInterface;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;

class RemoveCommentsTemplateProcessor implements TemplateProcessorInterface
{
protected RenderingContextInterface $renderingContext;

public function setRenderingContext(RenderingContextInterface $renderingContext): void
{
$this->renderingContext = $renderingContext;
}

/**
* Replaces all comment ViewHelpers with empty lines to exclude it
* from further processing in the templateParser while maintaining
* the line-count of the template string for the exception handler
* to reference to. These empty lines are again wrapped inside
* <f:comment> to not introduce any unwanted whitespace changes in
* existing templates.
*/
public function preProcessSource(string $templateSource): string
{
$parts = preg_split('#(</?f:comment>)#', $templateSource, -1, PREG_SPLIT_DELIM_CAPTURE);

$balance = 0;
foreach ($parts as $index => $part) {
if ($part === '<f:comment>') {
$balance++;
}
if ($balance > 0) {
$parts[$index] = '<f:comment>' . str_repeat(PHP_EOL, substr_count($part, PHP_EOL)) . '</f:comment>';
}
if ($part === '</f:comment>') {
$balance--;
}
}

return implode('', $parts);
}
}
2 changes: 2 additions & 0 deletions src/Core/Rendering/RenderingContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\EscapingModifierTemplateProcessor;
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\NamespaceDetectionTemplateProcessor;
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\PassthroughSourceModifierTemplateProcessor;
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\RemoveCommentsTemplateProcessor;
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessorInterface;
use TYPO3Fluid\Fluid\Core\Variables\StandardVariableProvider;
use TYPO3Fluid\Fluid\Core\Variables\VariableProviderInterface;
Expand Down Expand Up @@ -140,6 +141,7 @@ public function __construct()
new EscapingModifierTemplateProcessor(),
new PassthroughSourceModifierTemplateProcessor(),
new NamespaceDetectionTemplateProcessor(),
new RemoveCommentsTemplateProcessor(),
],
);
$this->setViewHelperResolver(new ViewHelperResolver());
Expand Down
21 changes: 4 additions & 17 deletions src/ViewHelpers/CommentViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
/**
* This ViewHelper prevents rendering of any content inside the tag.
*
* Contents of the comment will still be **parsed** thus throwing an
* Exception if it contains syntax errors. You can put child nodes in
* CDATA tags to avoid this.
* Contents of the comment will **not be parsed** thus it can be used to
* comment out invalid Fluid syntax or non-existent ViewHelpers during
* development.
*
* Using this ViewHelper won't have a notable effect on performance,
* especially once the template is parsed. However, it can lead to reduced
* especially once the template is parsed. However, it can lead to reduced
* readability. You can use layouts and partials to split a large template
* into smaller parts. Using self-descriptive names for the partials can
* make comments redundant.
Expand All @@ -43,19 +43,6 @@
* Before
* After
*
* Prevent parsing
* ---------------
*
* ::
*
* <f:comment><![CDATA[
* <f:some.invalid.syntax />
* ]]></f:comment>
*
* Output:
*
* Will be nothing.
*
* @api
*/
class CommentViewHelper extends AbstractViewHelper
Expand Down
28 changes: 8 additions & 20 deletions tests/Functional/ViewHelpers/CommentViewHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,11 @@

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use TYPO3Fluid\Fluid\Core\Parser\Exception;
use TYPO3Fluid\Fluid\Tests\Functional\AbstractFunctionalTestCase;
use TYPO3Fluid\Fluid\View\TemplateView;

final class CommentViewHelperTest extends AbstractFunctionalTestCase
{
/**
* @todo: That's a rather nasty side effect of f:comment. The parser
* still parses f:comment body, so if the body contains
* invalid stuff (e.g. a not closed VH tag), it explodes.
* The workaround is to have a CDATA wrap, as in the test set
* below. However, it might be possible to look into the parser
* regexes to see if parsing of 'f:comment' content could be
* suppressed somehow.
*/
#[Test]
public function renderThrowsExceptionWhenEncapsulatingInvalidCode(): void
{
$this->expectException(Exception::class);
$view = new TemplateView();
$view->getRenderingContext()->setCache(self::$cache);
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource('<f:comment><f:render></f:comment>');
$view->render();
}

public static function renderDataProvider(): \Generator
{
yield 'no output as self closing tag' => [
Expand All @@ -54,6 +34,14 @@ public static function renderDataProvider(): \Generator
'<f:comment><![CDATA[<f:render>]]></f:comment>',
'',
];
yield 'does not choke with invalid fluid syntax' => [
'<f:comment><f:if>not properly closed</f:comment>',
'',
];
yield 'does not choke with invalid fluid ViewHelper' => [
'<f:comment><my:nonexistentViewHelper /></f:comment>',
'',
];
}

#[DataProvider('renderDataProvider')]
Expand Down

0 comments on commit faad81a

Please sign in to comment.