mirror of
https://github.com/twigphp/Twig.git
synced 2026-08-31 20:47:28 +00:00
made escaping on ternary expressions more fine-grained
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
* 1.39.0 (2019-XX-XX)
|
||||
|
||||
* made escaping on ternary expressions (?: and ??) more fine-grained
|
||||
* added the possibility to give a nice name to string templates (template_from_string function)
|
||||
* fixed the "with" behavior to always include the globals (for consistency with the "include" and "embed" tags)
|
||||
* fixed "include" with "ignore missing" when an error loading occurs in the included template
|
||||
|
||||
@@ -13,6 +13,8 @@ if ``raw`` is the last filter applied to it:
|
||||
|
||||
.. note::
|
||||
|
||||
**This note only applies to Twig before versions 1.39 and 2.8**.
|
||||
|
||||
Be careful when using the ``raw`` filter inside expressions:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InlinePrint extends AbstractExpression
|
||||
{
|
||||
public function __construct(Node $node, $lineno)
|
||||
{
|
||||
parent::__construct(['node' => $node], [], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('print (')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,11 @@ use Twig\Environment;
|
||||
use Twig\Node\AutoEscapeNode;
|
||||
use Twig\Node\BlockNode;
|
||||
use Twig\Node\BlockReferenceNode;
|
||||
use Twig\Node\DoNode;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Expression\InlinePrint;
|
||||
use Twig\Node\ImportNode;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
@@ -69,8 +72,13 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
$this->blocks = [];
|
||||
} elseif ($node instanceof FilterExpression) {
|
||||
return $this->preEscapeFilterNode($node, $env);
|
||||
} elseif ($node instanceof PrintNode) {
|
||||
return $this->escapePrintNode($node, $env, $this->needEscaping($env));
|
||||
} elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping($env)) {
|
||||
$expression = $node->getNode('expr');
|
||||
if ($expression instanceof ConditionalExpression && $this->shouldUnwrapConditional($expression, $env, $type)) {
|
||||
return new DoNode($this->unwrapConditional($expression, $env, $type), $expression->getTemplateLine());
|
||||
}
|
||||
|
||||
return $this->escapePrintNode($node, $env, $type);
|
||||
}
|
||||
|
||||
if ($node instanceof AutoEscapeNode || $node instanceof BlockNode) {
|
||||
@@ -82,6 +90,44 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function shouldUnwrapConditional(ConditionalExpression $expression, Environment $env, $type)
|
||||
{
|
||||
$expr2Safe = $this->isSafeFor($type, $expression->getNode('expr2'), $env);
|
||||
$expr3Safe = $this->isSafeFor($type, $expression->getNode('expr3'), $env);
|
||||
|
||||
return $expr2Safe !== $expr3Safe;
|
||||
}
|
||||
|
||||
private function unwrapConditional(ConditionalExpression $expression, Environment $env, $type)
|
||||
{
|
||||
// convert "echo a ? b : c" to "a ? echo b : echo c" recursively
|
||||
$expr2 = $expression->getNode('expr2');
|
||||
if ($expr2 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr2, $env, $type)) {
|
||||
$expr2 = $this->unwrapConditional($expr2, $env, $type);
|
||||
} else {
|
||||
$expr2 = $this->escapeInlinePrintNode(new InlinePrint($expr2, $expr2->getTemplateLine()), $env, $type);
|
||||
}
|
||||
$expr3 = $expression->getNode('expr3');
|
||||
if ($expr3 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr2, $env, $type)) {
|
||||
$expr3 = $this->unwrapConditional($expr3, $env, $type);
|
||||
} else {
|
||||
$expr3 = $this->escapeInlinePrintNode(new InlinePrint($expr3, $expr3->getTemplateLine()), $env, $type);
|
||||
}
|
||||
|
||||
return new ConditionalExpression($expression->getNode('expr1'), $expr2, $expr3, $expression->getTemplateLine());
|
||||
}
|
||||
|
||||
private function escapeInlinePrintNode(InlinePrint $node, Environment $env, $type)
|
||||
{
|
||||
$expression = $node->getNode('node');
|
||||
|
||||
if ($this->isSafeFor($type, $expression, $env)) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return new InlinePrint($this->getEscaperFilter($type, $expression), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
protected function escapePrintNode(PrintNode $node, Environment $env, $type)
|
||||
{
|
||||
if (false === $type) {
|
||||
@@ -96,10 +142,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
|
||||
$class = \get_class($node);
|
||||
|
||||
return new $class(
|
||||
$this->getEscaperFilter($type, $expression),
|
||||
$node->getTemplateLine()
|
||||
);
|
||||
return new $class($this->getEscaperFilter($type, $expression), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
protected function preEscapeFilterNode(FilterExpression $filter, Environment $env)
|
||||
|
||||
@@ -11,19 +11,36 @@
|
||||
|
||||
3. Conditional expression with a variable
|
||||
{{ true ? "<br />" : someVar }}
|
||||
{{ false ? "<br />" : someVar }}
|
||||
{{ true ? someVar : "<br />" }}
|
||||
{{ false ? someVar : "<br />" }}
|
||||
|
||||
4. Nested conditionals with only literals
|
||||
{{ true ? (true ? "<br />" : "<br>") : "\n" }}
|
||||
|
||||
5. Nested conditionals with a variable
|
||||
{{ true ? (true ? "<br />" : someVar) : "\n" }}
|
||||
{{ true ? (false ? "<br />" : someVar) : "\n" }}
|
||||
{{ true ? (true ? someVar : "<br />") : "\n" }}
|
||||
{{ true ? (false ? someVar : "<br />") : "\n" }}
|
||||
|
||||
6. Nested conditionals with a variable marked safe
|
||||
{{ true ? (true ? "<br />" : someVar|raw) : "\n" }}
|
||||
{{ true ? (false ? "<br />" : someVar|raw) : "\n" }}
|
||||
{{ true ? (true ? someVar|raw : "<br />") : "\n" }}
|
||||
{{ true ? (false ? someVar|raw : "<br />") : "\n" }}
|
||||
|
||||
7. Without then clause
|
||||
{{ "<br />" ?: someVar }}
|
||||
{{ someFalseVar ?: "<br />" }}
|
||||
|
||||
8. NullCoalesce
|
||||
{{ aaaa ?? "<br />" }}
|
||||
{{ "<br />" ?? someVar }}
|
||||
|
||||
{% endautoescape %}
|
||||
--DATA--
|
||||
return []
|
||||
return ['someVar' => '<br />', 'someFalseVar' => false]
|
||||
--EXPECT--
|
||||
|
||||
1. Simple literal
|
||||
@@ -33,13 +50,30 @@ return []
|
||||
<br />
|
||||
|
||||
3. Conditional expression with a variable
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
4. Nested conditionals with only literals
|
||||
<br />
|
||||
|
||||
5. Nested conditionals with a variable
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
6. Nested conditionals with a variable marked safe
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
7. Without then clause
|
||||
<br />
|
||||
<br />
|
||||
|
||||
8. NullCoalesce
|
||||
<br />
|
||||
<br />
|
||||
|
||||
Reference in New Issue
Block a user