From 783bf10b39909c5eb665f2ca5302ae3dda2dcec9 Mon Sep 17 00:00:00 2001 From: bzick Date: Tue, 6 May 2014 00:45:37 +0400 Subject: [PATCH] Done tag option :raw and :escape --- sandbox/templates/greeting.tpl | 10 +-- src/Fenom.php | 2 +- src/Fenom/Compiler.php | 24 ++++--- src/Fenom/Tag.php | 97 +++++++++++++++++++++------- src/Fenom/Template.php | 30 +++++++-- tests/cases/Fenom/AutoEscapeTest.php | 49 +++++++------- tests/cases/Fenom/TagOptionTest.php | 12 ++++ tests/cases/Fenom/TemplateTest.php | 2 +- 8 files changed, 153 insertions(+), 73 deletions(-) create mode 100644 tests/cases/Fenom/TagOptionTest.php diff --git a/sandbox/templates/greeting.tpl b/sandbox/templates/greeting.tpl index 156d705..925f85a 100644 --- a/sandbox/templates/greeting.tpl +++ b/sandbox/templates/greeting.tpl @@ -1,5 +1,7 @@ +{var:escape $a} +asdasd +{/var} - -{Ts\Math::multi x=34 y=44} -{$a + Ts\Math::multi(34, 44)} -{34|Ts\Math::multi:44} \ No newline at end of file +{*{Ts\Math::multi x=34 y=44}*} +{*{$a + Ts\Math::multi(34, 44)}*} +{*{34|Ts\Math::multi:44}*} \ No newline at end of file diff --git a/src/Fenom.php b/src/Fenom.php index 7459f6f..000b90b 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -36,7 +36,7 @@ class Fenom const FORCE_VERIFY = 0x800; const AUTO_TRIM = 0x1000; // reserved const DENY_STATICS = 0x2000; - const AUTO_STRIP = 0x4000; // reserved + const AUTO_STRIP = 0x4000; /* @deprecated */ const DENY_INLINE_FUNCS = 0x20; diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index dd75ab7..ace37b7 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -628,7 +628,7 @@ class Compiler */ 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); } } - 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) { $tag["params"] = self::toArray($tag->tpl->parseParams($tokens)); + $tag->setOption(\Fenom::AUTO_ESCAPE, false); return 'ob_start();'; } @@ -685,7 +686,8 @@ class Compiler */ 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 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); - $scope["escape"] = $scope->tpl->escape; - $scope->tpl->escape = $boolean; + $expected = ($tokens->get(T_STRING) == "true" ? true : false); $tokens->next(); + $tag->setOption(\Fenom::AUTO_ESCAPE, $expected); } /** * @param Tokenizer $tokens - * @param Tag $scope + * @param Tag $tag */ - public static function autoescapeClose(Tokenizer $tokens, Tag $scope) - { - $scope->tpl->escape = $scope["escape"]; - } + public static function autoescapeClose(Tokenizer $tokens, Tag $tag) { } } diff --git a/src/Fenom/Tag.php b/src/Fenom/Tag.php index e017303..7b42765 100644 --- a/src/Fenom/Tag.php +++ b/src/Fenom/Tag.php @@ -16,15 +16,20 @@ class Tag extends \ArrayObject const FUNC = 2; const BLOCK = 4; + + const LTRIM = 1; + const RTRIM = 2; + /** * @var Template */ public $tpl; public $name; - public $options; + public $options = array(); public $line = 0; public $level = 0; public $callback; + public $escape; private $_offset = 0; private $_closed = true; @@ -34,6 +39,7 @@ class Tag extends \ArrayObject private $_close; private $_tags = array(); private $_floats = array(); + private $_changed = array(); /** * Create tag entity @@ -51,6 +57,7 @@ class Tag extends \ArrayObject $this->_body = & $body; $this->_offset = strlen($body); $this->_type = $info["type"]; + $this->escape = $tpl->getOptions() & \Fenom::AUTO_ESCAPE; if ($this->_type & self::BLOCK) { $this->_open = $info["open"]; @@ -70,10 +77,48 @@ class Tag extends \ArrayObject /** * Set tag 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) { + foreach($this->options as $option) { + $option = 'opt'.$option; + $this->$option(); + } return call_user_func($this->_open, $tokenizer, $this); } @@ -146,7 +195,15 @@ class Tag extends \ArrayObject throw new \LogicException("Tag {$this->name} already closed"); } 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 { 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; } - public function escape($code) + /** + * Generate output code + * @param string $code + * @return string + */ + public function out($code) { - return $this->tpl->out($code); - } - - public function optLtrim() - { - - } - - public function optRtrim() - { - - } - - public function optTrim() - { - - } - - public function optRaw() - { - + return $this->tpl->out($code, $this->escape); } public function optEscape() { + $this->escape = true; + } + public function optRaw() + { + $this->escape = false; } } \ No newline at end of file diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index a020e98..03ecb29 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -26,6 +26,7 @@ class Template extends Render { const VAR_NAME = '$var'; const TPL_NAME = '$tpl'; + /** * 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 * @param $code @@ -452,12 +466,16 @@ class Template extends Render /** * Output the value * - * @param $data + * @param string $data + * @param null|bool $escape * @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');"; } else { return "echo $data;"; @@ -590,11 +608,13 @@ class Template extends Render $tag = new Tag($action, $this, $info, $this->_body); if ($tokens->is(':')) { // parse tag options do { - $tag->setOption($tokens->next()->need(T_STRING)->getAndNext()); + $tag->tagOption($tokens->next()->need(T_STRING)->getAndNext()); } while ($tokens->is(':')); } $code = $tag->start($tokens); - if (!$tag->isClosed()) { + if ($tag->isClosed()) { + $tag->restoreAll(); + } else { array_push($this->_stack, $tag); } return $code; diff --git a/tests/cases/Fenom/AutoEscapeTest.php b/tests/cases/Fenom/AutoEscapeTest.php index b866a90..59f25b5 100644 --- a/tests/cases/Fenom/AutoEscapeTest.php +++ b/tests/cases/Fenom/AutoEscapeTest.php @@ -6,6 +6,7 @@ namespace Fenom; class AutoEscapeTest extends TestCase { + public static function providerHTML() { $html = ""; @@ -30,32 +31,32 @@ class AutoEscapeTest extends TestCase array('{autoescape false}{raw $html}{/autoescape}, {$html}', "$html, $html", $vars, 0), // inline function -// 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: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('{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 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 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 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('{test_function text=$html}, {$html}', "$html, $html", $vars, 0), + 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|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 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 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 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 false}{test_function:raw text=$html}{/autoescape}, {test_function text=$html}', "$html, $html", $vars, 0), // block function -// 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: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('{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 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 true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$escaped, $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 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 false}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function}{$html}{/test_block_function}', "$html, $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: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('{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 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 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 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 true}{test_block_function:raw}{$html}{/test_block_function}{/autoescape}, {test_block_function:raw}{$html}{/test_block_function}', "$html, $html", $vars, 0), ); } diff --git a/tests/cases/Fenom/TagOptionTest.php b/tests/cases/Fenom/TagOptionTest.php new file mode 100644 index 0000000..8b3c087 --- /dev/null +++ b/tests/cases/Fenom/TagOptionTest.php @@ -0,0 +1,12 @@ +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) { print_r($e->getMessage() . "\n" . $e->getTraceAsString()); while ($e->getPrevious()) {