Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [Parser] add configs to change conditional delimiters #5842

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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('{%', '%}');