Skip to content

Commit

Permalink
Merge pull request #5842 from kenjis/feat-parser-conditional-delimiter
Browse files Browse the repository at this point in the history
feat: [Parser] add configs to change conditional delimiters
  • Loading branch information
kenjis committed Apr 2, 2022
2 parents 0a35b4a + 2d90f0c commit e075dbf
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 3 deletions.
45 changes: 42 additions & 3 deletions system/View/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class Parser extends View
*/
public $rightDelimiter = '}';

/**
* Left delimiter characters for conditionals
*/
protected string $leftConditionalDelimiter = '{';

/**
* Right delimiter characters for conditionals
*/
protected string $rightConditionalDelimiter = '}';

/**
* Stores extracted noparse blocks.
*
Expand Down Expand Up @@ -405,7 +415,14 @@ public function insertNoparse(string $template): string
*/
protected function parseConditionals(string $template): string
{
$pattern = '/\{\s*(if|elseif)\s*((?:\()?(.*?)(?:\))?)\s*\}/ms';
$leftDelimiter = preg_quote($this->leftConditionalDelimiter, '/');
$rightDelimiter = preg_quote($this->rightConditionalDelimiter, '/');
$pattern = '/'
. $leftDelimiter
. '\s*(if|elseif)\s*((?:\()?(.*?)(?:\))?)\s*'
. $rightDelimiter
. '/ms';
/*
* For each match:
Expand All @@ -424,8 +441,16 @@ protected function parseConditionals(string $template): string
$template = str_replace($match[0], $statement, $template);
}

$template = preg_replace('/\{\s*else\s*\}/ms', '<?php else: ?>', $template);
$template = preg_replace('/\{\s*endif\s*\}/ms', '<?php endif; ?>', $template);
$template = preg_replace(
'/' . $leftDelimiter . '\s*else\s*' . $rightDelimiter . '/ms',
'<?php else: ?>',
$template
);
$template = preg_replace(
'/' . $leftDelimiter . '\s*endif\s*' . $rightDelimiter . '/ms',
'<?php endif; ?>',
$template
);

// Parse the PHP itself, or insert an error so they can debug
ob_start();
Expand Down Expand Up @@ -461,6 +486,20 @@ public function setDelimiters($leftDelimiter = '{', $rightDelimiter = '}'): Rend
return $this;
}

/**
* Over-ride the substitution conditional delimiters.
*
* @param string $leftDelimiter
* @param string $rightDelimiter
*/
public function setConditionalDelimiters($leftDelimiter = '{', $rightDelimiter = '}'): RendererInterface
{
$this->leftConditionalDelimiter = $leftDelimiter;
$this->rightConditionalDelimiter = $rightDelimiter;

return $this;
}

/**
* Handles replacing a pseudo-variable with the actual content. Will double-check
* for escaping brackets.
Expand Down
49 changes: 49 additions & 0 deletions tests/system/View/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -938,4 +938,53 @@ public function testRenderFindsOtherView()
$expected = '<h1>Hello World</h1>';
$this->assertSame($expected, $this->parser->render('Simpler.html'));
}

public function testChangedConditionalDelimitersTrue()
{
$this->parser->setConditionalDelimiters('{%', '%}');

$data = [
'doit' => true,
'dontdoit' => false,
];
$this->parser->setData($data);

$template = '{% if $doit %}Howdy{% endif %}{% if $dontdoit === false %}Welcome{% endif %}';
$output = $this->parser->renderString($template);

$this->assertSame('HowdyWelcome', $output);
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/5831
*/
public function testChangeConditionalDelimitersWorkWithJavaScriptCode()
{
$this->parser->setConditionalDelimiters('{%', '%}');

$data = [
'message' => 'Warning!',
];
$this->parser->setData($data);

$template = <<<'EOL'
<script type="text/javascript">
var f = function() {
if (true) {
alert('{message}');
}
}
</script>
EOL;
$expected = <<<'EOL'
<script type="text/javascript">
var f = function() {
if (true) {
alert('Warning!');
}
}
</script>
EOL;
$this->assertSame($expected, $this->parser->renderString($template));
}
}
25 changes: 25 additions & 0 deletions user_guide_src/source/outgoing/view_parser.rst
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,31 @@ of the comparison operators you would normally, like ``==``, ``===``, ``!==``, `
.. warning:: In the background, conditionals are parsed using an ``eval()``, so you must ensure that you take
care with the user data that is used within conditionals, or you could open your application up to security risks.

Changing the Conditional Delimiters
-----------------------------------

If you have JavaScript code like the following in your templates, the Parser raises a syntax error because there are strings that can be interpreted as a conditional::

<script type="text/javascript">
var f = function() {
if (hasAlert) {
alert('{message}');
}
}
</script>

In that case, you can change the delimiters for conditionals with the ``setConditionalDelimiters()`` method to avoid misinterpretations:

.. literalinclude:: view_parser/027.php

In this case, you will write code in your template::

{% if $role=='admin' %}
<h1>Welcome, Admin</h1>
{% else %}
<h1>Welcome, User</h1>
{% endif %}

Escaping Data
=============

Expand Down
3 changes: 3 additions & 0 deletions user_guide_src/source/outgoing/view_parser/027.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

$parser->setConditionalDelimiters('{%', '%}');

0 comments on commit e075dbf

Please sign in to comment.