mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Fixes
Update docs Add {import ... from ...} Improve parsing
This commit is contained in:
parent
26393ad22d
commit
a3c5128aba
@ -28,7 +28,7 @@ Benchmark::runs("twig", 'inheritance/twig/b100.tpl', __DIR__.'/templates/foreach
|
||||
Benchmark::runs("aspect", 'inheritance/smarty/b100.tpl', __DIR__.'/templates/foreach/data.json');
|
||||
|
||||
echo "\nDone. Cleanup.\n";
|
||||
passthru("rm -rf ".__DIR__."/compile/*");
|
||||
//passthru("rm -rf ".__DIR__."/compile/*");
|
||||
passthru("rm -f ".__DIR__."/templates/inheritance/smarty/*");
|
||||
passthru("rm -f ".__DIR__."/templates/inheritance/twig/*");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
About Aspect
|
||||
============
|
||||
About Aspect [RU]
|
||||
=================
|
||||
|
||||
Aspect - самый быстрый, гибкий и тонкий шаблонизатор для PHP, унаследовавший синтаксис от Smarty3 и улучшив его.
|
||||
Пожалуй это единственный шаблонизатор, который не использет ни регулярные выражения, как Twig, ни лексер от BISON, как Smarty3.
|
||||
|
@ -1,11 +1,22 @@
|
||||
Модификаторы
|
||||
Модификаторы [RU]
|
||||
============
|
||||
|
||||
Добавить модификатор:
|
||||
|
||||
```php
|
||||
$aspect->addModifier($modifier, $callback);
|
||||
```
|
||||
$aspect->addModifier(string $modifier, callable $callback);
|
||||
```
|
||||
|
||||
* `$modifier` - имя модификатора
|
||||
* `$callback` - строка с именем функции
|
||||
* `$modifier` - название модификатора, которое будет использоваться в шаблоне
|
||||
* `$callback` - коллбек, который будет вызван для изменения данных
|
||||
|
||||
For example:
|
||||
|
||||
```smarty
|
||||
{$variable|my_modifier:$param1:$param2}
|
||||
```
|
||||
|
||||
```php
|
||||
$aspect->addModifier('my_modifier', function ($variable, $param1, $param2) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
@ -1,20 +1,68 @@
|
||||
Теги
|
||||
====
|
||||
Tags [RU]
|
||||
=========
|
||||
|
||||
Теги делятся на компилеры и функции.
|
||||
Компилеры формируют синтаксис языка шаблона, добавляя такой функционал как foreach, if, while и т.д. В то время как функции - обычный вызов некоторой именованной функции
|
||||
В шаблонизаторе принято различать два типа тегов: компиляторы и функции.
|
||||
Компиляторы вызываются во время преобразования кода шаблона в PHP код и возвращяю PHP код который будет вставлен вместо тега.
|
||||
А функции вызываются непременно в момент выполнения шаблона и возвращают непосредственно данные которые будут отображены.
|
||||
Среди тегов как и в HTML есть строчные и блоковые теги.
|
||||
|
||||
Добавить компилер:
|
||||
## Inline function
|
||||
|
||||
Примитивное добавление функции можно осуществить следующим образом:
|
||||
|
||||
```php
|
||||
$aspect->addCompiler($compiler, $parser);
|
||||
$aspect->addFunction(string $function_name, callable $callback[, callable $parser]);
|
||||
```
|
||||
|
||||
* `$compiler` - имя модификатора
|
||||
* `$parser` - функция разбора тега в формате function (MF\Tokenizer $tokens, MF\Aspect\Template $tpl) {}
|
||||
|
||||
Добавить блочный компилер:
|
||||
В данном случае запускается стандартный парсер, который автоматически разберет аргументы тега, которые должны быть в формате HTML аттрибутов и отдаст их в функцию ассоциативным массивом.
|
||||
В данном случае вы можете переопределить парсер на произвольный в формате `function (Aspect\Tokenizer $tokenizer, Aspect\Template $template)`
|
||||
Существует более совершенный способ добавления функции:
|
||||
|
||||
```php
|
||||
$aspect->addBlockCompiler($compiler, $parsers, $tags);
|
||||
```
|
||||
$aspect->addFunctionSmarty(string $function_name, callable $callback);
|
||||
```
|
||||
|
||||
В данном случае парсер просканирует список аргументов коллбека и попробует сопоставить с аргументами из тега. Таким образом вы успешно можете добавлять Ваши штатные функции.
|
||||
|
||||
## Block function
|
||||
|
||||
Добавление блоковой функции аналогичен добавлению строковой за исключением того что есть возможность указать парсер для закрывающего тега.
|
||||
|
||||
```php
|
||||
$aspect->addBlockFunction(string $function_name, callable $callback[, callable $parser_open[, callable $parser_close]]);
|
||||
```
|
||||
|
||||
Сам коллбек принимает первым аргументом контент между открывающим и закрывающим тегом, а вторым аргументом - ассоциативный массив из аргуметов тега.
|
||||
|
||||
## Inline compiler
|
||||
|
||||
Добавление строчного компилятора осуществляеться очень просто:
|
||||
|
||||
```php
|
||||
$aspect->addCompiler(string $compiler, callable $parser);
|
||||
```
|
||||
|
||||
Парсер должен принимать `Aspect\Tokenizer $tokenizer`, `Aspect\Template $template` и возвращать PHP код.
|
||||
Компилятор так же можно импортировать из класса автоматически
|
||||
|
||||
```php
|
||||
$aspect->addCompilerSmart(string $compiler, $storage);
|
||||
```
|
||||
|
||||
`$storage` может быть как классом так и объектом. В данном случае шаблонизатор будет искать метод `tag{$compiler}`, который будет взят в качестве парсера тега.
|
||||
|
||||
## Block compiler
|
||||
|
||||
Добавление блочного компилятора осуществяется двум способами. Первый
|
||||
|
||||
```php
|
||||
$aspect->addBlockCompiler(string $compiler, array $parsers, array $tags);
|
||||
```
|
||||
|
||||
где `$parser` ассоциативный массив `["open" => parser, "close" => parser]`, сождержащий парсер на открывающий и на закрывающий тег, а `$tags` содержит список внутренних тегов в формате `["tag_name"] => parser`, которые могут быть использованы только с этим компилятором.
|
||||
Второй способ добавления парсера через импортирование из класса или объекта методов:
|
||||
|
||||
```php
|
||||
$aspect->addBlockCompilerSmart(string $compiler, $storage, array $tags, array $floats);
|
||||
```
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
Installation
|
||||
=============================
|
||||
============
|
||||
|
||||
For installation use [composer](http://getcompoer.org). Add in your `composer.json` requirements:
|
||||
```json
|
||||
|
@ -7,7 +7,7 @@ Math
|
||||
|
||||
Bitwize
|
||||
|
||||
`| & <<`
|
||||
`| & << >> |= &= <<= >>=`
|
||||
|
||||
Unary
|
||||
|
||||
@ -15,12 +15,8 @@ Unary
|
||||
|
||||
Boolean
|
||||
|
||||
`|| && and or < > <= >= == === !== != ≥ ≤ ≠`
|
||||
`|| && and or < > <= >= == === !== !=`
|
||||
|
||||
Ternar
|
||||
|
||||
`? :`
|
||||
|
||||
Test
|
||||
|
||||
`is in like`
|
@ -1,5 +1,5 @@
|
||||
Настройка
|
||||
=========
|
||||
Settings [RU]
|
||||
=============
|
||||
|
||||
### Engine settings
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
Syntax
|
||||
======
|
||||
Syntax [RU]
|
||||
===========
|
||||
|
||||
### Output variables
|
||||
|
||||
|
@ -2,7 +2,7 @@ Tag {for}
|
||||
=========
|
||||
|
||||
```smarty
|
||||
{for $counter=<start..end> [to=<end>] [step=<step>] [index=$index] [first=$first] [last=$last]}
|
||||
{for $counter=<start> to=<end> [step=<step>] [index=$index] [first=$first] [last=$last]}
|
||||
{* ...code... *}
|
||||
{break}
|
||||
{* ...code... *}
|
||||
|
@ -1,7 +1,12 @@
|
||||
Tag {macro}
|
||||
============
|
||||
Tag {macro} [RU]
|
||||
================
|
||||
|
||||
Declare macro
|
||||
Макросы - фрагмент шаблона который можно повторить сколь угодно раз и в каком угодно месте.
|
||||
Макросы не имеют общего пространства имен с шаблоном и могут оперировать только переданными переменными.
|
||||
|
||||
### {macro}
|
||||
|
||||
Обявление макроса происходит при помощи блочного тега `{macro}`
|
||||
|
||||
```smarty
|
||||
{macro plus(x, y, z=0)}
|
||||
@ -9,7 +14,7 @@ Declare macro
|
||||
{/macro}
|
||||
```
|
||||
|
||||
Invoke macro
|
||||
Вызов макроса происходит при помощи строкового тега `{macro}`. Аргументы передаются стандартно, как атрибуты в HTML тегах
|
||||
|
||||
```smarty
|
||||
{macro.plus x=$num y=100}
|
||||
@ -17,14 +22,23 @@ Invoke macro
|
||||
|
||||
### {import}
|
||||
|
||||
Import [macro](./macro.md) from another template
|
||||
Для использования маросов в другом шаблоне необходимо их импортировать при помощи тега `{import}`
|
||||
|
||||
```smarty
|
||||
{import 'math.tpl'}
|
||||
```
|
||||
|
||||
При импорте можно указать дргое пространство имен что бы можно было использовать одноименные макросы из разных шаблонов
|
||||
|
||||
```smarty
|
||||
{import 'math.tpl' as math}
|
||||
...
|
||||
{math.plus x=5 y=100}
|
||||
```
|
||||
|
||||
Пространство имен макросов может совпадать с названием какого-либо тега, в данном случае ничего плохого не произойдет: будет вызван макрос, а тег не исчезнит
|
||||
При необходимости можно импортировать только необходимые макросы, явно указав в теге `{import}`
|
||||
|
||||
```smarty
|
||||
{import [plus, minus, exp] from 'math.tpl' as math}
|
||||
```
|
@ -25,7 +25,7 @@ class Aspect {
|
||||
const DEFAULT_CLOSE_COMPILER = 'Aspect\Compiler::stdClose';
|
||||
const DEFAULT_FUNC_PARSER = 'Aspect\Compiler::stdFuncParser';
|
||||
const DEFAULT_FUNC_OPEN = 'Aspect\Compiler::stdFuncOpen';
|
||||
const DEFAULT_FUNC_CLOSE = 'Aspect\Compiler::stdFuncOpen';
|
||||
const DEFAULT_FUNC_CLOSE = 'Aspect\Compiler::stdFuncClose';
|
||||
const SMART_FUNC_PARSER = 'Aspect\Compiler::smartFuncParser';
|
||||
|
||||
/**
|
||||
@ -320,7 +320,18 @@ class Aspect {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addCompilerSmart($class) {
|
||||
/**
|
||||
* @param string $compiler
|
||||
* @param string|object $storage
|
||||
* @return $this
|
||||
*/
|
||||
public function addCompilerSmart($compiler, $storage) {
|
||||
if(method_exists($storage, "tag".$compiler)) {
|
||||
$this->_actions[$compiler] = array(
|
||||
'type' => self::INLINE_COMPILER,
|
||||
'parser' => array($storage, "tag".$compiler)
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -329,7 +340,7 @@ class Aspect {
|
||||
*
|
||||
* @param string $compiler
|
||||
* @param callable $open_parser
|
||||
* @param callable $close_parser
|
||||
* @param callable|string $close_parser
|
||||
* @param array $tags
|
||||
* @return Aspect
|
||||
*/
|
||||
@ -344,12 +355,40 @@ class Aspect {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param $compiler
|
||||
* @param $storage
|
||||
* @param array $tags
|
||||
* @param array $floats
|
||||
* @throws LogicException
|
||||
* @return Aspect
|
||||
*/
|
||||
public function addBlockCompilerSmart($class, array $tags, array $floats = array()) {
|
||||
public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array()) {
|
||||
$c = array(
|
||||
'type' => self::BLOCK_COMPILER,
|
||||
"tags" => array(),
|
||||
"float_tags" => array()
|
||||
);
|
||||
if(method_exists($storage, $compiler."Open")) {
|
||||
$c["open"] = $compiler."Open";
|
||||
} else {
|
||||
throw new \LogicException("Open compiler {$compiler}Open not found");
|
||||
}
|
||||
if(method_exists($storage, $compiler."Close")) {
|
||||
$c["close"] = $compiler."Close";
|
||||
} else {
|
||||
throw new \LogicException("Close compiler {$compiler}Close not found");
|
||||
}
|
||||
foreach($tags as $tag) {
|
||||
if(method_exists($storage, "tag".$tag)) {
|
||||
$c["tags"][ $tag ] = "tag".$tag;
|
||||
if($floats && in_array($tag, $floats)) {
|
||||
$c['float_tags'][ $tag ] = 1;
|
||||
}
|
||||
} else {
|
||||
throw new \LogicException("Tag compiler $tag (tag{$compiler}) not found");
|
||||
}
|
||||
}
|
||||
$this->_actions[$compiler] = $c;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -362,7 +401,7 @@ class Aspect {
|
||||
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) {
|
||||
$this->_actions[$function] = array(
|
||||
'type' => self::INLINE_FUNCTION,
|
||||
'parser' => $parser ?: self::DEFAULT_FUNC_PARSER,
|
||||
'parser' => $parser,
|
||||
'function' => $callback,
|
||||
);
|
||||
return $this;
|
||||
@ -385,16 +424,16 @@ class Aspect {
|
||||
/**
|
||||
* @param string $function
|
||||
* @param callable $callback
|
||||
* @param callable $parser_open
|
||||
* @param callable $parser_close
|
||||
* @param callable|string $parser_open
|
||||
* @param callable|string $parser_close
|
||||
* @return Aspect
|
||||
*/
|
||||
public function addBlockFunction($function, $callback, $parser_open = null, $parser_close = null) {
|
||||
public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE) {
|
||||
$this->_actions[$function] = array(
|
||||
'type' => self::BLOCK_FUNCTION,
|
||||
'open' => $parser_open ?: 'Aspect\Compiler::stdFuncOpen',
|
||||
'close' => $parser_close ?: 'Aspect\Compiler::stdFuncClose',
|
||||
'function' => $callback,
|
||||
'type' => self::BLOCK_FUNCTION,
|
||||
'open' => $parser_open,
|
||||
'close' => $parser_close,
|
||||
'function' => $callback,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
@ -564,7 +603,7 @@ class Aspect {
|
||||
return $this->_storage[ $template ];
|
||||
}
|
||||
} elseif($this->_options & self::FORCE_COMPILE) {
|
||||
return $this->compile($template, false);
|
||||
return $this->compile($template, $this->_options & self::DISABLE_CACHE);
|
||||
} else {
|
||||
return $this->_storage[ $template ] = $this->_load($template);
|
||||
}
|
||||
@ -583,7 +622,7 @@ class Aspect {
|
||||
*
|
||||
* @param string $tpl
|
||||
* @throws \RuntimeException
|
||||
* @return Aspect\Template|mixed
|
||||
* @return Aspect\Render
|
||||
*/
|
||||
protected function _load($tpl) {
|
||||
$file_name = $this->_getHash($tpl);
|
||||
@ -591,7 +630,6 @@ class Aspect {
|
||||
return $this->compile($tpl);
|
||||
} else {
|
||||
$aspect = $this;
|
||||
/** @var Aspect\Render $tpl */
|
||||
return include($this->_compile_dir."/".$file_name);
|
||||
}
|
||||
}
|
||||
@ -604,7 +642,7 @@ class Aspect {
|
||||
*/
|
||||
private function _getHash($tpl) {
|
||||
$hash = $tpl.":".$this->_options;
|
||||
return sprintf("%s.%u.%d.php", basename($tpl), crc32($hash), strlen($hash));
|
||||
return sprintf("%s.%u.%d.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,7 +148,6 @@ class Compiler {
|
||||
$before = $before ? implode("; ", $before).";" : "";
|
||||
$body = $body ? implode("; ", $body).";" : "";
|
||||
$scope["after"] = $scope["after"] ? implode("; ", $scope["after"]).";" : "";
|
||||
|
||||
if($key) {
|
||||
return "$prepend if($from) { $before foreach($from as $key => $value) { $body";
|
||||
} else {
|
||||
@ -161,8 +160,6 @@ class Compiler {
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @param Scope $scope
|
||||
* @internal param $
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function foreachElse($tokens, Scope $scope) {
|
||||
@ -385,7 +382,7 @@ class Compiler {
|
||||
return "";
|
||||
} else { // dynamic extends
|
||||
$tpl->_extends = $tpl_name;
|
||||
return '$parent = $tpl->getStorage()->getTemplate('.$tpl_name.');';
|
||||
return '$parent = $tpl->getStorage()->getTemplate("extend:".'.$tpl_name.');';
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,7 +558,8 @@ class Compiler {
|
||||
*/
|
||||
public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) {
|
||||
if(strpos($function, "::")) {
|
||||
$ref = new \ReflectionMethod($function);
|
||||
list($class, $method) = explode("::", $function, 2);
|
||||
$ref = new \ReflectionMethod($class, $method);
|
||||
} else {
|
||||
$ref = new \ReflectionFunction($function);
|
||||
}
|
||||
@ -710,6 +708,24 @@ class Compiler {
|
||||
* @throws ImproperUseException
|
||||
*/
|
||||
public static function tagImport(Tokenizer $tokens, Template $tpl) {
|
||||
$import = array();
|
||||
if($tokens->is('[')) {
|
||||
$tokens->next();
|
||||
while($tokens->valid()) {
|
||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$import[ $tokens->current() ] = true;
|
||||
$tokens->next();
|
||||
} elseif($tokens->is(']')) {
|
||||
$tokens->next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($tokens->current() != "from") {
|
||||
throw new UnexpectedException($tokens);
|
||||
}
|
||||
$tokens->next();
|
||||
}
|
||||
|
||||
$tpl->parseFirstArg($tokens, $name);
|
||||
if(!$name) {
|
||||
throw new ImproperUseException("Invalid usage tag {import}");
|
||||
|
@ -138,8 +138,8 @@ class Template extends Render {
|
||||
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-char of tags
|
||||
switch($this->_src[$start + 1]) { // check next char
|
||||
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
||||
$pos = $start + 1; // try find tags after the current char
|
||||
continue 2;
|
||||
$pos = $start + 1; // try find tags after the current char
|
||||
continue 2;
|
||||
case "*": // if comment block
|
||||
$end = strpos($this->_src, '*}', $start); // finding end of the comment block
|
||||
$_frag = substr($this->_src, $this->_pos, $start - $end); // read the comment block for precessing
|
||||
@ -163,7 +163,7 @@ class Template extends Render {
|
||||
$_tag = substr($tag, 1, -1);
|
||||
$_frag = $frag;
|
||||
}
|
||||
if($this->_ignore) { // check ignore scope
|
||||
if($this->_ignore) { // check ignore
|
||||
if($_tag === '/ignore') {
|
||||
$this->_ignore = false;
|
||||
$this->_appendText($_frag);
|
||||
@ -173,7 +173,12 @@ class Template extends Render {
|
||||
}
|
||||
} else {
|
||||
$this->_appendText($_frag);
|
||||
$this->_appendCode($this->_tag($_tag));
|
||||
$tokens = new Tokenizer($_tag);
|
||||
$this->_appendCode($this->_tag($tokens), $tag);
|
||||
if($tokens->key()) { // if tokenizer still have tokens
|
||||
throw new CompileException("Unexpected token '".$tokens->current()."' in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line);
|
||||
}
|
||||
|
||||
}
|
||||
$frag = "";
|
||||
}
|
||||
@ -195,7 +200,6 @@ class Template extends Render {
|
||||
call_user_func_array($cb, array(&$this->_body, $this));
|
||||
}
|
||||
}
|
||||
/*$this->_body = str_replace(array('?>'.PHP_EOL.'<?php ', '?><?php'), array(PHP_EOL, ' '), $this->_body);*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,7 +211,7 @@ class Template extends Render {
|
||||
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>'.PHP_EOL, $text);
|
||||
}
|
||||
|
||||
public static function escapeCode($code) {
|
||||
private function _escapeCode($code) {
|
||||
$c = "";
|
||||
foreach(token_get_all($code) as $token) {
|
||||
if(is_string($token)) {
|
||||
@ -225,12 +229,16 @@ class Template extends Render {
|
||||
* Append PHP code to template body
|
||||
*
|
||||
* @param string $code
|
||||
* @param $source
|
||||
*/
|
||||
private function _appendCode($code) {
|
||||
private function _appendCode($code, $source) {
|
||||
if(!$code) {
|
||||
return;
|
||||
} else {
|
||||
$this->_body .= self::escapeCode($code);
|
||||
if(strpos($code, '?>') !== false) {
|
||||
$code = $this->_escapeCode($code); // paste PHP_EOL
|
||||
}
|
||||
$this->_body .= "<?php\n/* {$this->_name}:{$this->_line}: {$source} */\n $code ?>".PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +267,7 @@ class Template extends Render {
|
||||
return "<?php \n".
|
||||
"/** Aspect template '".$this->_name."' compiled at ".date('Y-m-d H:i:s')." */\n".
|
||||
"return new Aspect\\Render(\$aspect, ".$this->_getClosureSource().", ".var_export(array(
|
||||
//"options" => $this->_options,
|
||||
"options" => $this->_options,
|
||||
"provider" => $this->_scm,
|
||||
"name" => $this->_name,
|
||||
"base_name" => $this->_base_name,
|
||||
@ -321,43 +329,28 @@ class Template extends Render {
|
||||
|
||||
/**
|
||||
* Internal tags router
|
||||
* @param string $src
|
||||
* @param Tokenizer $tokens
|
||||
* @throws UnexpectedException
|
||||
* @throws CompileException
|
||||
* @throws SecurityException
|
||||
* @return string executable PHP code
|
||||
*/
|
||||
private function _tag($src) {
|
||||
$tokens = new Tokenizer($src);
|
||||
private function _tag(Tokenizer $tokens) {
|
||||
try {
|
||||
switch($src[0]) {
|
||||
case '"':
|
||||
case '\'':
|
||||
case '$':
|
||||
$code = "echo ".$this->parseExp($tokens).";";
|
||||
break;
|
||||
case '#':
|
||||
$code = "echo ".$this->parseConst($tokens);
|
||||
break;
|
||||
case '/':
|
||||
$code = $this->_end($tokens);
|
||||
break;
|
||||
default:
|
||||
if($tokens->current() === "ignore") {
|
||||
$this->_ignore = true;
|
||||
$tokens->next();
|
||||
$code = '';
|
||||
} else {
|
||||
$code = $this->_parseAct($tokens);
|
||||
}
|
||||
}
|
||||
if($tokens->key()) { // if tokenizer still have tokens
|
||||
throw new UnexpectedException($tokens);
|
||||
}
|
||||
if(!$code) {
|
||||
return "";
|
||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
if($tokens->current() === "ignore") {
|
||||
$this->_ignore = true;
|
||||
$tokens->next();
|
||||
return '';
|
||||
} else {
|
||||
return $this->_parseAct($tokens);
|
||||
}
|
||||
} elseif ($tokens->is('/')) {
|
||||
return $this->_end($tokens);
|
||||
} elseif ($tokens->is('#')) {
|
||||
return "echo ".$this->parseConst($tokens).';';
|
||||
} else {
|
||||
return "<?php\n/* {$this->_name}:{$this->_line}: {$src} */\n {$code} ?>";
|
||||
return $code = "echo ".$this->parseExp($tokens).";";
|
||||
}
|
||||
} catch (ImproperUseException $e) {
|
||||
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
@ -376,6 +369,7 @@ class Template extends Render {
|
||||
* @throws TokenizeException
|
||||
*/
|
||||
private function _end(Tokenizer $tokens) {
|
||||
//return "end";
|
||||
$name = $tokens->getNext(Tokenizer::MACRO_STRING);
|
||||
$tokens->next();
|
||||
if(!$this->_stack) {
|
||||
@ -402,7 +396,7 @@ class Template extends Render {
|
||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$action = $tokens->getAndNext();
|
||||
} else {
|
||||
return 'echo '.$this->parseExp($tokens).';'; // may be math and boolean expression
|
||||
return 'echo '.$this->parseExp($tokens).';'; // may be math and/or boolean expression
|
||||
}
|
||||
|
||||
if($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON)) { // just invoke function or static method
|
||||
|
@ -266,7 +266,7 @@ class TemplateTest extends TestCase {
|
||||
public static function providerIncludeInvalid() {
|
||||
return array(
|
||||
array('Include {include} template', 'Aspect\CompileException', "Unexpected end of expression"),
|
||||
array('Include {include another="welcome.tpl"} template', 'Aspect\CompileException', "Unexpected token '=' in expression"),
|
||||
array('Include {include another="welcome.tpl"} template', 'Aspect\CompileException', "Unexpected token '='"),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,9 @@ class AspectTest extends \Aspect\TestCase {
|
||||
$this->assertSame("Custom modifier (myMod)Custom(/myMod)", $this->aspect->fetch('custom.tpl', array("a" => "Custom")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group add_functions
|
||||
*/
|
||||
public function testSetFunctions() {
|
||||
$this->aspect->setOptions(Aspect::FORCE_COMPILE);
|
||||
$this->aspect->addFunction("myfunc", "myFunc");
|
||||
|
Loading…
Reference in New Issue
Block a user