mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Add {autoescape} block tag and {raw} inline pseudo tag. Improve auto escaping
This commit is contained in:
parent
b99b874d38
commit
42b71ed644
|
@ -33,6 +33,7 @@ class Fenom {
|
||||||
const FORCE_COMPILE = 0xF0;
|
const FORCE_COMPILE = 0xF0;
|
||||||
const DISABLE_CACHE = 0x1F0;
|
const DISABLE_CACHE = 0x1F0;
|
||||||
const AUTO_ESCAPE = 0x200;
|
const AUTO_ESCAPE = 0x200;
|
||||||
|
const FORCE_VALIDATE = 0x400;
|
||||||
|
|
||||||
/* Default parsers */
|
/* Default parsers */
|
||||||
const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose';
|
const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose';
|
||||||
|
@ -43,7 +44,7 @@ class Fenom {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int[] of possible options, as associative array
|
* @var int[] of possible options, as associative array
|
||||||
* @see setOptions, addOptions, delOptions
|
* @see setOptions
|
||||||
*/
|
*/
|
||||||
private static $_option_list = array(
|
private static $_option_list = array(
|
||||||
"disable_methods" => self::DENY_METHODS,
|
"disable_methods" => self::DENY_METHODS,
|
||||||
|
@ -52,6 +53,8 @@ class Fenom {
|
||||||
"force_compile" => self::FORCE_COMPILE,
|
"force_compile" => self::FORCE_COMPILE,
|
||||||
"auto_reload" => self::AUTO_RELOAD,
|
"auto_reload" => self::AUTO_RELOAD,
|
||||||
"force_include" => self::FORCE_INCLUDE,
|
"force_include" => self::FORCE_INCLUDE,
|
||||||
|
"auto_escape" => self::AUTO_ESCAPE,
|
||||||
|
"force_validate" => self::FORCE_VALIDATE
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,6 +216,15 @@ class Fenom {
|
||||||
'cycle' => array(
|
'cycle' => array(
|
||||||
'type' => self::INLINE_COMPILER,
|
'type' => self::INLINE_COMPILER,
|
||||||
'parser' => 'Fenom\Compiler::tagCycle'
|
'parser' => 'Fenom\Compiler::tagCycle'
|
||||||
|
),
|
||||||
|
'raw' => array(
|
||||||
|
'type' => self::INLINE_COMPILER,
|
||||||
|
'parser' => 'Fenom\Compiler::tagRaw'
|
||||||
|
),
|
||||||
|
'autoescape' => array(
|
||||||
|
'type' => self::BLOCK_COMPILER,
|
||||||
|
'open' => 'Fenom\Compiler::autoescapeOpen',
|
||||||
|
'close' => 'Fenom\Compiler::autoescapeClose'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -234,7 +246,6 @@ class Fenom {
|
||||||
throw new InvalidArgumentException("Source must be a valid path or provider object");
|
throw new InvalidArgumentException("Source must be a valid path or provider object");
|
||||||
}
|
}
|
||||||
$fenom = new static($provider);
|
$fenom = new static($provider);
|
||||||
/* @var Fenom $fytro */
|
|
||||||
$fenom->setCompileDir($compile_dir);
|
$fenom->setCompileDir($compile_dir);
|
||||||
if($options) {
|
if($options) {
|
||||||
$fenom->setOptions($options);
|
$fenom->setOptions($options);
|
||||||
|
|
|
@ -588,7 +588,7 @@ class Compiler {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) {
|
public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) {
|
||||||
return "echo $function(".self::toArray($tpl->parseParams($tokens)).', $tpl);';
|
return "$function(".self::toArray($tpl->parseParams($tokens)).', $tpl)';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -618,7 +618,7 @@ class Compiler {
|
||||||
$args[] = $param->getDefaultValue();
|
$args[] = $param->getDefaultValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "echo $function(".implode(", ", $args).');';
|
return "$function(".implode(", ", $args).')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -643,7 +643,7 @@ class Compiler {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function stdFuncClose($tokens, Scope $scope) {
|
public static function stdFuncClose($tokens, Scope $scope) {
|
||||||
return "echo ".$scope["function"].'('.$scope["params"].', ob_get_clean(), $tpl);';
|
return $scope["function"].'('.$scope["params"].', ob_get_clean(), $tpl)';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -866,4 +866,48 @@ class Compiler {
|
||||||
$scope->tpl->_body = substr($scope->tpl->_body, 0, strlen($scope->tpl->_body) - strlen($content));
|
$scope->tpl->_body = substr($scope->tpl->_body, 0, strlen($scope->tpl->_body) - strlen($content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output value as is, without escaping
|
||||||
|
*
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Template $tpl
|
||||||
|
* @throws InvalidUsageException
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function tagRaw(Tokenizer $tokens, Template $tpl) {
|
||||||
|
$tpl->escape = false;
|
||||||
|
if($tokens->is(':')) {
|
||||||
|
$func = $tokens->getNext(Tokenizer::MACRO_STRING);
|
||||||
|
$tag = $tpl->getStorage()->getFunction($func);
|
||||||
|
if($tag["type"] == \Fenom::INLINE_FUNCTION) {
|
||||||
|
return $tpl->parseAct($tokens);
|
||||||
|
} elseif ($tag["type"] == \Fenom::BLOCK_FUNCTION) {
|
||||||
|
$code = $tpl->parseAct($tokens);
|
||||||
|
$tpl->getLastScope()->escape = false;
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
throw new InvalidUsageException("Raw mode allow for expressions or functions");
|
||||||
|
} else {
|
||||||
|
return $tpl->out($tpl->parseExp($tokens, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Scope $scope
|
||||||
|
*/
|
||||||
|
public static function autoescapeOpen(Tokenizer $tokens, Scope $scope) {
|
||||||
|
$boolean = ($tokens->get(T_STRING) == "true" ? true : false);
|
||||||
|
$scope["escape"] = $scope->tpl->escape;
|
||||||
|
$scope->tpl->escape = $boolean;
|
||||||
|
$tokens->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Scope $scope
|
||||||
|
*/
|
||||||
|
public static function autoescapeClose(Tokenizer $tokens, Scope $scope) {
|
||||||
|
$scope->tpl->escape = $scope["escape"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,11 @@ class Scope extends \ArrayObject {
|
||||||
public $tpl;
|
public $tpl;
|
||||||
public $is_compiler = true;
|
public $is_compiler = true;
|
||||||
public $is_closed = false;
|
public $is_closed = false;
|
||||||
|
public $escape = false;
|
||||||
private $_action;
|
private $_action;
|
||||||
private $_body;
|
private $_body;
|
||||||
private $_offset;
|
private $_offset;
|
||||||
|
private $_global_escape = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creating cope
|
* Creating cope
|
||||||
|
@ -56,6 +58,8 @@ class Scope extends \ArrayObject {
|
||||||
public function setFuncName($function) {
|
public function setFuncName($function) {
|
||||||
$this["function"] = $function;
|
$this["function"] = $function;
|
||||||
$this->is_compiler = false;
|
$this->is_compiler = false;
|
||||||
|
$this->_global_escape = $this->tpl->escape;
|
||||||
|
$this->tpl->escape = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,6 +108,9 @@ class Scope extends \ArrayObject {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function close($tokenizer) {
|
public function close($tokenizer) {
|
||||||
|
if(!$this->is_compiler) {
|
||||||
|
$this->tpl->escape = $this->_global_escape;
|
||||||
|
}
|
||||||
return call_user_func($this->_action["close"], $tokenizer, $this);
|
return call_user_func($this->_action["close"], $tokenizer, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,11 @@ class Template extends Render {
|
||||||
|
|
||||||
public $parents = array();
|
public $parents = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape output value
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $escape = false;
|
||||||
public $_extends;
|
public $_extends;
|
||||||
public $_extended = false;
|
public $_extended = false;
|
||||||
public $_compatible;
|
public $_compatible;
|
||||||
|
@ -159,6 +164,7 @@ class Template extends Render {
|
||||||
*/
|
*/
|
||||||
public function compile() {
|
public function compile() {
|
||||||
$end = $pos = 0;
|
$end = $pos = 0;
|
||||||
|
$this->escape = $this->_options & Fenom::AUTO_ESCAPE;
|
||||||
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags
|
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags
|
||||||
switch($this->_src[$start + 1]) { // check next character
|
switch($this->_src[$start + 1]) { // check next character
|
||||||
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
||||||
|
@ -397,11 +403,11 @@ class Template extends Render {
|
||||||
* @param $data
|
* @param $data
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function _print($data) {
|
public function out($data) {
|
||||||
if($this->_options & Fenom::AUTO_ESCAPE) {
|
if($this->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;";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -420,14 +426,14 @@ class Template extends Render {
|
||||||
$tokens->next();
|
$tokens->next();
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
return $this->_parseAct($tokens);
|
return $this->parseAct($tokens);
|
||||||
}
|
}
|
||||||
} elseif ($tokens->is('/')) {
|
} elseif ($tokens->is('/')) {
|
||||||
return $this->_end($tokens);
|
return $this->_end($tokens);
|
||||||
} elseif ($tokens->is('#')) {
|
} elseif ($tokens->is('#')) {
|
||||||
return $this->_print($this->parseConst($tokens), $tokens).';';
|
return $this->out($this->parseConst($tokens), $tokens).';';
|
||||||
} else {
|
} else {
|
||||||
return $code = $this->_print($this->parseExp($tokens), $tokens).";";
|
return $code = $this->out($this->parseExp($tokens), $tokens).";";
|
||||||
}
|
}
|
||||||
} catch (InvalidUsageException $e) {
|
} catch (InvalidUsageException $e) {
|
||||||
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
|
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||||
|
@ -442,7 +448,7 @@ class Template extends Render {
|
||||||
* Close tag handler
|
* Close tag handler
|
||||||
*
|
*
|
||||||
* @param Tokenizer $tokens
|
* @param Tokenizer $tokens
|
||||||
* @return mixed
|
* @return string
|
||||||
* @throws TokenizeException
|
* @throws TokenizeException
|
||||||
*/
|
*/
|
||||||
private function _end(Tokenizer $tokens) {
|
private function _end(Tokenizer $tokens) {
|
||||||
|
@ -457,7 +463,20 @@ class Template extends Render {
|
||||||
if($scope->name !== $name) {
|
if($scope->name !== $name) {
|
||||||
throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})");
|
throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})");
|
||||||
}
|
}
|
||||||
return $scope->close($tokens);
|
if($scope->is_compiler) {
|
||||||
|
return $scope->close($tokens);
|
||||||
|
} else {
|
||||||
|
$scope->tpl->escape = $scope->escape;
|
||||||
|
return $this->out($scope->close($tokens));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current scope
|
||||||
|
* @return Scope
|
||||||
|
*/
|
||||||
|
public function getLastScope() {
|
||||||
|
return end($this->_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -469,16 +488,16 @@ class Template extends Render {
|
||||||
* @throws TokenizeException
|
* @throws TokenizeException
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function _parseAct(Tokenizer $tokens) {
|
public function parseAct(Tokenizer $tokens) {
|
||||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||||
$action = $tokens->getAndNext();
|
$action = $tokens->getAndNext();
|
||||||
} else {
|
} else {
|
||||||
return $this->_print($this->parseExp($tokens), $tokens).';'; // may be math and/or boolean expression
|
return $this->out($this->parseExp($tokens), $tokens).';'; // may be math and/or boolean expression
|
||||||
}
|
}
|
||||||
|
|
||||||
if($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON)) { // just invoke function or static method
|
if($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON)) { // just invoke function or static method
|
||||||
$tokens->back();
|
$tokens->back();
|
||||||
return $this->_print($this->parseExp($tokens), $tokens).";";
|
return $this->out($this->parseExp($tokens), $tokens).";";
|
||||||
} elseif($tokens->is('.')) {
|
} elseif($tokens->is('.')) {
|
||||||
$name = $tokens->skip()->get(Tokenizer::MACRO_STRING);
|
$name = $tokens->skip()->get(Tokenizer::MACRO_STRING);
|
||||||
if($action !== "macro") {
|
if($action !== "macro") {
|
||||||
|
@ -499,11 +518,12 @@ class Template extends Render {
|
||||||
case Fenom::INLINE_COMPILER:
|
case Fenom::INLINE_COMPILER:
|
||||||
return call_user_func($act["parser"], $tokens, $this);
|
return call_user_func($act["parser"], $tokens, $this);
|
||||||
case Fenom::INLINE_FUNCTION:
|
case Fenom::INLINE_FUNCTION:
|
||||||
return call_user_func($act["parser"], $act["function"], $tokens, $this);
|
return $this->out(call_user_func($act["parser"], $act["function"], $tokens, $this));
|
||||||
case Fenom::BLOCK_FUNCTION:
|
case Fenom::BLOCK_FUNCTION:
|
||||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
||||||
$scope->setFuncName($act["function"]);
|
$scope->setFuncName($act["function"]);
|
||||||
array_push($this->_stack, $scope);
|
array_push($this->_stack, $scope);
|
||||||
|
$scope->escape = $this->_options & Fenom::AUTO_ESCAPE;
|
||||||
return $scope->open($tokens);
|
return $scope->open($tokens);
|
||||||
default:
|
default:
|
||||||
throw new \LogicException("Unknown function type");
|
throw new \LogicException("Unknown function type");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user