Add Tag entity for compilers. Add tag options

This commit is contained in:
Ivan Shalganov 2014-04-17 23:22:50 +04:00
parent 7fa41997b8
commit 0e8880faf9
8 changed files with 281 additions and 160 deletions

View File

@ -17,14 +17,12 @@ use Fenom\Template;
*/ */
class Fenom class Fenom
{ {
const VERSION = '1.5'; const VERSION = '2.0';
/* Actions */ /* Actions */
const INLINE_COMPILER = 1; const INLINE_COMPILER = 1;
const BLOCK_COMPILER = 2; const BLOCK_COMPILER = 5;
const INLINE_FUNCTION = 3; const INLINE_FUNCTION = 2;
const BLOCK_FUNCTION = 4; const BLOCK_FUNCTION = 7;
const MODIFIER = 5;
/* Options */ /* Options */
const DENY_ACCESSOR = 0x8; const DENY_ACCESSOR = 0x8;

View File

@ -83,10 +83,10 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function ifOpen(Tokenizer $tokens, Scope $scope) public static function ifOpen(Tokenizer $tokens, Tag $scope)
{ {
$scope["else"] = false; $scope["else"] = false;
return 'if(' . $scope->tpl->parseExpr($tokens) . ') {'; return 'if(' . $scope->tpl->parseExpr($tokens) . ') {';
@ -97,11 +97,11 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws InvalidUsageException * @throws InvalidUsageException
* @return string * @return string
*/ */
public static function tagElseIf(Tokenizer $tokens, Scope $scope) public static function tagElseIf(Tokenizer $tokens, Tag $scope)
{ {
if ($scope["else"]) { if ($scope["else"]) {
throw new InvalidUsageException('Incorrect use of the tag {elseif}'); throw new InvalidUsageException('Incorrect use of the tag {elseif}');
@ -113,12 +113,10 @@ class Compiler
* Tag {else} * Tag {else}
* *
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @internal param $
* @param Scope $scope
* @return string * @return string
*/ */
public static function tagElse(Tokenizer $tokens, Scope $scope) public static function tagElse(Tokenizer $tokens, Tag $scope)
{ {
$scope["else"] = true; $scope["else"] = true;
return '} else {'; return '} else {';
@ -129,12 +127,12 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws UnexpectedTokenException * @throws UnexpectedTokenException
* @throws InvalidUsageException * @throws InvalidUsageException
* @return string * @return string
*/ */
public static function foreachOpen(Tokenizer $tokens, Scope $scope) public static function foreachOpen(Tokenizer $tokens, Tag $scope)
{ {
$p = array("index" => false, "first" => false, "last" => false); $p = array("index" => false, "first" => false, "last" => false);
$key = null; $key = null;
@ -201,10 +199,10 @@ class Compiler
* Tag {foreachelse} * Tag {foreachelse}
* *
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function foreachElse($tokens, Scope $scope) public static function foreachElse($tokens, Tag $scope)
{ {
$scope["no-break"] = $scope["no-continue"] = $scope["else"] = true; $scope["no-break"] = $scope["no-continue"] = $scope["else"] = true;
return " {$scope['after']} } } else {"; return " {$scope['after']} } } else {";
@ -215,10 +213,10 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function foreachClose($tokens, Scope $scope) public static function foreachClose($tokens, Tag $scope)
{ {
if ($scope["else"]) { if ($scope["else"]) {
return '}'; return '}';
@ -230,12 +228,12 @@ class Compiler
/** /**
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws Error\UnexpectedTokenException * @throws Error\UnexpectedTokenException
* @throws Error\InvalidUsageException * @throws Error\InvalidUsageException
* @return string * @return string
*/ */
public static function forOpen(Tokenizer $tokens, Scope $scope) public static function forOpen(Tokenizer $tokens, Tag $scope)
{ {
$p = array("index" => false, "first" => false, "last" => false, "step" => 1, "to" => false, "max" => false, "min" => false); $p = array("index" => false, "first" => false, "last" => false, "step" => 1, "to" => false, "max" => false, "min" => false);
$scope["after"] = $before = $body = array(); $scope["after"] = $before = $body = array();
@ -291,10 +289,10 @@ class Compiler
/** /**
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function forElse(Tokenizer $tokens, Scope $scope) public static function forElse(Tokenizer $tokens, Tag $scope)
{ {
$scope["no-break"] = $scope["no-continue"] = true; $scope["no-break"] = $scope["no-continue"] = true;
$scope["else"] = true; $scope["else"] = true;
@ -304,10 +302,10 @@ class Compiler
/** /**
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function forClose($tokens, Scope $scope) public static function forClose($tokens, Tag $scope)
{ {
if ($scope["else"]) { if ($scope["else"]) {
return '}'; return '}';
@ -319,10 +317,10 @@ class Compiler
/** /**
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function whileOpen(Tokenizer $tokens, Scope $scope) public static function whileOpen(Tokenizer $tokens, Tag $scope)
{ {
return 'while(' . $scope->tpl->parseExpr($tokens) . ') {'; return 'while(' . $scope->tpl->parseExpr($tokens) . ') {';
} }
@ -332,10 +330,10 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function switchOpen(Tokenizer $tokens, Scope $scope) public static function switchOpen(Tokenizer $tokens, Tag $scope)
{ {
$expr = $scope->tpl->parseExpr($tokens); $expr = $scope->tpl->parseExpr($tokens);
$scope["case"] = array(); $scope["case"] = array();
@ -349,9 +347,9 @@ class Compiler
/** /**
* Resort cases for {switch} * Resort cases for {switch}
* @param Scope $scope * @param Tag $scope
*/ */
private static function _caseResort(Scope $scope) private static function _caseResort(Tag $scope)
{ {
$content = $scope->cutContent(); $content = $scope->cutContent();
if ($scope["last"] === false) { if ($scope["last"] === false) {
@ -372,10 +370,10 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function tagCase(Tokenizer $tokens, Scope $scope) public static function tagCase(Tokenizer $tokens, Tag $scope)
{ {
self::_caseResort($scope); self::_caseResort($scope);
do { do {
@ -395,10 +393,10 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function tagDefault($tokens, Scope $scope) public static function tagDefault($tokens, Tag $scope)
{ {
self::_caseResort($scope); self::_caseResort($scope);
$scope["last"] = false; $scope["last"] = false;
@ -410,10 +408,10 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function switchClose(Tokenizer $tokens, Scope $scope) public static function switchClose(Tokenizer $tokens, Tag $scope)
{ {
self::_caseResort($scope); self::_caseResort($scope);
$expr = $scope["var"]; $expr = $scope["var"];
@ -434,11 +432,11 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws InvalidUsageException * @throws InvalidUsageException
* @return string * @return string
*/ */
public static function tagContinue($tokens, Scope $scope) public static function tagContinue($tokens, Tag $scope)
{ {
if (empty($scope["no-continue"])) { if (empty($scope["no-continue"])) {
return 'continue;'; return 'continue;';
@ -452,11 +450,11 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws InvalidUsageException * @throws InvalidUsageException
* @return string * @return string
*/ */
public static function tagBreak($tokens, Scope $scope) public static function tagBreak($tokens, Tag $scope)
{ {
if (empty($scope["no-break"])) { if (empty($scope["no-break"])) {
return 'break;'; return 'break;';
@ -543,11 +541,11 @@ class Compiler
/** /**
* Tag {block ...} * Tag {block ...}
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws \RuntimeException * @throws \RuntimeException
* @return string * @return string
*/ */
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) public static function tagBlockOpen(Tokenizer $tokens, Tag $scope)
{ {
if ($scope->level > 0) { if ($scope->level > 0) {
$scope->tpl->_compatible = true; $scope->tpl->_compatible = true;
@ -562,9 +560,9 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
*/ */
public static function tagBlockClose($tokens, Scope $scope) public static function tagBlockClose($tokens, Tag $scope)
{ {
$tpl = $scope->tpl; $tpl = $scope->tpl;
$name = $scope["name"]; $name = $scope["name"];
@ -597,10 +595,10 @@ class Compiler
* Tag {parent} * Tag {parent}
* *
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function tagParent($tokens, Scope $scope) public static function tagParent($tokens, Tag $scope)
{ {
$block_scope = $scope->tpl->getParentScope('block'); $block_scope = $scope->tpl->getParentScope('block');
if (!$block_scope['use_parent']) { if (!$block_scope['use_parent']) {
@ -624,32 +622,30 @@ class Compiler
* Standard function parser * Standard function parser
* *
* @static * @static
* @param mixed $function
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Tag $tag * @param Tag $tag
* @return string * @return string
*/ */
public static function stdFuncParser($function, Tokenizer $tokens, Tag $tag) public static function stdFuncParser(Tokenizer $tokens, Tag $tag)
{ {
return "$function(" . self::toArray($tag->tpl->parseParams($tokens)) . ', $tpl)'; return $tag->escape($tag->callback."(" . self::toArray($tag->tpl->parseParams($tokens)) . ', $tpl)');
} }
/** /**
* Smart function parser * Smart function parser
* *
* @static * @static
* @param string $function
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Tag $tag * @param Tag $tag
* @return string * @return string
*/ */
public static function smartFuncParser($function, Tokenizer $tokens, Tag $tag) public static function smartFuncParser(Tokenizer $tokens, Tag $tag)
{ {
if (strpos($function, "::")) { if (strpos($tag->callback, "::")) {
list($class, $method) = explode("::", $function, 2); list($class, $method) = explode("::", $tag->callback, 2);
$ref = new \ReflectionMethod($class, $method); $ref = new \ReflectionMethod($class, $method);
} else { } else {
$ref = new \ReflectionFunction($function); $ref = new \ReflectionFunction($tag->callback);
} }
$args = array(); $args = array();
$params = $tag->tpl->parseParams($tokens); $params = $tag->tpl->parseParams($tokens);
@ -662,7 +658,7 @@ class Compiler
$args[] = var_export($param->getDefaultValue(), true); $args[] = var_export($param->getDefaultValue(), true);
} }
} }
return "$function(" . implode(", ", $args) . ')'; return $tag->escape($tag->callback."(" . implode(", ", $args) . ')');
} }
/** /**
@ -670,12 +666,12 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $tag
* @return string * @return string
*/ */
public static function stdFuncOpen(Tokenizer $tokens, Scope $scope) public static function stdFuncOpen(Tokenizer $tokens, Tag $tag)
{ {
$scope["params"] = self::toArray($scope->tpl->parseParams($tokens)); $tag["params"] = self::toArray($tag->tpl->parseParams($tokens));
return 'ob_start();'; return 'ob_start();';
} }
@ -684,12 +680,12 @@ class Compiler
* *
* @static * @static
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $tag
* @return string * @return string
*/ */
public static function stdFuncClose($tokens, Scope $scope) public static function stdFuncClose($tokens, Tag $tag)
{ {
return $scope["function"] . '(' . $scope["params"] . ', ob_get_clean(), $tpl)'; return $tag->escape($tag->callback . '(' . $tag["params"] . ', ob_get_clean(), $tpl)');
} }
/** /**
@ -709,14 +705,14 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function varOpen(Tokenizer $tokens, Scope $scope) public static function varOpen(Tokenizer $tokens, Tag $scope)
{ {
$var = $scope->tpl->parseVariable($tokens); $var = $scope->tpl->parseVariable($tokens);
if ($tokens->is('=')) { // inline tag {var ...} if ($tokens->is('=')) { // inline tag {var ...}
$scope->is_closed = true; $scope->close();
$tokens->next(); $tokens->next();
if ($tokens->is("[")) { if ($tokens->is("[")) {
return $var . '=' . $scope->tpl->parseArray($tokens); return $var . '=' . $scope->tpl->parseArray($tokens);
@ -736,10 +732,10 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function varClose(Tokenizer $tokens, Scope $scope) public static function varClose(Tokenizer $tokens, Tag $scope)
{ {
return $scope["name"] . '=' . $scope["value"] . ';'; return $scope["name"] . '=' . $scope["value"] . ';';
} }
@ -747,10 +743,10 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function filterOpen(Tokenizer $tokens, Scope $scope) public static function filterOpen(Tokenizer $tokens, Tag $scope)
{ {
$scope["filter"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()"); $scope["filter"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()");
return "ob_start();"; return "ob_start();";
@ -758,10 +754,10 @@ class Compiler
/** /**
* @param $tokens * @param $tokens
* @param Scope $scope * @param Tag $scope
* @return string * @return string
*/ */
public static function filterClose($tokens, Scope $scope) public static function filterClose($tokens, Tag $scope)
{ {
return "echo " . $scope["filter"] . ";"; return "echo " . $scope["filter"] . ";";
} }
@ -877,10 +873,10 @@ class Compiler
* Define macro * Define macro
* *
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
* @throws InvalidUsageException * @throws InvalidUsageException
*/ */
public static function macroOpen(Tokenizer $tokens, Scope $scope) public static function macroOpen(Tokenizer $tokens, Tag $scope)
{ {
$scope["name"] = $tokens->get(Tokenizer::MACRO_STRING); $scope["name"] = $tokens->get(Tokenizer::MACRO_STRING);
$scope["recursive"] = false; $scope["recursive"] = false;
@ -923,9 +919,9 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
*/ */
public static function macroClose(Tokenizer $tokens, Scope $scope) public static function macroClose(Tokenizer $tokens, Tag $scope)
{ {
if ($scope["recursive"]) { if ($scope["recursive"]) {
$scope["macro"]["recursive"] = true; $scope["macro"]["recursive"] = true;
@ -943,19 +939,14 @@ class Compiler
*/ */
public static function tagRaw(Tokenizer $tokens, Tag $tag) public static function tagRaw(Tokenizer $tokens, Tag $tag)
{ {
$tpl = $tag->tpl; return 'echo '.$tag->tpl->parseExpr($tokens);
$escape = (bool)$tpl->escape;
$tpl->escape = false;
$code = $tpl->out($tpl->parseExpr($tokens));
$tpl->escape = $escape;
return $code;
} }
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
*/ */
public static function autoescapeOpen(Tokenizer $tokens, Scope $scope) public static function autoescapeOpen(Tokenizer $tokens, Tag $scope)
{ {
$boolean = ($tokens->get(T_STRING) == "true" ? true : false); $boolean = ($tokens->get(T_STRING) == "true" ? true : false);
$scope["escape"] = $scope->tpl->escape; $scope["escape"] = $scope->tpl->escape;
@ -965,9 +956,9 @@ class Compiler
/** /**
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param Scope $scope * @param Tag $scope
*/ */
public static function autoescapeClose(Tokenizer $tokens, Scope $scope) public static function autoescapeClose(Tokenizer $tokens, Tag $scope)
{ {
$scope->tpl->escape = $scope["escape"]; $scope->tpl->escape = $scope["escape"];
} }

View File

@ -10,7 +10,10 @@
namespace Fenom; namespace Fenom;
class Tag { class Tag extends \ArrayObject {
const COMPILER = 1;
const FUNC = 2;
const BLOCK = 4;
/** /**
* @var Template * @var Template
@ -18,10 +21,161 @@ class Tag {
public $tpl; public $tpl;
public $name; public $name;
public $options; public $options;
public $line = 0;
public $level = 0;
public $callback;
public function __construct($name, Template $tpl) { private $_offset = 0;
$this->name = $name; private $_closed = true;
private $_body;
private $_type = 0;
private $_open;
private $_close;
private $_tags = array();
private $_floats = array();
public function __construct($name, Template $tpl, $info, &$body)
{
$this->tpl = $tpl; $this->tpl = $tpl;
$this->name = $name;
$this->line = $tpl->getLine();
$this->level = $tpl->getStackSize();
$this->_body = &$body;
$this->_offset = strlen($body);
$this->_type = $info["type"];
if($this->_type & self::BLOCK) {
$this->_open = $info["open"];
$this->_close = $info["close"];
$this->_tags = isset($info["tags"]) ? $info["tags"] : array();
$this->_floats = isset($info["float_tags"]) ? $info["float_tags"] : array();
$this->_closed = false;
} else {
$this->_open = $info["parser"];
}
if($this->_type & self::FUNC) {
$this->callback = $info["function"];
}
}
public function setOption($option) {
}
public function isClosed() {
return $this->_closed;
}
/**
* Open callback
*
* @param Tokenizer $tokenizer
* @return mixed
*/
public function start($tokenizer)
{
return call_user_func($this->_open, $tokenizer, $this);
}
/**
* Check, has the block this tag
*
* @param string $tag
* @param int $level
* @return bool
*/
public function hasTag($tag, $level)
{
if (isset($this->_tags[$tag])) {
if ($level) {
return isset($this->_floats[$tag]);
} else {
return true;
}
}
return false;
}
/**
* Call tag callback
*
* @param string $tag
* @param Tokenizer $tokenizer
* @throws \LogicException
* @return string
*/
public function tag($tag, $tokenizer)
{
if (isset($this->_tags[$tag])) {
return call_user_func($this->_tags[$tag], $tokenizer, $this);
} else {
throw new \LogicException("The block tag {$this->name} no have tag {$tag}");
}
}
/**
* Close callback
*
* @param Tokenizer $tokenizer
* @throws \LogicException
* @return string
*/
public function end($tokenizer)
{
if($this->_closed) {
throw new \LogicException("Tag {$this->name} already closed");
}
if($this->_close) {
return call_user_func($this->_close, $tokenizer, $this);
} else {
throw new \LogicException("Сan not use a inline tag {$this->name} as a block");
}
}
public function close() {
$this->_closed = true;
}
/**
* Return content of block
*
* @throws \LogicException
* @return string
*/
public function getContent()
{
return substr($this->_body, $this->_offset);
}
/**
* Cut scope content
*
* @return string
* @throws \LogicException
*/
public function cutContent()
{
$content = substr($this->_body, $this->_offset + 1);
$this->_body = substr($this->_body, 0, $this->_offset);
return $content;
}
/**
* Replace scope content
*
* @param $new_content
*/
public function replaceContent($new_content)
{
$this->cutContent();
$this->_body .= $new_content;
}
public function escape($code) {
return $this->tpl->out($code);
} }
public function optLtrim() { public function optLtrim() {

View File

@ -34,7 +34,6 @@ class Template extends Render
* Disable modifier parser. * Disable modifier parser.
*/ */
const DENY_MODS = 2; const DENY_MODS = 2;
/** /**
* @var int shared counter * @var int shared counter
*/ */
@ -81,7 +80,7 @@ class Template extends Render
/** /**
* Call stack * Call stack
* @var Scope[] * @var Tag[]
*/ */
private $_stack = array(); private $_stack = array();
@ -442,7 +441,7 @@ class Template extends Render
} }
/** /**
* Add depends from template * Add depends
* @param Render $tpl * @param Render $tpl
*/ */
public function addDepend(Render $tpl) public function addDepend(Render $tpl)
@ -530,7 +529,7 @@ class Template extends Render
} elseif ($tokens->is('/')) { } elseif ($tokens->is('/')) {
return $this->parseEndTag($tokens); return $this->parseEndTag($tokens);
} else { } else {
return $this->out($this->parseExpr($tokens), $tokens); return $this->out($this->parseExpr($tokens));
} }
} catch (InvalidUsageException $e) { } catch (InvalidUsageException $e) {
throw new CompileException($e->getMessage() . " in {$this->_name} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e); throw new CompileException($e->getMessage() . " in {$this->_name} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
@ -555,18 +554,12 @@ class Template extends Render
if (!$this->_stack) { if (!$this->_stack) {
throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened"); throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened");
} }
/** @var Scope $scope */ /** @var Tag $tag */
$scope = array_pop($this->_stack); $tag = array_pop($this->_stack);
if ($scope->name !== $name) { if ($tag->name !== $name) {
throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened in line {$scope->line})"); throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$tag->name}, opened in line {$tag->line})");
}
if ($scope->is_compiler) {
return $scope->close($tokens);
} else {
$code = $this->out($scope->close($tokens));
$scope->tpl->escape = $scope->escape; // restore escape option
return $code;
} }
return $tag->end($tokens);
} }
/** /**
@ -581,63 +574,30 @@ class Template extends Render
*/ */
public function parseAct(Tokenizer $tokens) public function parseAct(Tokenizer $tokens)
{ {
$options = array(); $action = $tokens->get(Tokenizer::MACRO_STRING);
if ($tokens->is(Tokenizer::MACRO_STRING)) { $tokens->next();
$action = $tokens->getAndNext(); if ($tokens->is("(", T_DOUBLE_COLON, T_NS_SEPARATOR) && !$tokens->isWhiteSpaced()) { // just invoke function or static method
} else {
return $this->out($this->parseExpr($tokens)); // may be math and/or boolean expression
}
if ($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON) && !$tokens->isWhiteSpaced()) { // just invoke function or static method
$tokens->back(); $tokens->back();
return $this->out($this->parseExpr($tokens)); return $this->out($this->parseExpr($tokens));
} } elseif ($tokens->is('.')) {
if ($tokens->is('.')) {
$name = $tokens->skip()->get(Tokenizer::MACRO_STRING); $name = $tokens->skip()->get(Tokenizer::MACRO_STRING);
if ($action !== "macro") { if ($action !== "macro") {
$name = $action . "." . $name; $name = $action . "." . $name;
} }
return $this->parseMacroCall($tokens, $name); return $this->parseMacroCall($tokens, $name);
} elseif ($tokens->is(T_DOUBLE_COLON, T_NS_SEPARATOR)) { // static method call
$tokens->back();
$p = $tokens->p;
$static = $this->parseStatic($tokens);
if ($tokens->is("(")) {
return $this->out($this->parseExpr($tokens->seek($p)));
} else {
return $this->out(Compiler::smartFuncParser($static, $tokens, new Tag($static, $this)));
} }
} elseif($tokens->is(':')) { // parse tag options if ($info = $this->_fenom->getTag($action, $this)) {
$tag = new Tag($action, $this, $info, $this->_body);
if($tokens->is(':')) { // parse tag options
do { do {
$options[ $tokens->next()->need(T_STRING)->getAndNext() ] = true; $tag->setOption($tokens->next()->need(T_STRING)->getAndNext());
} while($tokens->is(':')); } while($tokens->is(':'));
} }
$code = $tag->start($tokens);
if ($tag = $this->_fenom->getTag($action, $this)) { if (!$tag->isClosed()) {
if($tag["type"] == Fenom::BLOCK_COMPILER || $tag["type"] == Fenom::BLOCK_FUNCTION) { array_push($this->_stack, $tag);
$scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body);
} else {
$scope = new Tag($action, $this);
}
$scope->options = $options;
switch ($tag["type"]) {
case Fenom::BLOCK_COMPILER:
$code = $scope->open($tokens);
if (!$scope->is_closed) {
array_push($this->_stack, $scope);
} }
return $code; return $code;
case Fenom::INLINE_COMPILER:
return call_user_func($tag["parser"], $tokens, $scope);
case Fenom::INLINE_FUNCTION:
return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $scope));
case Fenom::BLOCK_FUNCTION:
$scope->setFuncName($tag["function"]);
array_push($this->_stack, $scope);
return $scope->open($tokens);
default:
throw new \LogicException("Unknown function type");
}
} }
for ($j = $i = count($this->_stack) - 1; $i >= 0; $i--) { // call function's internal tag for ($j = $i = count($this->_stack) - 1; $i >= 0; $i--) { // call function's internal tag
@ -652,6 +612,14 @@ class Template extends Render
} }
} }
/**
* Get current template line
* @return int
*/
public function getLine() {
return $this->_line;
}
/** /**
* Parse expressions. The mix of operators and terms. * Parse expressions. The mix of operators and terms.
* *
@ -836,7 +804,7 @@ class Template extends Render
} }
/** /**
* Parse variable name: $a, $a.b, $a.b[c] * Parse variable name: $a, $a.b, $a.b['c']
* @param Tokenizer $tokens * @param Tokenizer $tokens
* @param $var * @param $var
* @return string * @return string
@ -1339,7 +1307,7 @@ class Template extends Render
} }
} }
if ($recursive) { if ($recursive) {
if($recursive instanceof Scope) { if($recursive instanceof Tag) {
$recursive['recursive'] = true; $recursive['recursive'] = true;
} }
return '$tpl->getMacro("' . $name . '")->__invoke('.Compiler::toArray($args).', $tpl);'; return '$tpl->getMacro("' . $name . '")->__invoke('.Compiler::toArray($args).', $tpl);';

View File

@ -37,6 +37,9 @@ class FunctionsTest extends TestCase
$this->tpl('function_array_param_pos.tpl', '{sum [1, 2, 3, 4, 5]}'); $this->tpl('function_array_param_pos.tpl', '{sum [1, 2, 3, 4, 5]}');
} }
/**
* @group sb
*/
public function testFunctionWithParams() public function testFunctionWithParams()
{ {
$output = $this->fenom->fetch('function_params_scalar.tpl'); $output = $this->fenom->fetch('function_params_scalar.tpl');

View File

@ -65,8 +65,12 @@ class MacrosTest extends TestCase
// $this->fenom->compile("macro_recursive.tpl")->display([]); // $this->fenom->compile("macro_recursive.tpl")->display([]);
// $this->fenom->flush(); // $this->fenom->flush();
// var_dump($this->fenom->fetch("macro_recursive.tpl", [])); // var_dump($this->fenom->fetch("macro_recursive.tpl", []));
var_dump($this->fenom->compile("macro_recursive_import.tpl")->display(array())); var_dump($this->fenom->compileCode('{macro factorial(num)}
var_dump($this->fenom->display("macro_recursive_import.tpl", array())); {if $num}
{$num} {macro.factorial num=$num-1} {$num}
{/if}
{/macro}')->getBody());
// var_dump($this->fenom->display("macro_recursive_import.tpl", array()));
} catch (\Exception $e) { } catch (\Exception $e) {
var_dump($e->getMessage() . ": " . $e->getTraceAsString()); var_dump($e->getMessage() . ": " . $e->getTraceAsString());
} }
@ -109,6 +113,9 @@ class MacrosTest extends TestCase
$this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true)); $this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true));
} }
/**
* @group macro-recursive
*/
public function testRecursive() public function testRecursive()
{ {
$this->fenom->compile('macro_recursive.tpl'); $this->fenom->compile('macro_recursive.tpl');

View File

@ -823,7 +823,7 @@ class TemplateTest extends TestCase
public function providerStatic() public function providerStatic()
{ {
return array( return array(
array('{Fenom\TemplateTest::multi x=3 y=4}', '12'), // array('{Fenom\TemplateTest::multi x=3 y=4}', '12'),
array('{Fenom\TemplateTest::multi(3,4)}', '12'), array('{Fenom\TemplateTest::multi(3,4)}', '12'),
array('{12 + Fenom\TemplateTest::multi(3,4)}', '24'), array('{12 + Fenom\TemplateTest::multi(3,4)}', '24'),
array('{12 + 3|Fenom\TemplateTest::multi:4}', '24'), array('{12 + 3|Fenom\TemplateTest::multi:4}', '24'),
@ -848,7 +848,7 @@ class TemplateTest extends TestCase
public function _testSandbox() public function _testSandbox()
{ {
try { try {
var_dump($this->fenom->compileCode('{var:ignore $a} value {/var}')->getBody()); var_dump($this->fenom->compileCode('{$a}')->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()) {

View File

@ -21,18 +21,18 @@ function myCompiler(Fenom\Tokenizer $tokenizer, Fenom\Tag $tag)
return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";'; return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";';
} }
function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Tag $scope)
{ {
$p = $scope->tpl->parseParams($tokenizer); $p = $scope->tpl->parseParams($tokenizer);
return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";'; return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";';
} }
function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Tag $scope)
{ {
return 'echo "End of compiler";'; return 'echo "End of compiler";';
} }
function myBlockCompilerTag(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) function myBlockCompilerTag(Fenom\Tokenizer $tokenizer, Fenom\Tag $scope)
{ {
$p = $scope->tpl->parseParams($tokenizer); $p = $scope->tpl->parseParams($tokenizer);
return 'echo "Tag ".' . $p["name"] . '." of compiler";'; return 'echo "Tag ".' . $p["name"] . '." of compiler";';