Commit cc2ee344 authored by Taddeus Kroes's avatar Taddeus Kroes

Template variable values are not HTML-excaped by default.

parent 130a7468
Template:
- Escape variable values by default, prevent escape using $$ in template instead of $.
- Be less 'forgiving': throw errors if requested constant/variable does not exist.
\ No newline at end of file
...@@ -267,7 +267,7 @@ class Template extends Node { ...@@ -267,7 +267,7 @@ class Template extends Node {
* *
* This function is a helper for {@link evaluate_expression()}. * This function is a helper for {@link evaluate_expression()}.
* *
* @param array $matches Regex matches for variable pattern. * @param string[] $matches Regex matches for variable pattern.
* @return string The evaluation of the variable. * @return string The evaluation of the variable.
* @param Node $data A data tree containing variable values to use. * @param Node $data A data tree containing variable values to use.
* @throws \BadMethodCallException If an error occured while calling a variable method. * @throws \BadMethodCallException If an error occured while calling a variable method.
...@@ -276,12 +276,13 @@ class Template extends Node { ...@@ -276,12 +276,13 @@ class Template extends Node {
*/ */
private static function evaluate_variable(array $matches, Node $data) { private static function evaluate_variable(array $matches, Node $data) {
$before = $matches[1]; $before = $matches[1];
$variable = $matches[2]; $noescape_sign = $matches[2];
$variable = $matches[3];
$value = $data->get($variable); $value = $data->get($variable);
if( count($matches) == 4 ) { if( count($matches) == 5 ) {
// $<name>.<name> // $<name>.<name>
$attribute = $matches[3]; $attribute = $matches[4];
if( $value === null ) { if( $value === null ) {
throw new \UnexpectedValueException( throw new \UnexpectedValueException(
...@@ -304,9 +305,9 @@ class Template extends Node { ...@@ -304,9 +305,9 @@ class Template extends Node {
} else { } else {
$attr_error('variable is no array or object'); $attr_error('variable is no array or object');
} }
} elseif( count($matches) == 5 ) { } elseif( count($matches) == 6 ) {
// $<name>.<name>() // $<name>.<name>()
$method = $matches[3]; $method = $matches[4];
if( $value === null ) { if( $value === null ) {
throw new \UnexpectedValueException( throw new \UnexpectedValueException(
...@@ -328,9 +329,25 @@ class Template extends Node { ...@@ -328,9 +329,25 @@ class Template extends Node {
} }
} }
// Escape value
if( is_string($value) && !$noescape_sign )
$value = self::escape_variable_value($value);
return $before . $value; return $before . $value;
} }
/**
* Escape a vairable value for displaying in HTML.
*
* Uses {@link http://php.net/htmlentities} with ENT_QUOTES.
*
* @param string $value The variable value to escape.
* @return string The escaped value.
*/
private static function escape_variable_value($value) {
return htmlentities($value, ENT_QUOTES);
}
/** /**
* Evaluate a conditional expression. * Evaluate a conditional expression.
* *
...@@ -420,8 +437,9 @@ class Template extends Node { ...@@ -420,8 +437,9 @@ class Template extends Node {
if( preg_match("/^([^?]*?)\s*\?([^:]*)(?::(.*))?$/", $expression, $matches) ) { if( preg_match("/^([^?]*?)\s*\?([^:]*)(?::(.*))?$/", $expression, $matches) ) {
// <nested_exp>?<nested_exp> | <nested_exp>?<nested_exp>:<nested_exp> // <nested_exp>?<nested_exp> | <nested_exp>?<nested_exp>:<nested_exp>
return self::evaluate_condition($matches, $data); return self::evaluate_condition($matches, $data);
} elseif( preg_match("/^(.*?)\\$($name)(?:\.($name)(\(\))?)?$/", $expression, $matches) ) { } elseif( preg_match("/^(.*?)\\$(\\$?)($name)(?:\.($name)(\(\))?)?$/", $expression, $matches) ) {
// $<name> | $<name>.<name> | $<name>.<name>() // $<name> | $<name>.<name> | $<name>.<name>()
// | $$<name> | $$<name>.<name> | $$<name>.<name>()
return self::evaluate_variable($matches, $data); return self::evaluate_variable($matches, $data);
} elseif( preg_match("/^($function)\((.+?)\)?$/", $expression, $matches) ) { } elseif( preg_match("/^($function)\((.+?)\)?$/", $expression, $matches) ) {
// <function>(<nested_exp>) // <function>(<nested_exp>)
......
...@@ -21,6 +21,8 @@ class DataObject { ...@@ -21,6 +21,8 @@ class DataObject {
} }
class TemplateTest extends PHPUnit_Framework_TestCase { class TemplateTest extends PHPUnit_Framework_TestCase {
const INTERNATIONALIZATION_STRING = 'Itrntinliztin';
/** /**
* @depends test_add_root_success * @depends test_add_root_success
*/ */
...@@ -43,6 +45,8 @@ class TemplateTest extends PHPUnit_Framework_TestCase { ...@@ -43,6 +45,8 @@ class TemplateTest extends PHPUnit_Framework_TestCase {
'object' => new DataObject, 'object' => new DataObject,
'foobar' => 'my_foobar_variable', 'foobar' => 'my_foobar_variable',
'foobaz' => 'MY_FOOBAZ_VARIABLE', 'foobaz' => 'MY_FOOBAZ_VARIABLE',
'html' => '<script></script>',
'internationalization' => self::INTERNATIONALIZATION_STRING,
)); ));
} }
...@@ -284,6 +288,22 @@ class TemplateTest extends PHPUnit_Framework_TestCase { ...@@ -284,6 +288,22 @@ class TemplateTest extends PHPUnit_Framework_TestCase {
$this->assert_evaluates('foobar', '$object.baz()'); $this->assert_evaluates('foobar', '$object.baz()');
} }
/**
* @depends test_evaluate_variable_success
*/
function test_evaluate_variable_escape() {
$this->assert_evaluates('&lt;script&gt;&lt;/script&gt;', '$html');
$this->assert_evaluates('I&ntilde;t&euml;rn&acirc;ti&ocirc;n&agrave;liz&aelig;ti&oslash;n', '$internationalization');
}
/**
* @depends test_evaluate_variable_success
*/
function test_evaluate_variable_noescape() {
$this->assert_evaluates('<script></script>', '$$html');
$this->assert_evaluates('Itrntinliztin', '$$internationalization');
}
function test_evaluate_constant() { function test_evaluate_constant() {
$this->assert_evaluates('foobar_const', 'FOOBAR'); $this->assert_evaluates('foobar_const', 'FOOBAR');
$this->assert_evaluates('{NON_DEFINED_CONST}', 'NON_DEFINED_CONST'); $this->assert_evaluates('{NON_DEFINED_CONST}', 'NON_DEFINED_CONST');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment