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 DISABLE_CACHE = 0x1F0;
|
||||
const AUTO_ESCAPE = 0x200;
|
||||
const FORCE_VALIDATE = 0x400;
|
||||
|
||||
/* Default parsers */
|
||||
const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose';
|
||||
@ -43,7 +44,7 @@ class Fenom {
|
||||
|
||||
/**
|
||||
* @var int[] of possible options, as associative array
|
||||
* @see setOptions, addOptions, delOptions
|
||||
* @see setOptions
|
||||
*/
|
||||
private static $_option_list = array(
|
||||
"disable_methods" => self::DENY_METHODS,
|
||||
@ -52,6 +53,8 @@ class Fenom {
|
||||
"force_compile" => self::FORCE_COMPILE,
|
||||
"auto_reload" => self::AUTO_RELOAD,
|
||||
"force_include" => self::FORCE_INCLUDE,
|
||||
"auto_escape" => self::AUTO_ESCAPE,
|
||||
"force_validate" => self::FORCE_VALIDATE
|
||||
);
|
||||
|
||||
/**
|
||||
@ -213,6 +216,15 @@ class Fenom {
|
||||
'cycle' => array(
|
||||
'type' => self::INLINE_COMPILER,
|
||||
'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");
|
||||
}
|
||||
$fenom = new static($provider);
|
||||
/* @var Fenom $fytro */
|
||||
$fenom->setCompileDir($compile_dir);
|
||||
if($options) {
|
||||
$fenom->setOptions($options);
|
||||
|
@ -588,7 +588,7 @@ class Compiler {
|
||||
* @return string
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
return "echo $function(".implode(", ", $args).');';
|
||||
return "$function(".implode(", ", $args).')';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -643,7 +643,7 @@ class Compiler {
|
||||
* @return string
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $is_compiler = true;
|
||||
public $is_closed = false;
|
||||
public $escape = false;
|
||||
private $_action;
|
||||
private $_body;
|
||||
private $_offset;
|
||||
private $_global_escape = false;
|
||||
|
||||
/**
|
||||
* Creating cope
|
||||
@ -56,6 +58,8 @@ class Scope extends \ArrayObject {
|
||||
public function setFuncName($function) {
|
||||
$this["function"] = $function;
|
||||
$this->is_compiler = false;
|
||||
$this->_global_escape = $this->tpl->escape;
|
||||
$this->tpl->escape = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,6 +108,9 @@ class Scope extends \ArrayObject {
|
||||
* @return string
|
||||
*/
|
||||
public function close($tokenizer) {
|
||||
if(!$this->is_compiler) {
|
||||
$this->tpl->escape = $this->_global_escape;
|
||||
}
|
||||
return call_user_func($this->_action["close"], $tokenizer, $this);
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,11 @@ class Template extends Render {
|
||||
|
||||
public $parents = array();
|
||||
|
||||
/**
|
||||
* Escape output value
|
||||
* @var bool
|
||||
*/
|
||||
public $escape = false;
|
||||
public $_extends;
|
||||
public $_extended = false;
|
||||
public $_compatible;
|
||||
@ -159,6 +164,7 @@ class Template extends Render {
|
||||
*/
|
||||
public function compile() {
|
||||
$end = $pos = 0;
|
||||
$this->escape = $this->_options & Fenom::AUTO_ESCAPE;
|
||||
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags
|
||||
switch($this->_src[$start + 1]) { // check next character
|
||||
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
||||
@ -397,11 +403,11 @@ class Template extends Render {
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
private function _print($data) {
|
||||
if($this->_options & Fenom::AUTO_ESCAPE) {
|
||||
return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8')";
|
||||
public function out($data) {
|
||||
if($this->escape) {
|
||||
return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8');";
|
||||
} else {
|
||||
return "echo $data";
|
||||
return "echo $data;";
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -420,14 +426,14 @@ class Template extends Render {
|
||||
$tokens->next();
|
||||
return '';
|
||||
} else {
|
||||
return $this->_parseAct($tokens);
|
||||
return $this->parseAct($tokens);
|
||||
}
|
||||
} elseif ($tokens->is('/')) {
|
||||
return $this->_end($tokens);
|
||||
} elseif ($tokens->is('#')) {
|
||||
return $this->_print($this->parseConst($tokens), $tokens).';';
|
||||
return $this->out($this->parseConst($tokens), $tokens).';';
|
||||
} else {
|
||||
return $code = $this->_print($this->parseExp($tokens), $tokens).";";
|
||||
return $code = $this->out($this->parseExp($tokens), $tokens).";";
|
||||
}
|
||||
} catch (InvalidUsageException $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
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @return mixed
|
||||
* @return string
|
||||
* @throws TokenizeException
|
||||
*/
|
||||
private function _end(Tokenizer $tokens) {
|
||||
@ -457,7 +463,20 @@ class Template extends Render {
|
||||
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})");
|
||||
}
|
||||
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
|
||||
* @return string
|
||||
*/
|
||||
private function _parseAct(Tokenizer $tokens) {
|
||||
public function parseAct(Tokenizer $tokens) {
|
||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$action = $tokens->getAndNext();
|
||||
} 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
|
||||
$tokens->back();
|
||||
return $this->_print($this->parseExp($tokens), $tokens).";";
|
||||
return $this->out($this->parseExp($tokens), $tokens).";";
|
||||
} elseif($tokens->is('.')) {
|
||||
$name = $tokens->skip()->get(Tokenizer::MACRO_STRING);
|
||||
if($action !== "macro") {
|
||||
@ -499,11 +518,12 @@ class Template extends Render {
|
||||
case Fenom::INLINE_COMPILER:
|
||||
return call_user_func($act["parser"], $tokens, $this);
|
||||
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:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
||||
$scope->setFuncName($act["function"]);
|
||||
array_push($this->_stack, $scope);
|
||||
$scope->escape = $this->_options & Fenom::AUTO_ESCAPE;
|
||||
return $scope->open($tokens);
|
||||
default:
|
||||
throw new \LogicException("Unknown function type");
|
||||
|
Loading…
Reference in New Issue
Block a user