Skip to content

Commit

Permalink
modifier flags WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Jan 10, 2024
1 parent 3a3dd2f commit f6fe4ad
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 49 deletions.
81 changes: 54 additions & 27 deletions src/Latte/Compiler/Nodes/Php/ModifierNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,53 @@

namespace Latte\Compiler\Nodes\Php;

use Latte\CompileException;
use Latte\Compiler\Node;
use Latte\Compiler\Position;
use Latte\Compiler\PrintContext;


class ModifierNode extends Node
{
/** @var array<string, FilterNode> */
public array $flags = [];
public bool $check = false;


public function __construct(
/** @var FilterNode[] */
public array $filters,
public bool $escape = false,
public bool $check = true,
public ?Position $position = null,
) {
(function (FilterNode ...$args) {})(...$filters);
}


public function defineFlags(string ...$names): void
{
foreach ($this->filters as $i => $filter) {
$name = $filter->name->name;
if (in_array($name, $names, true)) {
if ($filter->nullsafe || $filter->args) {
throw new CompileException("Flag |$name cannot have arguments or nullsafe pipe.", $filter->position);
}
unset($this->filters[$i]);
$this->flags[$name] = $filter;
}
}
$this->escape = in_array('noescape', $names, true)
? empty($this->flags['noescape'])
: $this->escape;
}


public function hasFlag(string $name): bool
{
return isset($this->flags[$name]);
}


public function hasFilter(string $name): bool
{
foreach ($this->filters as $filter) {
Expand All @@ -47,30 +76,15 @@ public function print(PrintContext $context): string

public function printSimple(PrintContext $context, string $expr): string
{
$escape = $this->escape;
$check = $this->check;
$filters = [];
foreach ($this->filters as $filter) {
$name = $filter->name->name;
if ($name === 'nocheck' || $name === 'noCheck') {
$check = false;
} elseif ($name === 'noescape') {
$escape = false;
} else {
if ($name === 'datastream' || $name === 'dataStream') {
$check = false;
}
$filters[] = $filter;
}
}
$expr = FilterNode::printFilters($context, $filters, $expr);
$this->checkUnallowedFlags();
$expr = FilterNode::printFilters($context, $this->filters, $expr);

$escaper = $context->getEscaper();
if ($check) {
if ($this->check) {
$expr = $escaper->check($expr);
}

$expr = $escape
$expr = $this->escape
? $escaper->escape($expr)
: $escaper->escapeMandatory($expr);

Expand All @@ -80,16 +94,12 @@ public function printSimple(PrintContext $context, string $expr): string

public function printContentAware(PrintContext $context, string $expr): string
{
$this->checkUnallowedFlags();
foreach ($this->filters as $filter) {
$name = $filter->name->name;
if ($name === 'noescape') {
$noescape = true;
} else {
$expr = $filter->printContentAware($context, $expr);
}
$expr = $filter->printContentAware($context, $expr);
}

if ($this->escape && empty($noescape)) {
if ($this->escape) {
$expr = 'LR\Filters::convertTo($ʟ_fi, '
. var_export($context->getEscaper()->export(), true) . ', '
. $expr
Expand All @@ -105,5 +115,22 @@ public function &getIterator(): \Generator
foreach ($this->filters as &$filter) {
yield $filter;
}
foreach ($this->flags as &$filter) {
yield $filter;
}
}


private function checkUnallowedFlags(): void
{
foreach ($this->filters as $filter) {
$name = $filter->name->name;
if ($this->escape && $name === 'noescape') { // back compatibility
$this->defineFlags('noescape');
$this->escape = false;
} elseif ($name === 'noescape' || $name === 'nocheck' || $name === 'noCheck') {
throw new CompileException("Filter |$name is not allowed here.", $filter->position);
}
}
}
}
5 changes: 3 additions & 2 deletions src/Latte/Essential/Nodes/BlockNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ public static function create(Tag $tag, TemplateParser $parser): \Generator
}

$node->modifier = $tag->parser->parseModifier();
$node->modifier->escape = (bool) $node->modifier->filters;
if ($node->modifier->hasFilter('noescape') && count($node->modifier->filters) === 1) {
$node->modifier->defineFlags('noescape');
$node->modifier->escape = $node->modifier->escape && $node->modifier->filters;
if ($node->modifier->hasFlag('noescape') && !$node->modifier->filters) {
throw new CompileException('Filter |noescape is not expected here.', $tag->position);
}

Expand Down
11 changes: 5 additions & 6 deletions src/Latte/Essential/Nodes/ForeachNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ public static function create(Tag $tag): \Generator
self::parseArguments($tag->parser, $node);

$modifier = $tag->parser->parseModifier();
foreach ($modifier->filters as $filter) {
match ($filter->name->name) {
'nocheck', 'noCheck' => $node->checkArgs = false,
'noiterator', 'noIterator' => $node->iterator = false,
default => throw new CompileException('Only modifiers |noiterator and |nocheck are allowed here.', $tag->position),
};
$modifier->defineFlags('nocheck', 'noCheck', 'noiterator', 'noIterator');
$node->checkArgs = !$modifier->hasFlag('nocheck') && !$modifier->hasFlag('noCheck');
$node->iterator = $modifier->hasFlag('noiterator') || $modifier->hasFlag('noIterator') ? false : null;
if ($modifier->filters) {
throw new CompileException('Only modifiers |noiterator and |nocheck are allowed here.', $tag->position);
}

if ($tag->void) {
Expand Down
8 changes: 4 additions & 4 deletions src/Latte/Essential/Nodes/IncludeBlockNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public static function create(Tag $tag, TemplateParser $parser): static
$stream->tryConsume(',');
$node->args = $tag->parser->parseArguments();
$node->modifier = $tag->parser->parseModifier();
$node->modifier->escape = (bool) $node->modifier->filters;

$node->parent = $tokenName->is('parent');
if ($node->parent && $node->modifier->filters) {
Expand All @@ -76,6 +75,8 @@ public static function create(Tag $tag, TemplateParser $parser): static
$node->name = $item->node->block->name;
}

$node->modifier->defineFlags('noescape');
$node->modifier->escape = $node->modifier->escape && $node->modifier->filters;
$node->blocks = &$parser->blocks;
$node->layer = $parser->blockLayer;
return $node;
Expand All @@ -84,13 +85,12 @@ public static function create(Tag $tag, TemplateParser $parser): static

public function print(PrintContext $context): string
{
$noEscape = $this->modifier->hasFilter('noescape');
$modArg = count($this->modifier->filters) > (int) $noEscape
$modArg = $this->modifier->filters
? $context->format(
'function ($s, $type) { $ʟ_fi = new LR\FilterInfo($type); return %modifyContent($s); }',
$this->modifier,
)
: ($noEscape || $this->parent ? '' : PhpHelpers::dump($context->getEscaper()->export()));
: ($this->modifier->hasFlag('noescape') || $this->parent ? '' : PhpHelpers::dump($context->getEscaper()->export()));

return $this->from
? $this->printBlockFrom($context, $modArg)
Expand Down
8 changes: 4 additions & 4 deletions src/Latte/Essential/Nodes/IncludeFileNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@ public static function create(Tag $tag): static
$stream->tryConsume(',');
$node->args = $tag->parser->parseArguments();
$node->modifier = $tag->parser->parseModifier();
$node->modifier->escape = (bool) $node->modifier->filters;
$node->modifier->defineFlags('noescape');
$node->modifier->escape = $node->modifier->escape && $node->modifier->filters;
return $node;
}


public function print(PrintContext $context): string
{
$noEscape = $this->modifier->hasFilter('noescape');
return $context->format(
'$this->createTemplate(%node, %node? + $this->params, %dump)->renderToContentType(%raw) %line;',
$this->file,
$this->args,
$this->mode,
count($this->modifier->filters) > (int) $noEscape
$this->modifier->filters
? $context->format(
'function ($s, $type) { $ʟ_fi = new LR\FilterInfo($type); return %modifyContent($s); }',
$this->modifier,
)
: PhpHelpers::dump($noEscape ? null : $context->getEscaper()->export()),
: PhpHelpers::dump($this->modifier->hasFlag('noescape') ? null : $context->getEscaper()->export()),
$this->position,
);
}
Expand Down
12 changes: 10 additions & 2 deletions src/Latte/Essential/Nodes/PrintNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,24 @@ public static function create(Tag $tag, TemplateParser $parser): static
: null;
$node->expression = $tag->parser->parseExpression();
$node->modifier = $tag->parser->parseModifier();
$node->modifier->escape = true;
$node->modifier->defineFlags('noescape');
return $node;
}


public function print(PrintContext $context): string
{
if ($this->followsQuote && $context->getEscaper()->export() === 'html/raw/js') {
$escaper = $context->getEscaper();
if ($this->followsQuote && $escaper->export() === 'html/raw/js') {
throw new CompileException("Do not place {$this->followsQuote} inside quotes in JavaScript.", $this->position);
}

if ($escaper->export() === 'html/attr/url') {
$this->modifier->defineFlags('nocheck', 'noCheck');
$this->modifier->check = !$this->modifier->hasFlag('nocheck') && !$this->modifier->hasFlag('noCheck')
&& !$this->modifier->hasFilter('datastream') && !$this->modifier->hasFilter('dataStream');
}

return $context->format(
"echo %modify(%node) %line;\n",
$this->modifier,
Expand Down
2 changes: 1 addition & 1 deletion src/Latte/Essential/Nodes/TranslateNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static function create(Tag $tag, ?callable $translator): \Generator
$node = $tag->node = new static;
$args = $tag->parser->parseArguments();
$node->modifier = $tag->parser->parseModifier();
$node->modifier->escape = true;
$node->modifier->defineFlags('noescape');
if ($tag->void) {
return new NopNode;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Latte/Essential/TranslatorExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function parseTranslate(Tag $tag): PrintNode
}

$node->modifier = $tag->parser->parseModifier();
$node->modifier->escape = true;
$node->modifier->defineFlags('noescape');

if ($this->translator
&& $this->key
Expand Down
24 changes: 24 additions & 0 deletions tests/common/Compiler.errors.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,27 @@ Assert::exception(
Latte\CompileException::class,
'Nullsafe pipe is not allowed here (on line 1 at column 13)',
);

Assert::exception(
fn() => $latte->compile('{capture $foo|noescape}...{/capture}'),
Latte\CompileException::class,
'Filter |noescape is not allowed here (on line 1 at column 14)',
);

Assert::exception(
fn() => $latte->compile('{$foo|nocheck}'),
Latte\CompileException::class,
'Filter |nocheck is not allowed here (on line 1 at column 6)',
);

Assert::exception(
fn() => $latte->compile('{$foo?|noescape}'),
Latte\CompileException::class,
'Flag |noescape cannot have arguments or nullsafe pipe (on line 1 at column 6)',
);

Assert::exception(
fn() => $latte->compile('{$foo|noescape:123}'),
Latte\CompileException::class,
'Flag |noescape cannot have arguments or nullsafe pipe (on line 1 at column 6)',
);
3 changes: 2 additions & 1 deletion tests/phpParser/modifier.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Assert::same(

__halt_compiler();
Latte\Compiler\Nodes\Php\ModifierNode
flags: array (0)
check: false
filters: array (2)
| 0 => Latte\Compiler\Nodes\Php\FilterNode
| | name: Latte\Compiler\Nodes\Php\IdentifierNode
Expand Down Expand Up @@ -72,5 +74,4 @@ Latte\Compiler\Nodes\Php\ModifierNode
| | nullsafe: false
| | position: 1:26 (offset 25)
escape: false
check: true
position: 1:1 (offset 0)
3 changes: 2 additions & 1 deletion tests/phpParser/modifierNullsafe.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Assert::same(

__halt_compiler();
Latte\Compiler\Nodes\Php\ModifierNode
flags: array (0)
check: false
filters: array (2)
| 0 => Latte\Compiler\Nodes\Php\FilterNode
| | name: Latte\Compiler\Nodes\Php\IdentifierNode
Expand Down Expand Up @@ -72,5 +74,4 @@ Latte\Compiler\Nodes\Php\ModifierNode
| | nullsafe: true
| | position: 1:28 (offset 27)
escape: false
check: true
position: 1:1 (offset 0)

0 comments on commit f6fe4ad

Please sign in to comment.