Add tag entry

This commit is contained in:
Ivan Shalganov 2014-04-12 01:00:58 +04:00
parent 11ae49f187
commit 1437a13bd1
8 changed files with 151 additions and 94 deletions

View File

@ -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}
```

View File

@ -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
*

View File

@ -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;
}

View File

@ -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
View 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() {
}
}

View File

@ -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 . ')';

View File

@ -84,7 +84,6 @@ class Tokenizer
public $tokens;
public $p = 0;
public $quotes = 0;
public $options;
private $_max = 0;
private $_last_no = 0;

View File

@ -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)