Done tag option :raw and :escape

This commit is contained in:
bzick 2014-05-06 00:45:37 +04:00
parent ac4ccd0fca
commit 783bf10b39
8 changed files with 153 additions and 73 deletions

View File

@ -1,5 +1,7 @@
{var:escape $a}
asdasd
{/var}
{*{Ts\Math::multi x=34 y=44}*}
{Ts\Math::multi x=34 y=44} {*{$a + Ts\Math::multi(34, 44)}*}
{$a + Ts\Math::multi(34, 44)} {*{34|Ts\Math::multi:44}*}
{34|Ts\Math::multi:44}

View File

@ -36,7 +36,7 @@ class Fenom
const FORCE_VERIFY = 0x800; const FORCE_VERIFY = 0x800;
const AUTO_TRIM = 0x1000; // reserved const AUTO_TRIM = 0x1000; // reserved
const DENY_STATICS = 0x2000; const DENY_STATICS = 0x2000;
const AUTO_STRIP = 0x4000; // reserved const AUTO_STRIP = 0x4000;
/* @deprecated */ /* @deprecated */
const DENY_INLINE_FUNCS = 0x20; const DENY_INLINE_FUNCS = 0x20;

View File

@ -628,7 +628,7 @@ class Compiler
*/ */
public static function stdFuncParser(Tokenizer $tokens, Tag $tag) public static function stdFuncParser(Tokenizer $tokens, Tag $tag)
{ {
return $tag->escape($tag->callback . "(" . self::toArray($tag->tpl->parseParams($tokens)) . ', $tpl)'); return $tag->out($tag->callback . "(" . self::toArray($tag->tpl->parseParams($tokens)) . ', $tpl)');
} }
/** /**
@ -658,7 +658,7 @@ class Compiler
$args[] = var_export($param->getDefaultValue(), true); $args[] = var_export($param->getDefaultValue(), true);
} }
} }
return $tag->escape($tag->callback . "(" . implode(", ", $args) . ')'); return $tag->out($tag->callback . "(" . implode(", ", $args) . ')');
} }
/** /**
@ -672,6 +672,7 @@ class Compiler
public static function stdFuncOpen(Tokenizer $tokens, Tag $tag) public static function stdFuncOpen(Tokenizer $tokens, Tag $tag)
{ {
$tag["params"] = self::toArray($tag->tpl->parseParams($tokens)); $tag["params"] = self::toArray($tag->tpl->parseParams($tokens));
$tag->setOption(\Fenom::AUTO_ESCAPE, false);
return 'ob_start();'; return 'ob_start();';
} }
@ -685,7 +686,8 @@ class Compiler
*/ */
public static function stdFuncClose($tokens, Tag $tag) public static function stdFuncClose($tokens, Tag $tag)
{ {
return $tag->escape($tag->callback . '(' . $tag["params"] . ', ob_get_clean(), $tpl)'); $tag->restore(\Fenom::AUTO_ESCAPE);
return $tag->out($tag->callback . '(' . $tag["params"] . ', ob_get_clean(), $tpl)');
} }
/** /**
@ -944,23 +946,19 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Tag $scope * @param Tag $tag
*/ */
public static function autoescapeOpen(Tokenizer $tokens, Tag $scope) public static function autoescapeOpen(Tokenizer $tokens, Tag $tag)
{ {
$boolean = ($tokens->get(T_STRING) == "true" ? true : false); $expected = ($tokens->get(T_STRING) == "true" ? true : false);
$scope["escape"] = $scope->tpl->escape;
$scope->tpl->escape = $boolean;
$tokens->next(); $tokens->next();
$tag->setOption(\Fenom::AUTO_ESCAPE, $expected);
} }
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Tag $scope * @param Tag $tag
*/ */
public static function autoescapeClose(Tokenizer $tokens, Tag $scope) public static function autoescapeClose(Tokenizer $tokens, Tag $tag) { }
{
$scope->tpl->escape = $scope["escape"];
}
} }

View File

@ -16,15 +16,20 @@ class Tag extends \ArrayObject
const FUNC = 2; const FUNC = 2;
const BLOCK = 4; const BLOCK = 4;
const LTRIM = 1;
const RTRIM = 2;
/** /**
* @var Template * @var Template
*/ */
public $tpl; public $tpl;
public $name; public $name;
public $options; public $options = array();
public $line = 0; public $line = 0;
public $level = 0; public $level = 0;
public $callback; public $callback;
public $escape;
private $_offset = 0; private $_offset = 0;
private $_closed = true; private $_closed = true;
@ -34,6 +39,7 @@ class Tag extends \ArrayObject
private $_close; private $_close;
private $_tags = array(); private $_tags = array();
private $_floats = array(); private $_floats = array();
private $_changed = array();
/** /**
* Create tag entity * Create tag entity
@ -51,6 +57,7 @@ class Tag extends \ArrayObject
$this->_body = & $body; $this->_body = & $body;
$this->_offset = strlen($body); $this->_offset = strlen($body);
$this->_type = $info["type"]; $this->_type = $info["type"];
$this->escape = $tpl->getOptions() & \Fenom::AUTO_ESCAPE;
if ($this->_type & self::BLOCK) { if ($this->_type & self::BLOCK) {
$this->_open = $info["open"]; $this->_open = $info["open"];
@ -70,10 +77,48 @@ class Tag extends \ArrayObject
/** /**
* Set tag option * Set tag option
* @param string $option * @param string $option
* @throws \RuntimeException
*/ */
public function setOption($option) public function tagOption($option)
{ {
if(method_exists($this, 'opt'.$option)) {
$this->options[] = $option;
} else {
throw new \RuntimeException("Unknown tag option $option");
}
}
/**
* Rewrite template option for tag. When tag will be closed option will be reverted.
* @param int $option option constant
* @param bool $value true add option, false remove option
*/
public function setOption($option, $value) {
$actual = (bool)($this->tpl->getOptions() & $option);
if($actual != $value) {
$this->_changed[$option] = $actual;
$this->tpl->setOption(\Fenom::AUTO_ESCAPE, $value);
}
}
/**
* Restore the option
* @param int $option
*/
public function restore($option)
{
if(isset($this->_changed[$option])) {
$this->tpl->setOption($option, $this->_changed[$option]);
unset($this->_changed[$option]);
}
}
public function restoreAll()
{
foreach($this->_changed as $option => $value) {
$this->tpl->setOption($option, $this->_changed[$option]);
unset($this->_changed[$option]);
}
} }
/** /**
@ -93,6 +138,10 @@ class Tag extends \ArrayObject
*/ */
public function start($tokenizer) public function start($tokenizer)
{ {
foreach($this->options as $option) {
$option = 'opt'.$option;
$this->$option();
}
return call_user_func($this->_open, $tokenizer, $this); return call_user_func($this->_open, $tokenizer, $this);
} }
@ -146,7 +195,15 @@ class Tag extends \ArrayObject
throw new \LogicException("Tag {$this->name} already closed"); throw new \LogicException("Tag {$this->name} already closed");
} }
if ($this->_close) { if ($this->_close) {
return call_user_func($this->_close, $tokenizer, $this); foreach($this->options as $option) {
$option = 'opt'.$option.'end';
if(method_exists($this, $option)) {
$this->$option();
}
}
$code = call_user_func($this->_close, $tokenizer, $this);
$this->restoreAll();
return $code;
} else { } else {
throw new \LogicException("Can not use a inline tag {$this->name} as a block"); throw new \LogicException("Can not use a inline tag {$this->name} as a block");
} }
@ -195,33 +252,23 @@ class Tag extends \ArrayObject
$this->_body .= $new_content; $this->_body .= $new_content;
} }
public function escape($code) /**
* Generate output code
* @param string $code
* @return string
*/
public function out($code)
{ {
return $this->tpl->out($code); return $this->tpl->out($code, $this->escape);
}
public function optLtrim()
{
}
public function optRtrim()
{
}
public function optTrim()
{
}
public function optRaw()
{
} }
public function optEscape() public function optEscape()
{ {
$this->escape = true;
}
public function optRaw()
{
$this->escape = false;
} }
} }

View File

@ -26,6 +26,7 @@ class Template extends Render
{ {
const VAR_NAME = '$var'; const VAR_NAME = '$var';
const TPL_NAME = '$tpl'; const TPL_NAME = '$tpl';
/** /**
* Disable array parser. * Disable array parser.
*/ */
@ -287,6 +288,19 @@ class Template extends Render
} }
} }
/**
* Set or unset the option
* @param int $option
* @param bool $value
*/
public function setOption($option, $value) {
if($value) {
$this->_options |= $option;
} else {
$this->_options &= ~$option;
}
}
/** /**
* Execute some code at loading cache * Execute some code at loading cache
* @param $code * @param $code
@ -452,12 +466,16 @@ class Template extends Render
/** /**
* Output the value * Output the value
* *
* @param $data * @param string $data
* @param null|bool $escape
* @return string * @return string
*/ */
public function out($data) public function out($data, $escape = null)
{ {
if ($this->escape) { if($escape === null) {
$escape = $this->_options & Fenom::AUTO_ESCAPE;
}
if ($escape) {
return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8');"; return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8');";
} else { } else {
return "echo $data;"; return "echo $data;";
@ -590,11 +608,13 @@ class Template extends Render
$tag = new Tag($action, $this, $info, $this->_body); $tag = new Tag($action, $this, $info, $this->_body);
if ($tokens->is(':')) { // parse tag options if ($tokens->is(':')) { // parse tag options
do { do {
$tag->setOption($tokens->next()->need(T_STRING)->getAndNext()); $tag->tagOption($tokens->next()->need(T_STRING)->getAndNext());
} while ($tokens->is(':')); } while ($tokens->is(':'));
} }
$code = $tag->start($tokens); $code = $tag->start($tokens);
if (!$tag->isClosed()) { if ($tag->isClosed()) {
$tag->restoreAll();
} else {
array_push($this->_stack, $tag); array_push($this->_stack, $tag);
} }
return $code; return $code;

View File

@ -6,6 +6,7 @@ namespace Fenom;
class AutoEscapeTest extends TestCase class AutoEscapeTest extends TestCase
{ {
public static function providerHTML() public static function providerHTML()
{ {
$html = "<script>alert('injection');</script>"; $html = "<script>alert('injection');</script>";
@ -30,32 +31,32 @@ class AutoEscapeTest extends TestCase
array('{autoescape false}{raw $html}{/autoescape}, {$html}', "$html, $html", $vars, 0), array('{autoescape false}{raw $html}{/autoescape}, {$html}', "$html, $html", $vars, 0),
// inline function // inline function
// array('{test_function text=$html}, {$html}', "$html, $html", $vars, 0), array('{test_function text=$html}, {$html}', "$html, $html", $vars, 0),
// array('{test_function text=$html}, {$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{test_function text=$html}, {$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{test_function:raw text=$html}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{test_function:raw text=$html}, {$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{test_function:raw text="{$html|up}"}, {$html}', strtoupper($html) . ", $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{test_function:raw text="{$html|up}"}, {$html}', strtoupper($html) . ", $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape true}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$escaped, $html", $vars, 0), array('{autoescape true}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$escaped, $html", $vars, 0),
// array('{autoescape false}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape false}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape true}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape false}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), array('{autoescape false}{test_function text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0),
// array('{autoescape true}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), array('{autoescape true}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0),
// array('{autoescape false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape true}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), array('{autoescape false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0),
// block function // block function
// array('{test_block_function}{$html}{/test_block_function}', $html, $vars, 0), array('{test_block_function}{$html}{/test_block_function}', $html, $vars, 0),
// array('{test_block_function}{$html}{/test_block_function}', $escaped, $vars, \Fenom::AUTO_ESCAPE), array('{test_block_function}{$html}{/test_block_function}', $escaped, $vars, \Fenom::AUTO_ESCAPE),
// array('{test_block_function:raw}{$html}{/test_block_function}', $html, $vars, \Fenom::AUTO_ESCAPE), array('{test_block_function:raw}{$html}{/test_block_function}', $html, $vars, \Fenom::AUTO_ESCAPE),
// array('{test_block_function:raw}{"{$html|up}"}{/test_block_function}', strtoupper($html), $vars, \Fenom::AUTO_ESCAPE), array('{test_block_function:raw}{"{$html|up}"}{/test_block_function}', strtoupper($html), $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $html", $vars, 0), array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $html", $vars, 0),
// array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0), array('{autoescape false}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0),
// array('{autoescape true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $html", $vars, 0), array('{autoescape true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0),
// array('{autoescape false}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape false}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $escaped", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $escaped", $vars, \Fenom::AUTO_ESCAPE), array('{autoescape true}{test_block_function}{$html}{/test_block_function}{/autoescape}, {test_block_function:raw}{$html}{/test_block_function}', "$escaped, $html", $vars, \Fenom::AUTO_ESCAPE),
// array('{autoescape false}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $html", $vars, 0), array('{autoescape true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function:raw}{$html}{/test_block_function}', "$html, $html", $vars, 0),
); );
} }

View File

@ -0,0 +1,12 @@
<?php
namespace Fenom;
class TagOptionTest extends TestCase {
public function testTrim() {
}
}

View File

@ -848,7 +848,7 @@ class TemplateTest extends TestCase
public function _testSandbox() public function _testSandbox()
{ {
try { try {
var_dump($this->fenom->compileCode('{$a}')->getBody()); var_dump($this->fenom->setOptions(0)->compileCode("{autoescape true}{test_block_function:raw}{\$html}{/test_block_function}{/autoescape}")->getBody());
} catch (\Exception $e) { } catch (\Exception $e) {
print_r($e->getMessage() . "\n" . $e->getTraceAsString()); print_r($e->getMessage() . "\n" . $e->getTraceAsString());
while ($e->getPrevious()) { while ($e->getPrevious()) {