diff --git a/docs/tags/macro.md b/docs/tags/macro.md index c5708b8..b6d3a05 100644 --- a/docs/tags/macro.md +++ b/docs/tags/macro.md @@ -23,9 +23,9 @@ Tag {macro} [RU] Во время рекурсивного вызова используйте суффикс macro что бы обратиться к текущему макросу: ```smarty -{macro plus(x, y, z=0)} +{macro plus($x, $y, $z=0)} ... - {macro.plus x=2 y=4} + {macro.plus x=2 y=$y} ... {/macro} ``` diff --git a/src/Fenom.php b/src/Fenom.php index 0edfbbd..57d6bf3 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -259,6 +259,39 @@ class Fenom ) ); + /** + * List of tests + * @see https://github.com/bzick/fenom/blob/develop/docs/operators.md#test-operator + * @var array + */ + protected $_tests = array( + 'integer' => 'is_int(%s)', + 'int' => 'is_int(%s)', + 'float' => 'is_float(%s)', + 'double' => 'is_float(%s)', + 'decimal' => 'is_float(%s)', + 'string' => 'is_string(%s)', + 'bool' => 'is_bool(%s)', + 'boolean' => 'is_bool(%s)', + 'number' => 'is_numeric(%s)', + 'numeric' => 'is_numeric(%s)', + 'scalar' => 'is_scalar(%s)', + 'object' => 'is_object(%s)', + 'callable' => 'is_callable(%s)', + 'callback' => 'is_callable(%s)', + 'array' => 'is_array(%s)', + 'iterable' => '\Fenom\Modifier::isIterable(%s)', + 'const' => 'defined(%s)', + 'template' => '$tpl->getStorage()->templateExists(%s)', + 'empty' => 'empty(%s)', + 'set' => 'isset(%s)', + '_empty' => '!%s', // for none variable + '_set' => '(%s !== null)', // for none variable + 'odd' => '(%s & 1)', + 'even' => '!(%s %% 2)', + 'third' => '!(%s %% 3)' + ); + /** * Just factory * @@ -538,6 +571,24 @@ class Fenom return $this; } + /** + * Add custom test + * @param string $name test name + * @param string $code test PHP code. Code may contains placeholder %s, which will be replaced by test-value. For example: is_callable(%s) + */ + public function addTest($name, $code) { + $this->_tests[$name] = $code; + } + + /** + * Get test code by name + * @param string $name + * @return string|bool + */ + public function getTest($name) { + return isset($this->_tests[$name]) ? $this->_tests[$name] : false; + } + /** * Return modifier function * diff --git a/src/Fenom/Compiler.php b/src/Fenom/Compiler.php index bac8628..6434fb3 100644 --- a/src/Fenom/Compiler.php +++ b/src/Fenom/Compiler.php @@ -27,12 +27,13 @@ class Compiler * * @static * @param Tokenizer $tokens - * @param Template $tpl + * @param Tag $tag * @throws \LogicException * @return string */ - public static function tagInclude(Tokenizer $tokens, Template $tpl) + public static function tagInclude(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; $name = false; $cname = $tpl->parsePlainArg($tokens, $name); $p = $tpl->parseParams($tokens); @@ -60,12 +61,13 @@ class Compiler /** * Tag {insert ...} * @param Tokenizer $tokens - * @param Template $tpl - * @return string + * @param Tag $tag * @throws Error\InvalidUsageException + * @return string */ - public static function tagInsert(Tokenizer $tokens, Template $tpl) + public static function tagInsert(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; $tpl->parsePlainArg($tokens, $name); if (!$name) { throw new InvalidUsageException("Tag {insert} accept only static template name"); @@ -466,13 +468,13 @@ class Compiler /** * Dispatch {extends} tag * @param Tokenizer $tokens - * @param Template $tpl - * @throws \RuntimeException + * @param Tag $tag * @throws Error\InvalidUsageException * @return string */ - public static function tagExtends(Tokenizer $tokens, Template $tpl) + public static function tagExtends(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; if ($tpl->extends) { throw new InvalidUsageException("Only one {extends} allowed"); } elseif ($tpl->getStackSize()) { @@ -520,12 +522,13 @@ class Compiler /** * Tag {use ...} * @param Tokenizer $tokens - * @param Template $tpl - * @throws InvalidUsageException + * @param Tag $tag + * @throws Error\InvalidUsageException * @return string */ - public static function tagUse(Tokenizer $tokens, Template $tpl) + public static function tagUse(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; if ($tpl->getStackSize()) { throw new InvalidUsageException("Tag {use} can not be nested"); } @@ -623,24 +626,24 @@ class Compiler * @static * @param mixed $function * @param Tokenizer $tokens - * @param Template $tpl + * @param Tag $tag * @return string */ - public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) + public static function stdFuncParser($function, Tokenizer $tokens, Tag $tag) { - return "$function(" . self::toArray($tpl->parseParams($tokens)) . ', $tpl)'; + return "$function(" . self::toArray($tag->tpl->parseParams($tokens)) . ', $tpl)'; } /** * Smart function parser * * @static - * @param $function + * @param string $function * @param Tokenizer $tokens - * @param Template $tpl + * @param Tag $tag * @return string */ - public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) + public static function smartFuncParser($function, Tokenizer $tokens, Tag $tag) { if (strpos($function, "::")) { list($class, $method) = explode("::", $function, 2); @@ -649,7 +652,7 @@ class Compiler $ref = new \ReflectionFunction($function); } $args = array(); - $params = $tpl->parseParams($tokens); + $params = $tag->tpl->parseParams($tokens); foreach ($ref->getParameters() as $param) { if (isset($params[$param->getName()])) { $args[] = $params[$param->getName()]; @@ -767,12 +770,13 @@ class Compiler * Tag {cycle} * * @param Tokenizer $tokens - * @param Template $tpl + * @param Tag $tag + * @throws Error\InvalidUsageException * @return string - * @throws InvalidUsageException */ - public static function tagCycle(Tokenizer $tokens, Template $tpl) + public static function tagCycle(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; if ($tokens->is("[")) { $exp = $tpl->parseArray($tokens); } else { @@ -806,13 +810,14 @@ class Compiler * Import macros from templates * * @param Tokenizer $tokens - * @param Template $tpl - * @throws UnexpectedTokenException - * @throws InvalidUsageException + * @param Tag $tag + * @throws Error\UnexpectedTokenException + * @throws Error\InvalidUsageException * @return string */ - public static function tagImport(Tokenizer $tokens, Template $tpl) + public static function tagImport(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; $import = array(); if ($tokens->is('[')) { $tokens->next(); @@ -933,29 +938,15 @@ class Compiler * Output value as is, without escaping * * @param Tokenizer $tokens - * @param Template $tpl - * @throws InvalidUsageException + * @param Tag $tag * @return string */ - public static function tagRaw(Tokenizer $tokens, Template $tpl) + public static function tagRaw(Tokenizer $tokens, Tag $tag) { + $tpl = $tag->tpl; $escape = (bool)$tpl->escape; $tpl->escape = false; - if ($tokens->is(':')) { - $func = $tokens->getNext(Tokenizer::MACRO_STRING); - $tag = $tpl->getStorage()->getTag($func, $tpl); - if ($tag["type"] == \Fenom::INLINE_FUNCTION) { - $code = $tpl->parseAct($tokens); -// } elseif ($tag["type"] == \Fenom::BLOCK_FUNCTION) { -// $code = $tpl->parseAct($tokens); -// $tpl->getLastScope()->escape = false; -// return $code; - } else { - throw new InvalidUsageException("Raw mode allow for expressions or functions"); - } - } else { - $code = $tpl->out($tpl->parseExpr($tokens)); - } + $code = $tpl->out($tpl->parseExpr($tokens)); $tpl->escape = $escape; return $code; } diff --git a/src/Fenom/Scope.php b/src/Fenom/Scope.php index 60a966a..8f0f764 100644 --- a/src/Fenom/Scope.php +++ b/src/Fenom/Scope.php @@ -26,8 +26,7 @@ class Scope extends \ArrayObject public $tpl; public $is_compiler = true; public $is_closed = false; - public $escape = false; - public $options = array(); + public $options; private $_action; private $_body; private $_offset; diff --git a/src/Fenom/Tag.php b/src/Fenom/Tag.php new file mode 100644 index 0000000..fc7e40f --- /dev/null +++ b/src/Fenom/Tag.php @@ -0,0 +1,46 @@ +name = $name; + $this->tpl = $tpl; + } + + public function optLtrim() { + + } + + public function optRtrim() { + + } + + public function optTrim() { + + } + + public function optRaw() { + + } + + public function optEscape() { + + } +} \ No newline at end of file diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 4777874..4c9a1e2 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -109,34 +109,6 @@ class Template extends Render */ private $_crc = 0; - protected static $_tests = array( - 'integer' => 'is_int(%s)', - 'int' => 'is_int(%s)', - 'float' => 'is_float(%s)', - 'double' => 'is_float(%s)', - 'decimal' => 'is_float(%s)', - 'string' => 'is_string(%s)', - 'bool' => 'is_bool(%s)', - 'boolean' => 'is_bool(%s)', - 'number' => 'is_numeric(%s)', - 'numeric' => 'is_numeric(%s)', - 'scalar' => 'is_scalar(%s)', - 'object' => 'is_object(%s)', - 'callable' => 'is_callable(%s)', - 'callback' => 'is_callable(%s)', - 'array' => 'is_array(%s)', - 'iterable' => '\Fenom\Modifier::isIterable(%s)', - 'const' => 'defined(%s)', - 'template' => '$tpl->getStorage()->templateExists(%s)', - 'empty' => 'empty(%s)', - 'set' => 'isset(%s)', - '_empty' => '!%s', // for none variable - '_set' => '(%s !== null)', // for none variable - 'odd' => '(%s & 1)', - 'even' => '!(%s %% 2)', - 'third' => '!(%s %% 3)' - ); - /** * @param Fenom $fenom Template storage * @param int $options @@ -609,6 +581,7 @@ class Template extends Render */ public function parseAct(Tokenizer $tokens) { + $options = array(); if ($tokens->is(Tokenizer::MACRO_STRING)) { $action = $tokens->getAndNext(); } else { @@ -632,38 +605,35 @@ class Template extends Render if ($tokens->is("(")) { return $this->out($this->parseExpr($tokens->seek($p))); } else { - return $this->out(Compiler::smartFuncParser($static, $tokens, $this)); + return $this->out(Compiler::smartFuncParser($static, $tokens, new Tag($static, $this))); } } elseif($tokens->is(':')) { // parse tag options do { - $tokens->options[ $tokens->next()->need(T_STRING)->getAndNext() ] = true; + $options[ $tokens->next()->need(T_STRING)->getAndNext() ] = true; } while($tokens->is(':')); } if ($tag = $this->_fenom->getTag($action, $this)) { - if(isset($tokens->options['ignore']) && ($tag["type"] & Fenom::BLOCK_COMPILER)) { - $this->_ignore = $action; + if($tag["type"] == Fenom::BLOCK_COMPILER || $tag["type"] == Fenom::BLOCK_FUNCTION) { + $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: - $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); - $scope->options = &$tokens->options; $code = $scope->open($tokens); if (!$scope->is_closed) { array_push($this->_stack, $scope); } return $code; case Fenom::INLINE_COMPILER: - return call_user_func($tag["parser"], $tokens, $this); + return call_user_func($tag["parser"], $tokens, $scope); case Fenom::INLINE_FUNCTION: - return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $this)); + return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $scope)); case Fenom::BLOCK_FUNCTION: - $scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body); - $scope->options = &$tokens->options; $scope->setFuncName($tag["function"]); array_push($this->_stack, $scope); - $scope->escape = $this->escape; - $this->escape = false; return $scope->open($tokens); default: throw new \LogicException("Unknown function type"); @@ -1052,10 +1022,10 @@ class Template extends Render if (!$variable && ($action == "set" || $action == "empty")) { $action = "_$action"; $tokens->next(); - return $invert . sprintf(self::$_tests[$action], $value); - } elseif (isset(self::$_tests[$action])) { + return $invert . sprintf($this->_fenom->getTest($action), $value); + } elseif ($test = $this->_fenom->getTest($action)) { $tokens->next(); - return $invert . sprintf(self::$_tests[$action], $value); + return $invert . sprintf($test, $value); } elseif ($tokens->isSpecialVal()) { $tokens->next(); return '(' . $value . ' ' . $equal . '= ' . $action . ')'; diff --git a/src/Fenom/Tokenizer.php b/src/Fenom/Tokenizer.php index 29bdb10..43bdb52 100644 --- a/src/Fenom/Tokenizer.php +++ b/src/Fenom/Tokenizer.php @@ -84,7 +84,6 @@ class Tokenizer public $tokens; public $p = 0; public $quotes = 0; - public $options; private $_max = 0; private $_last_no = 0; diff --git a/tests/resources/actions.php b/tests/resources/actions.php index eaaff61..33d98f3 100644 --- a/tests/resources/actions.php +++ b/tests/resources/actions.php @@ -15,15 +15,16 @@ function myBlockFunc($params, $content) return "Block:" . $params["name"] . ':' . trim($content) . ':Block'; } -function myCompiler(Fenom\Tokenizer $tokenizer, Fenom\Template $tpl) +function myCompiler(Fenom\Tokenizer $tokenizer, Fenom\Tag $tag) { - $p = $tpl->parseParams($tokenizer); + $p = $tag->tpl->parseParams($tokenizer); return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";'; } function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) { - return myCompiler($tokenizer, $scope->tpl); + $p = $scope->tpl->parseParams($tokenizer); + return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";'; } function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope)