From e55402c2f42cb871313081dc4debdff82b319753 Mon Sep 17 00:00:00 2001 From: bzick Date: Sun, 5 Oct 2014 20:37:30 +0400 Subject: [PATCH] Dev accessor. Add ~~ operator. ++Docs. ++Tests --- docs/ru/operators.md | 4 +- docs/ru/syntax.md | 46 +++++--- src/Fenom.php | 79 +++++++++++-- src/Fenom/Accessor.php | 113 ++++++++++++++++++ src/Fenom/Render.php | 18 +-- src/Fenom/Template.php | 55 +++------ src/Fenom/Tokenizer.php | 183 +++++++---------------------- tests/TestCase.php | 8 ++ tests/cases/Fenom/AccessorTest.php | 144 +++++++++++++++++++++++ tests/cases/Fenom/TemplateTest.php | 60 ++-------- 10 files changed, 436 insertions(+), 274 deletions(-) create mode 100644 src/Fenom/Accessor.php create mode 100644 tests/cases/Fenom/AccessorTest.php diff --git a/docs/ru/operators.md b/docs/ru/operators.md index 996ef60..6d6f402 100644 --- a/docs/ru/operators.md +++ b/docs/ru/operators.md @@ -127,11 +127,13 @@ Fenom поддерживает префиксные и постфиксные о * `--$a` - префиксный декремент, уменьшает $a на единицу, затем возвращает значение $a. * `$a--` - постфиксный декремент, возвращает значение $a, затем уменьшает $a на единицу. -### Строковый оператор +### Строковые операторы Оператор конкатенации `~` возвращает строку, представляющую собой соединение левого и правого аргумента. `$a ~ $b` - возвращает результат объединения сток `$a` и `$b` +`$a ~~ $b` - возвращает результат объединения сток `$a` и `$b` через пробел +`$a ~= $b` - присвоение с объединением ### Тернарные операторы diff --git a/docs/ru/syntax.md b/docs/ru/syntax.md index ef9106a..50d4027 100644 --- a/docs/ru/syntax.md +++ b/docs/ru/syntax.md @@ -81,25 +81,31 @@ Безименная системная переменная начинается с `$.` и предоставляет доступ к глобальным системным переменным и системной информации: -* `$.get` — `$_GET`. -* `$.post` — `$_POST`. -* `$.cookie` — `$_COOKIE`. -* `$.session` — `$_SESSION`. -* `$.globals` — `$GLOBALS`. -* `$.request` — `$_REQUEST`. -* `$.files` — `$_FILES`. -* `$.server` — `$_SERVER`. -* `$.env` — `$_ENV`. +* `$.env` — массив `$_ENV`. +* `$.get` — массив `$_GET`. +* `$.post` — массив `$_POST`. +* `$.files` — массив `$_FILES`. +* `$.cookie` — массив `$_COOKIE`. +* `$.server` — массив `$_SERVER`. +* `$.session` — массив `$_SESSION`. +* `$.globals` — массив `$GLOBALS`. +* `$.request` — массив `$_REQUEST`. * `$.tpl.name` возвращает текущее название шаблона. -* `$.tpl.schema` возвращает код провайдера шаблона. +* `$.tpl.basename` возвращает текущее название шаблона без схемы. +* `$.tpl.scm` возвращает схему шаблона. +* `$.tpl.options` возвращает параметры шбалона в виде целого числа. +* `$.tpl.depends` возвращает массив шаблонов на которые ссылается текущий шаблон. +* `$.tpl.time` возвращает штамп времени когда шаблон последний раз менялся * `$.version` возвращает версию Fenom. -* `$.const` обращение к PHP константе: `$.const.PHP_EOL` . - -```smarty -{if $.get.debug? && $.const.DEBUG} - ... -{/if} -``` +* `$.const.*` обращение к PHP константе: `$.const.PHP_EOL` обращение к константе `PHP_EOL`. Поддерживается пространство имен + которое разделяется через точку: `$.const.Storage.FS::DIR_SEPARATOR` обращение к PHP константе `Storage\FS::DIR_SEPARATOR` + если такой констатнты нет будет взята константа `Storage\FS\DIR_SEPARATOR`. +* `$.php.*` обращение к статическомому методу. `$.php.Storage.FS::put($filename, $data)` обращение к методу `Storage\FS::put($filename, $data)`. + `$.php.Storage.FS.put($filename, $data)` `Storage\FS\put($filename, $data)` +* `$.tag.*` обращение к тегу. `$.tag.mailto($filename, $data)` {mailto ""}. +* `$.func.*` +* `$.fetch($name, $values)` +* `$.macro` `$.macro.math.plus` `$.macro.math.plus(...)` ## Скалярные значения @@ -307,7 +313,7 @@ NULL - это отсутствие присутствия, а FALSE - прису Все сущности шаблона можно разжелить на две группы: * заполнитель (placeholder) — вывод переменной в шаблоне, например `{$name}` -* тег — конструкция выполняющаяя некоторые действия, выглядит как именованный заполнитель (placeholder), например `{include $name}` +* тег — конструкция, выполняющаяя некоторые действия, которая выглядит как именованный заполнитель (placeholder), например `{include $name}` Теги так же можно разделить на две группы: @@ -316,6 +322,10 @@ NULL - это отсутствие присутствия, а FALSE - прису * Компиляторы. В отличии от функций компиляторы вызываются во время компиляции шаблона и возвращают PHP код, который описывает некоторое действие. Компиляторы и формируют основные конструкции типа `if`, `foreach` и т.д. +``` +{set $a = $.func.mailto($email)} +``` + ### Игнорирование кода В шаблонизаторе Fenom используются фигурные скобки для отделения HTML от кода Fenom. diff --git a/src/Fenom.php b/src/Fenom.php index 554f40b..bb79e53 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -17,7 +17,7 @@ use Fenom\Template; */ class Fenom { - const VERSION = '2.0'; + const VERSION = '2.4'; /* Actions */ const INLINE_COMPILER = 1; const BLOCK_COMPILER = 5; @@ -81,6 +81,11 @@ class Fenom */ public $tag_filters = array(); + /** + * @var string[] + */ + public $call_filters = array(); + /** * @var callable[] */ @@ -340,6 +345,24 @@ class Fenom 'third' => '!(%s %% 3)' ); + protected $_accessors = array( + 'get' => 'Fenom\Accessor::getVar', + 'env' => 'Fenom\Accessor::getVar', + 'post' => 'Fenom\Accessor::getVar', + 'request' => 'Fenom\Accessor::getVar', + 'cookie' => 'Fenom\Accessor::getVar', + 'globals' => 'Fenom\Accessor::getVar', + 'server' => 'Fenom\Accessor::getVar', + 'session' => 'Fenom\Accessor::getVar', + 'files' => 'Fenom\Accessor::getVar', + 'tpl' => 'Fenom\Accessor::tpl', + 'version' => 'Fenom\Accessor::version', + 'const' => 'Fenom\Accessor::constant', + 'php' => 'Fenom\Accessor::php', + 'tag' => 'Fenom\Accessor::Tag', + 'fetch' => 'Fenom\Accessor::Fetch', + ); + /** * Just factory * @@ -510,12 +533,7 @@ class Fenom * @param array $tags * @return Fenom */ - public function addBlockCompiler( - $compiler, - $open_parser, - $close_parser = self::DEFAULT_CLOSE_COMPILER, - array $tags = array() - ) { + public function addBlockCompiler($compiler, $open_parser, $close_parser = self::DEFAULT_CLOSE_COMPILER, array $tags = array()) { $this->_actions[$compiler] = array( 'type' => self::BLOCK_COMPILER, 'open' => $open_parser, @@ -772,6 +790,53 @@ class Fenom return $this->_options; } + /** + * Add global accessor ($.) + * @param string $name + * @param callable $parser + * @return Fenom + */ + public function addAccessor($name, $parser) + { + $this->_accessors[$name] = $parser; + return $this; + } + + /** + * Remove accessor + * @param string $name + * @return Fenom + */ + public function removeAccessor($name) + { + unset($this->_accessors[$name]); + return $this; + } + + /** + * Get an accessor + * @param string $name + * @return callable + */ + public function getAccessor($name) { + if(isset($this->_accessors[$name])) { + return $this->_accessors[$name]; + } else { + return false; + } + } + + /** + * Add filter for $.php accessor. + * Uses glob syntax. + * @param string $pattern + * @return $this + */ + public function addCallFilter($pattern) { + $this->call_filters[] = $pattern; + return $this; + } + /** * @param bool|string $scm * @return Fenom\ProviderInterface diff --git a/src/Fenom/Accessor.php b/src/Fenom/Accessor.php new file mode 100644 index 0000000..aaf2093 --- /dev/null +++ b/src/Fenom/Accessor.php @@ -0,0 +1,113 @@ + '$_GET', + 'post' => '$_POST', + 'session' => '$_SESSION', + 'cookie' => '$_COOKIE', + 'request' => '$_REQUEST', + 'files' => '$_FILES', + 'globals' => '$GLOBALS', + 'server' => '$_SERVER', + 'env' => '$_ENV' + ); + + /** + * Accessor for global variables + * @param Tokenizer $tokens + * @param Template $tpl + */ + public static function getVar(Tokenizer $tokens, Template $tpl) { + $name = $tokens->prev[Tokenizer::TEXT]; + if(isset(self::$vars[$name])) { + $var = $tpl->parseVariable($tokens, self::$vars[$name]); + return "(isset($var) ? $var : null)"; + } else { + throw new UnexpectedTokenException($tokens->back()); + } + } + + /** + * Accessor for template information + * @param Tokenizer $tokens + */ + public static function tpl(Tokenizer $tokens) { + $method = $tokens->skip('.')->need(T_STRING)->getAndNext(); + if(method_exists('Fenom\Render', 'get'.$method)) { + return '$tpl->get'.ucfirst($method).'()'; + } else { + throw new UnexpectedTokenException($tokens->back()); + } + } + + public static function version() { + return 'Fenom::VERSION'; + } + + /** + * @param Tokenizer $tokens + * @return string + */ + public static function constant(Tokenizer $tokens) { + $const = [$tokens->skip('.')->need(Tokenizer::MACRO_STRING)->getAndNext()]; + while($tokens->is('.')) { + $const[] = $tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext(); + } + $const = implode('\\', $const); + if($tokens->is(T_DOUBLE_COLON)) { + $const .= '::'.$tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext(); + } + return '@constant('.var_export($const, true).')'; + + } + + /** + * @param Tokenizer $tokens + * @param Template $tpl + * @return string + */ + public static function php(Tokenizer $tokens, Template $tpl) { + $callable = [$tokens->skip('.')->need(Tokenizer::MACRO_STRING)->getAndNext()]; + while($tokens->is('.')) { + $callable[] = $tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext(); + } + $callable = implode('\\', $callable); + if($tokens->is(T_DOUBLE_COLON)) { + $callable .= '::'.$tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext(); + } + if(!is_callable($callable)) { + throw new \LogicException("PHP method ".str_replace('\\', '.', $callable).' does not exists.'); + } + if($tokens->is('(')) { + $arguments = 'array'.$tpl->parseArgs($tokens).''; + } else { + $arguments = 'array()'; + } + return 'call_user_func_array('.var_export($callable, true).', '.$arguments.')'; + + } + + public static function tag(Tokenizer $tokens, Template $tpl) { + $tag = $tokens->get(Tokenizer::MACRO_STRING); + $info = $tpl->getStorage()->getTag($tag, $tpl); + if($info['type'] !== \Fenom::INLINE_FUNCTION) { + throw new \LogicException("Only inline functions allowed in accessor"); + } + } +} \ No newline at end of file diff --git a/src/Fenom/Render.php b/src/Fenom/Render.php index e126d75..48b260a 100644 --- a/src/Fenom/Render.php +++ b/src/Fenom/Render.php @@ -90,8 +90,7 @@ class Render extends \ArrayObject $this->_time = $props["time"]; $this->_depends = $props["depends"]; $this->_macros = $props["macros"]; -// $this->_blocks = $props["blocks"]; - $this->_code = $code; + $this->_code = $code; } /** @@ -249,19 +248,6 @@ class Render extends \ArrayObject public function __get($name) { - if ($name == 'info') { - return array( - 'name' => $this->_name, - 'schema' => $this->_scm, - 'time' => $this->_time - ); - } else { - return null; - } - } - - public function __isset($name) - { - return $name == 'info'; + return $this->$name = null; } } diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index 09bf5e6..536ccd5 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -268,7 +268,7 @@ class Template extends Render throw new CompileException("Unclosed tag" . (count($_names) > 1 ? "s" : "") . ": " . implode( ", ", $_names - ), 0, 1, $this->_name, $scope->line); // $scope already defined there! + ), 0, 1, $this->_name, $scope->line); // for PHPStorm: $scope already defined there! } $this->_src = ""; // cleanup if ($this->_post) { @@ -662,7 +662,7 @@ class Template extends Render // parse term $term = $this->parseTerm($tokens, $var); // term of the expression if ($term !== false) { - if ($this->_options & Fenom::FORCE_VERIFY) { + if ($var && ($this->_options & Fenom::FORCE_VERIFY)) { $term = '(isset(' . $term . ') ? ' . $term . ' : null)'; $var = false; } @@ -728,6 +728,10 @@ class Template extends Render if ($tokens->is(T_LNUMBER, T_DNUMBER)) { $concat[] = "strval(" . $this->parseTerm($tokens) . ")"; } else { + if($tokens->is('~')) { + $tokens->next(); + $concat[] = " "; + } if(!$concat[] = $this->parseTerm($tokens)) { throw new UnexpectedTokenException($tokens); } @@ -788,7 +792,8 @@ class Template extends Render } return $code; } elseif ($tokens->is('$')) { - $var = $this->parseAccessor($tokens, $is_var); + $is_var = false; + $var = $this->parseAccessor($tokens); return $unary . $var; } elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) { return $unary . $tokens->getAndNext() . $this->parseVariable($tokens); @@ -913,44 +918,18 @@ class Template extends Render /** * Parse accessor + * @param Tokenizer $tokens + * @return string */ - public function parseAccessor(Tokenizer $tokens, &$is_var) + public function parseAccessor(Tokenizer $tokens) { - $is_var = false; - $vars = array( - 'get' => '$_GET', - 'post' => '$_POST', - 'session' => '$_SESSION', - 'cookie' => '$_COOKIE', - 'request' => '$_REQUEST', - 'files' => '$_FILES', - 'globals' => '$GLOBALS', - 'server' => '$_SERVER', - 'env' => '$_ENV', - 'tpl' => '$tpl->info' - ); - if ($this->_options & Fenom::DENY_ACCESSOR) { - throw new \LogicException("Accessor are disabled"); + $accessor = $tokens->need('$')->next()->need('.')->next()->current(); + $callback = $this->getStorage()->getAccessor($accessor); + if($callback) { + return call_user_func($callback, $tokens->next(), $this); + } else { + throw new \RuntimeException("Unknown accessor '$accessor'"); } - $key = $tokens->need('$')->next()->need('.')->next()->current(); - $tokens->next(); - if (isset($vars[$key])) { - $is_var = true; - return $this->parseVariable($tokens, $vars[$key]); - } - switch ($key) { - case 'const': - $tokens->need('.')->next(); - $var = '@constant(' . var_export($this->parseName($tokens), true) . ')'; - break; - case 'version': - $var = '\Fenom::VERSION'; - break; - default: - throw new UnexpectedTokenException($tokens->back()); - } - - return $var; } /** diff --git a/src/Fenom/Tokenizer.php b/src/Fenom/Tokenizer.php index bc67cde..4a0ad6d 100644 --- a/src/Fenom/Tokenizer.php +++ b/src/Fenom/Tokenizer.php @@ -11,17 +11,6 @@ namespace Fenom; use Fenom\Error\UnexpectedTokenException; -/** - * for PHP <5.4 compatible - */ -defined('T_INSTEADOF') || define('T_INSTEADOF', 341); -defined('T_TRAIT') || define('T_TRAIT', 355); -defined('T_TRAIT_C') || define('T_TRAIT_C', 365); -/** - * for PHP <5.5 compatible - */ -defined('T_YIELD') || define('T_YIELD', 267); - /** * Each token have structure * - Token (constant T_* or text) @@ -93,154 +82,62 @@ class Tokenizer */ public static $macros = array( self::MACRO_STRING => array( - \T_ABSTRACT => 1, - \T_ARRAY => 1, - \T_AS => 1, - \T_BREAK => 1, - \T_BREAK => 1, - \T_CASE => 1, - \T_CATCH => 1, - \T_CLASS => 1, - \T_CLASS_C => 1, - \T_CLONE => 1, - \T_CONST => 1, - \T_CONTINUE => 1, - \T_DECLARE => 1, - \T_DEFAULT => 1, - \T_DIR => 1, - \T_DO => 1, - \T_ECHO => 1, - \T_ELSE => 1, - \T_ELSEIF => 1, - \T_EMPTY => 1, - \T_ENDDECLARE => 1, - \T_ENDFOR => 1, - \T_ENDFOREACH => 1, - \T_ENDIF => 1, - \T_ENDSWITCH => 1, - \T_ENDWHILE => 1, - \T_EVAL => 1, - \T_EXIT => 1, - \T_EXTENDS => 1, - \T_FILE => 1, - \T_FINAL => 1, - \T_FOR => 1, - \T_FOREACH => 1, - \T_FUNCTION => 1, - \T_FUNC_C => 1, - \T_GLOBAL => 1, - \T_GOTO => 1, - \T_HALT_COMPILER => 1, - \T_IF => 1, - \T_IMPLEMENTS => 1, - \T_INCLUDE => 1, - \T_INCLUDE_ONCE => 1, - \T_INSTANCEOF => 1, - \T_INSTEADOF => 1, - \T_INTERFACE => 1, - \T_ISSET => 1, - \T_LINE => 1, - \T_LIST => 1, - \T_LOGICAL_AND => 1, - \T_LOGICAL_OR => 1, - \T_LOGICAL_XOR => 1, - \T_METHOD_C => 1, - \T_NAMESPACE => 1, - \T_NS_C => 1, - \T_NEW => 1, - \T_PRINT => 1, - \T_PRIVATE => 1, - \T_PUBLIC => 1, - \T_PROTECTED => 1, - \T_REQUIRE => 1, - \T_REQUIRE_ONCE => 1, - \T_RETURN => 1, - \T_RETURN => 1, - \T_STRING => 1, - \T_SWITCH => 1, - \T_THROW => 1, - \T_TRAIT => 1, - \T_TRAIT_C => 1, - \T_TRY => 1, - \T_UNSET => 1, - \T_USE => 1, - \T_VAR => 1, - \T_WHILE => 1, - \T_YIELD => 1 + \T_ABSTRACT => 1, \T_ARRAY => 1, \T_AS => 1, \T_BREAK => 1, + \T_BREAK => 1, \T_CASE => 1, \T_CATCH => 1, \T_CLASS => 1, + \T_CLASS_C => 1, \T_CLONE => 1, \T_CONST => 1, \T_CONTINUE => 1, + \T_DECLARE => 1, \T_DEFAULT => 1, \T_DIR => 1, \T_DO => 1, + \T_ECHO => 1, \T_ELSE => 1, \T_ELSEIF => 1, \T_EMPTY => 1, + \T_ENDDECLARE => 1, \T_ENDFOR => 1, \T_ENDFOREACH => 1, \T_ENDIF => 1, + \T_ENDSWITCH => 1, \T_ENDWHILE => 1, \T_EVAL => 1, \T_EXIT => 1, + \T_EXTENDS => 1, \T_FILE => 1, \T_FINAL => 1, \T_FOR => 1, + \T_FOREACH => 1, \T_FUNCTION => 1, \T_FUNC_C => 1, \T_GLOBAL => 1, + \T_GOTO => 1, \T_HALT_COMPILER => 1, \T_IF => 1, \T_IMPLEMENTS => 1, + \T_INCLUDE => 1, \T_INCLUDE_ONCE => 1, \T_INSTANCEOF => 1, 341 /* T_INSTEADOF */ => 1, + \T_INTERFACE => 1, \T_ISSET => 1, \T_LINE => 1, \T_LIST => 1, + \T_LOGICAL_AND => 1, \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_METHOD_C => 1, + \T_NAMESPACE => 1, \T_NS_C => 1, \T_NEW => 1, \T_PRINT => 1, + \T_PRIVATE => 1, \T_PUBLIC => 1, \T_PROTECTED => 1, \T_REQUIRE => 1, + \T_REQUIRE_ONCE => 1, \T_RETURN => 1, \T_RETURN => 1, \T_STRING => 1, + \T_SWITCH => 1, \T_THROW => 1, 355 /* T_TRAIT */ => 1, 365 /* T_TRAIT_C */ => 1, + \T_TRY => 1, \T_UNSET => 1, \T_USE => 1, \T_VAR => 1, + \T_WHILE => 1, 267 /* T_YIELD */ => 1 ), self::MACRO_INCDEC => array( - \T_INC => 1, - \T_DEC => 1 + \T_INC => 1, \T_DEC => 1 ), self::MACRO_UNARY => array( - "!" => 1, - "~" => 1, - "-" => 1 + "!" => 1, "~" => 1, "-" => 1 ), self::MACRO_BINARY => array( - \T_BOOLEAN_AND => 1, - \T_BOOLEAN_OR => 1, - \T_IS_GREATER_OR_EQUAL => 1, - \T_IS_EQUAL => 1, - \T_IS_IDENTICAL => 1, - \T_IS_NOT_EQUAL => 1, - \T_IS_NOT_IDENTICAL => 1, - \T_IS_SMALLER_OR_EQUAL => 1, - \T_LOGICAL_AND => 1, - \T_LOGICAL_OR => 1, - \T_LOGICAL_XOR => 1, - \T_SL => 1, - \T_SR => 1, - "+" => 1, - "-" => 1, - "*" => 1, - "/" => 1, - ">" => 1, - "<" => 1, - "^" => 1, - "%" => 1, + \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_IS_GREATER_OR_EQUAL => 1, + \T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, \T_IS_NOT_EQUAL => 1, + \T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, \T_LOGICAL_AND => 1, + \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_SL => 1, + \T_SR => 1, "+" => 1, "-" => 1, + "*" => 1, "/" => 1, ">" => 1, + "<" => 1, "^" => 1, "%" => 1, "&" => 1 ), self::MACRO_BOOLEAN => array( - \T_LOGICAL_OR => 1, - \T_LOGICAL_XOR => 1, - \T_BOOLEAN_AND => 1, - \T_BOOLEAN_OR => 1, + \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, + \T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_LOGICAL_AND => 1 ), self::MACRO_MATH => array( - "+" => 1, - "-" => 1, - "*" => 1, - "/" => 1, - "^" => 1, - "%" => 1, - "&" => 1, - "|" => 1 + "+" => 1, "-" => 1, "*" => 1, + "/" => 1, "^" => 1, "%" => 1, + "&" => 1, "|" => 1 ), self::MACRO_COND => array( - \T_IS_EQUAL => 1, - \T_IS_IDENTICAL => 1, - ">" => 1, - "<" => 1, - \T_SL => 1, - \T_SR => 1, - \T_IS_NOT_EQUAL => 1, - \T_IS_NOT_IDENTICAL => 1, - \T_IS_SMALLER_OR_EQUAL => 1, + \T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, ">" => 1, + "<" => 1, \T_SL => 1, \T_SR => 1, + \T_IS_NOT_EQUAL => 1, \T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, ), self::MACRO_EQUALS => array( - \T_AND_EQUAL => 1, - \T_DIV_EQUAL => 1, - \T_MINUS_EQUAL => 1, - \T_MOD_EQUAL => 1, - \T_MUL_EQUAL => 1, - \T_OR_EQUAL => 1, - \T_PLUS_EQUAL => 1, - \T_SL_EQUAL => 1, - \T_SR_EQUAL => 1, - \T_XOR_EQUAL => 1, - '=' => 1, + \T_AND_EQUAL => 1, \T_DIV_EQUAL => 1, \T_MINUS_EQUAL => 1, + \T_MOD_EQUAL => 1, \T_MUL_EQUAL => 1, \T_OR_EQUAL => 1, + \T_PLUS_EQUAL => 1, \T_SL_EQUAL => 1, \T_SR_EQUAL => 1, + \T_XOR_EQUAL => 1, '=' => 1, ), self::MACRO_SCALAR => array( \T_LNUMBER => 1, diff --git a/tests/TestCase.php b/tests/TestCase.php index f1b40a4..872e87c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -276,9 +276,13 @@ class TestCase extends \PHPUnit_Framework_TestCase } } +const HELPER_CONSTANT = 'helper.const'; + class Helper { + const CONSTANT = "helper.class.const"; + public $word = 'helper'; public function __construct($word) @@ -306,3 +310,7 @@ class Helper } } +function helper_func($string, $pad = 10) { + return str_pad($string, $pad, "."); +} + diff --git a/tests/cases/Fenom/AccessorTest.php b/tests/cases/Fenom/AccessorTest.php new file mode 100644 index 0000000..9587ff4 --- /dev/null +++ b/tests/cases/Fenom/AccessorTest.php @@ -0,0 +1,144 @@ +exec('{$.'.$var.'.one}', self::getVars(), "{$var}1"); + $this->exec('{$.'.$var.'.undefined}', self::getVars(), ""); + } + + public static function providerTpl() + { + return array( + array("name"), + array("scm"), + array("basename"), + array("options"), + array("time"), + ); + } + + /** + * @dataProvider providerTpl + * @param string $name + */ + public function testTpl($name) + { + $this->tpl("accessor.tpl", '{$.tpl.'.$name.'}'); + $tpl = $this->fenom->setOptions(\Fenom::FORCE_VERIFY)->getTemplate('accessor.tpl'); + $this->assertSame(strval($tpl->{"get$name"}()), $tpl->fetch(self::getVars())); + } + + public function testVersion() + { + $this->assertRender('{$.version}', \Fenom::VERSION); + } + + public static function providerConst() + { + return array( + array("$.const.PHP_VERSION_ID", PHP_VERSION_ID), + array('$.const.UNDEFINED', ''), + array("$.const.FENOM_RESOURCES", FENOM_RESOURCES), + array("$.const.Fenom.HELPER_CONSTANT", HELPER_CONSTANT), + array("$.const.Fenom.UNDEFINED", ''), + array("$.const.Fenom::VERSION", \Fenom::VERSION), + array("$.const.Fenom::UNDEFINED", ''), + array("$.const.Fenom.Helper::CONSTANT", Helper::CONSTANT), + array("$.const.Fenom.Helper::UNDEFINED", ''), + ); + } + + /** + * @dataProvider providerConst + * @param $tpl + * @param $value + * @group const + */ + public function testConst($tpl, $value) + { + $this->assertRender('{'.$tpl.'}', strval($value)); + } + + + public static function providerPHP() { + return array( + array('$.php.strrev("string")', strrev("string")), + array('$.php.Fenom.helper_func("string", 12)', helper_func("string", 12)), + array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string")), + ); + } + + /** + * @dataProvider providerPHP + * @group php + */ + public function testPHP($tpl, $result) { + $this->assertRender('{'.$tpl.'}', $result); + } + + + public static function providerAccessor() + { + return array( + array('{$.get.one}', 'get1'), + array('{$.post.one}', 'post1'), + array('{$.request.one}', 'request1'), + array('{$.session.one}', 'session1'), + array('{$.files.one}', 'files1'), + array('{$.globals.one}', 'globals1'), + array('{$.cookie.one}', 'cookie1'), + array('{$.server.one}', 'server1'), + array('{"string"|append:"_":$.get.one}', 'string_get1'), + array('{$.get.one?}', '1'), + array('{$.get.one is set}', '1'), + array('{$.get.two is empty}', '1'), + array('{$.version}', \Fenom::VERSION), + array('{$.tpl.name}', 'runtime.tpl'), + array('{$.tpl.time}', '0'), + array('{$.tpl.schema}', ''), + ); + } + + + public static function providerAccessorInvalid() + { + return array( + array('{$.nope.one}', 'Fenom\Error\CompileException', "Unexpected token 'nope'"), + array('{$.get.one}', 'Fenom\Error\SecurityException', 'Accessor are disabled', \Fenom::DENY_ACCESSOR), + ); + } +} \ No newline at end of file diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index 42db34a..4edcf69 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -17,15 +17,7 @@ class TemplateTest extends TestCase { parent::setUp(); $this->tpl('welcome.tpl', 'Welcome, {$username} ({$email})'); - $_GET['one'] = 'get1'; - $_POST['one'] = 'post1'; - $_REQUEST['one'] = 'request1'; - $_FILES['one'] = 'files1'; - $_SERVER['one'] = 'server1'; - $_SESSION['one'] = 'session1'; - $GLOBALS['one'] = 'globals1'; - $_ENV['one'] = 'env1'; - $_COOKIE['one'] = 'cookie1'; + } public static function providerVars() @@ -1217,40 +1209,6 @@ class TemplateTest extends TestCase ); } - public static function providerAccessor() - { - return array( - array('{$.get.one}', 'get1'), - array('{$.post.one}', 'post1'), - array('{$.request.one}', 'request1'), - array('{$.session.one}', 'session1'), - array('{$.files.one}', 'files1'), - array('{$.globals.one}', 'globals1'), - array('{$.cookie.one}', 'cookie1'), - array('{$.server.one}', 'server1'), - array('{$.const.PHP_EOL}', PHP_EOL), - array('{$.const.MY}', ''), - array('{$.version}', Fenom::VERSION), - array('{"string"|append:"_":$.get.one}', 'string_get1'), - array('{$.get.one?}', '1'), - array('{$.get.one is set}', '1'), - array('{$.get.two is empty}', '1'), - array('{$.version}', Fenom::VERSION), - array('{$.tpl?}', '1'), - array('{$.tpl.name}', 'runtime.tpl'), - array('{$.tpl.time}', '0'), - array('{$.tpl.schema}', ''), - ); - } - - public static function providerAccessorInvalid() - { - return array( - array('{$.nope.one}', 'Fenom\Error\CompileException', "Unexpected token 'nope'"), - array('{$.get.one}', 'Fenom\Error\SecurityException', 'Accessor are disabled', Fenom::DENY_ACCESSOR), - ); - } - public function providerStatic() { return array( @@ -1622,19 +1580,19 @@ class TemplateTest extends TestCase * @group accessor * @dataProvider providerAccessor */ - public function testAccessor($code, $result) - { - $this->exec($code, self::getVars(), $result); - } +// public function testAccessor($code, $result) +// { +// $this->exec($code, self::getVars(), $result); +// } /** * @group accessor * @dataProvider providerAccessorInvalid */ - public function testAccessorInvalid($code, $exception, $message, $options = 0) - { - $this->execError($code, $exception, $message, $options); - } +// public function testAccessorInvalid($code, $exception, $message, $options = 0) +// { +// $this->execError($code, $exception, $message, $options); +// } /** * @group static