mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Add tag entry
This commit is contained in:
parent
11ae49f187
commit
1437a13bd1
@ -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}
|
||||
```
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
46
src/Fenom/Tag.php
Normal file
46
src/Fenom/Tag.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Fenom.
|
||||
*
|
||||
* (c) 2013 Ivan Shalganov
|
||||
*
|
||||
* For the full copyright and license information, please view the license.md
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Fenom;
|
||||
|
||||
|
||||
class Tag {
|
||||
|
||||
/**
|
||||
* @var Template
|
||||
*/
|
||||
public $tpl;
|
||||
public $name;
|
||||
public $options;
|
||||
|
||||
public function __construct($name, Template $tpl) {
|
||||
$this->name = $name;
|
||||
$this->tpl = $tpl;
|
||||
}
|
||||
|
||||
public function optLtrim() {
|
||||
|
||||
}
|
||||
|
||||
public function optRtrim() {
|
||||
|
||||
}
|
||||
|
||||
public function optTrim() {
|
||||
|
||||
}
|
||||
|
||||
public function optRaw() {
|
||||
|
||||
}
|
||||
|
||||
public function optEscape() {
|
||||
|
||||
}
|
||||
}
|
@ -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 . ')';
|
||||
|
@ -84,7 +84,6 @@ class Tokenizer
|
||||
public $tokens;
|
||||
public $p = 0;
|
||||
public $quotes = 0;
|
||||
public $options;
|
||||
private $_max = 0;
|
||||
private $_last_no = 0;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user