mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Merge remote-tracking branch 'refs/remotes/origin/develop'
This commit is contained in:
commit
c1d0268a94
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,6 +1,19 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
## 1.2.0
|
||||
|
||||
- Feature #28: macros may be called recursively
|
||||
- Feature #29: add {unset} tag
|
||||
- Add hook for loading modifiers and tags
|
||||
- Add hook for loading modifiers and tags
|
||||
- Feature #3: Add string operator '~'
|
||||
- Improve parsers: parserExp, parserVar, parserVariable, parserMacro
|
||||
- Fix ternary bug
|
||||
- Bugs--
|
||||
- Tests++
|
||||
- Docs++
|
||||
|
||||
## 1.1.0
|
||||
|
||||
- Bug #19: Bug with "if" expressions starting with "("
|
||||
|
@ -3,14 +3,14 @@ Fenom - Template Engine for PHP
|
||||
|
||||
> Composer package: `{"fenom/fenom": "dev-master"}`. See on [Packagist.org](https://packagist.org/packages/bzick/fenom)
|
||||
|
||||
[![Build Status](https://travis-ci.org/bzick/fenom.png?branch=master)](https://travis-ci.org/bzick/fenom)
|
||||
[![Build Status](https://travis-ci.org/bzick/fenom.png?branch=master)](https://travis-ci.org/fenom/fenom)
|
||||
## [Usage](./docs/usage.md) :: [Documentation](./docs/readme.md) :: [Benchmark](./docs/benchmark.md) :: [Articles](./docs/articles.md)
|
||||
|
||||
* Simple [syntax](./docs/syntax.md)
|
||||
* [Fast](./docs/benchmark.md)
|
||||
* [Secure](./docs/settings.md)
|
||||
* [Simple](./ideology.md)
|
||||
* [Flexible](./docs/readme.md#extends)
|
||||
* Simple
|
||||
* [Flexible](./docs/ext/extensions.md)
|
||||
* [Lightweight](./docs/benchmark.md#stats)
|
||||
* [Powerful](./docs/readme.md)
|
||||
* Easy to use:
|
||||
|
@ -1,9 +1,8 @@
|
||||
{
|
||||
"name": "fenom/fenom",
|
||||
"type": "library",
|
||||
"description": "Fenom - fast template engine for PHP",
|
||||
"homepage": "http://bzick.github.io/fenom/",
|
||||
"keywords": ["fenom", "template", "templating", "cytro"],
|
||||
"description": "Fenom - excellent template engine for PHP",
|
||||
"keywords": ["fenom", "template", "templating", "templater"],
|
||||
"license": "BSD-3",
|
||||
"authors": [
|
||||
{
|
||||
|
@ -1,5 +1,9 @@
|
||||
Extensions
|
||||
==========
|
||||
|
||||
* [Extra pack](https://github.com/bzick/fenom-extra) basic add-ons for web-base project.
|
||||
* *Smarty pack* (planned) Smarty3 adapter
|
||||
* [Extra pack](https://github.com/bzick/fenom-extra) of add-ons for Fenom template engine.
|
||||
* Tools for static files (css, js).
|
||||
* Global variables
|
||||
* Allow more hooks for extending
|
||||
* Add variable container
|
||||
* You can only use the necessary add-ons
|
@ -81,6 +81,10 @@ Operators
|
||||
* `--$a` - decrement the variable and use it
|
||||
* `$a--` - use the variable and decrement it
|
||||
|
||||
### String operator
|
||||
|
||||
* `$a ~ $b` - return concatenation of variables `$a` and `$b`
|
||||
|
||||
### Ternary operator
|
||||
|
||||
* `$a ? $b : $c` - returns `$b` if `$a` is not empty, and `$c` otherwise
|
||||
|
247
src/Fenom.php
247
src/Fenom.php
@ -7,16 +7,17 @@
|
||||
* For the full copyright and license information, please view the license.md
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
use Fenom\Template,
|
||||
Fenom\ProviderInterface;
|
||||
use Fenom\ProviderInterface;
|
||||
use Fenom\Template;
|
||||
|
||||
/**
|
||||
* Fenom Template Engine
|
||||
*
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Fenom {
|
||||
const VERSION = '1.1';
|
||||
class Fenom
|
||||
{
|
||||
const VERSION = '1.2';
|
||||
|
||||
/* Actions */
|
||||
const INLINE_COMPILER = 1;
|
||||
@ -27,7 +28,7 @@ class Fenom {
|
||||
|
||||
/* Options */
|
||||
const DENY_METHODS = 0x10;
|
||||
const DENY_INLINE_FUNCS = 0x20;
|
||||
const DENY_NATIVE_FUNCS = 0x20;
|
||||
const FORCE_INCLUDE = 0x40;
|
||||
const AUTO_RELOAD = 0x80;
|
||||
const FORCE_COMPILE = 0x100;
|
||||
@ -37,6 +38,9 @@ class Fenom {
|
||||
const AUTO_TRIM = 0x1000; // reserved
|
||||
const DENY_STATICS = 0x2000; // reserved
|
||||
|
||||
/* @deprecated */
|
||||
const DENY_INLINE_FUNCS = 0x20;
|
||||
|
||||
/* Default parsers */
|
||||
const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose';
|
||||
const DEFAULT_FUNC_PARSER = 'Fenom\Compiler::stdFuncParser';
|
||||
@ -44,13 +48,15 @@ class Fenom {
|
||||
const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose';
|
||||
const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser';
|
||||
|
||||
const MAX_MACRO_RECURSIVE = 32;
|
||||
|
||||
/**
|
||||
* @var int[] of possible options, as associative array
|
||||
* @see setOptions
|
||||
*/
|
||||
private static $_options_list = array(
|
||||
"disable_methods" => self::DENY_METHODS,
|
||||
"disable_native_funcs" => self::DENY_INLINE_FUNCS,
|
||||
"disable_native_funcs" => self::DENY_NATIVE_FUNCS,
|
||||
"disable_cache" => self::DISABLE_CACHE,
|
||||
"force_compile" => self::FORCE_COMPILE,
|
||||
"auto_reload" => self::AUTO_RELOAD,
|
||||
@ -61,10 +67,26 @@ class Fenom {
|
||||
"disable_statics" => self::DENY_STATICS,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
public $pre_filters = array();
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
public $filters = array();
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
public $post_filters = array();
|
||||
|
||||
/**
|
||||
* @var Fenom\Render[] Templates storage
|
||||
*/
|
||||
protected $_storage = array();
|
||||
|
||||
/**
|
||||
* @var string compile directory
|
||||
*/
|
||||
@ -75,10 +97,6 @@ class Fenom {
|
||||
*/
|
||||
protected $_options = 0;
|
||||
|
||||
protected $_on_pre_cmp = array();
|
||||
protected $_on_cmp = array();
|
||||
protected $_on_post_cmp = array();
|
||||
|
||||
/**
|
||||
* @var ProviderInterface
|
||||
*/
|
||||
@ -104,7 +122,6 @@ class Fenom {
|
||||
"unescape" => 'Fenom\Modifier::unescape',
|
||||
"strip" => 'Fenom\Modifier::strip',
|
||||
"length" => 'Fenom\Modifier::length',
|
||||
"default" => 'Fenom\Modifier::defaultValue',
|
||||
"iterable" => 'Fenom\Modifier::isIterable'
|
||||
);
|
||||
|
||||
@ -230,6 +247,10 @@ class Fenom {
|
||||
'type' => self::BLOCK_COMPILER,
|
||||
'open' => 'Fenom\Compiler::autoescapeOpen',
|
||||
'close' => 'Fenom\Compiler::autoescapeClose'
|
||||
),
|
||||
'unset' => array(
|
||||
'type' => self::INLINE_COMPILER,
|
||||
'parser' => 'Fenom\Compiler::tagUnset'
|
||||
)
|
||||
);
|
||||
|
||||
@ -242,7 +263,8 @@ class Fenom {
|
||||
* @throws InvalidArgumentException
|
||||
* @return Fenom
|
||||
*/
|
||||
public static function factory($source, $compile_dir = '/tmp', $options = 0) {
|
||||
public static function factory($source, $compile_dir = '/tmp', $options = 0)
|
||||
{
|
||||
if (is_string($source)) {
|
||||
$provider = new Fenom\Provider($source);
|
||||
} elseif ($source instanceof ProviderInterface) {
|
||||
@ -262,7 +284,8 @@ class Fenom {
|
||||
/**
|
||||
* @param Fenom\ProviderInterface $provider
|
||||
*/
|
||||
public function __construct(Fenom\ProviderInterface $provider) {
|
||||
public function __construct(Fenom\ProviderInterface $provider)
|
||||
{
|
||||
$this->_provider = $provider;
|
||||
}
|
||||
|
||||
@ -272,7 +295,8 @@ class Fenom {
|
||||
* @param string $dir directory to store compiled templates in
|
||||
* @return Fenom
|
||||
*/
|
||||
public function setCompileDir($dir) {
|
||||
public function setCompileDir($dir)
|
||||
{
|
||||
$this->_compile_dir = $dir;
|
||||
return $this;
|
||||
}
|
||||
@ -280,24 +304,50 @@ class Fenom {
|
||||
/**
|
||||
*
|
||||
* @param callable $cb
|
||||
* @return self
|
||||
*/
|
||||
public function addPreCompileFilter($cb) {
|
||||
$this->_on_pre_cmp[] = $cb;
|
||||
public function addPreFilter($cb)
|
||||
{
|
||||
$this->pre_filters[] = $cb;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPreFilters()
|
||||
{
|
||||
return $this->pre_filters;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param callable $cb
|
||||
* @return self
|
||||
*/
|
||||
public function addPostCompileFilter($cb) {
|
||||
$this->_on_post_cmp[] = $cb;
|
||||
public function addPostFilter($cb)
|
||||
{
|
||||
$this->post_filters[] = $cb;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getPostFilters()
|
||||
{
|
||||
return $this->post_filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $cb
|
||||
* @return self
|
||||
*/
|
||||
public function addCompileFilter($cb) {
|
||||
$this->_on_cmp[] = $cb;
|
||||
public function addFilter($cb)
|
||||
{
|
||||
$this->filters[] = $cb;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,7 +357,8 @@ class Fenom {
|
||||
* @param string $callback the modifier callback
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addModifier($modifier, $callback) {
|
||||
public function addModifier($modifier, $callback)
|
||||
{
|
||||
$this->_modifiers[$modifier] = $callback;
|
||||
return $this;
|
||||
}
|
||||
@ -319,7 +370,8 @@ class Fenom {
|
||||
* @param callable $parser
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addCompiler($compiler, $parser) {
|
||||
public function addCompiler($compiler, $parser)
|
||||
{
|
||||
$this->_actions[$compiler] = array(
|
||||
'type' => self::INLINE_COMPILER,
|
||||
'parser' => $parser
|
||||
@ -332,7 +384,8 @@ class Fenom {
|
||||
* @param string|object $storage
|
||||
* @return $this
|
||||
*/
|
||||
public function addCompilerSmart($compiler, $storage) {
|
||||
public function addCompilerSmart($compiler, $storage)
|
||||
{
|
||||
if (method_exists($storage, "tag" . $compiler)) {
|
||||
$this->_actions[$compiler] = array(
|
||||
'type' => self::INLINE_COMPILER,
|
||||
@ -351,7 +404,8 @@ 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,
|
||||
@ -369,7 +423,8 @@ class Fenom {
|
||||
* @throws LogicException
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array()) {
|
||||
public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array())
|
||||
{
|
||||
$c = array(
|
||||
'type' => self::BLOCK_COMPILER,
|
||||
"tags" => array(),
|
||||
@ -405,7 +460,8 @@ class Fenom {
|
||||
* @param callable|string $parser
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) {
|
||||
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER)
|
||||
{
|
||||
$this->_actions[$function] = array(
|
||||
'type' => self::INLINE_FUNCTION,
|
||||
'parser' => $parser,
|
||||
@ -419,7 +475,8 @@ class Fenom {
|
||||
* @param callable $callback
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addFunctionSmart($function, $callback) {
|
||||
public function addFunctionSmart($function, $callback)
|
||||
{
|
||||
$this->_actions[$function] = array(
|
||||
'type' => self::INLINE_FUNCTION,
|
||||
'parser' => self::SMART_FUNC_PARSER,
|
||||
@ -435,7 +492,8 @@ class Fenom {
|
||||
* @param callable|string $parser_close
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE) {
|
||||
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,
|
||||
@ -449,7 +507,8 @@ class Fenom {
|
||||
* @param array $funcs
|
||||
* @return Fenom
|
||||
*/
|
||||
public function addAllowedFunctions(array $funcs) {
|
||||
public function addAllowedFunctions(array $funcs)
|
||||
{
|
||||
$this->_allowed_funcs = $this->_allowed_funcs + array_flip($funcs);
|
||||
return $this;
|
||||
}
|
||||
@ -457,40 +516,75 @@ class Fenom {
|
||||
/**
|
||||
* Return modifier function
|
||||
*
|
||||
* @param $modifier
|
||||
* @param string $modifier
|
||||
* @param Fenom\Template $template
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getModifier($modifier) {
|
||||
public function getModifier($modifier, Template $template = null)
|
||||
{
|
||||
if (isset($this->_modifiers[$modifier])) {
|
||||
return $this->_modifiers[$modifier];
|
||||
} elseif ($this->isAllowedFunction($modifier)) {
|
||||
return $modifier;
|
||||
} else {
|
||||
throw new \Exception("Modifier $modifier not found");
|
||||
return $this->_loadModifier($modifier, $template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return function
|
||||
*
|
||||
* @param string $function
|
||||
* @return string|bool
|
||||
* @param string $modifier
|
||||
* @param Fenom\Template $template
|
||||
* @return bool
|
||||
*/
|
||||
public function getFunction($function) {
|
||||
if(isset($this->_actions[$function])) {
|
||||
return $this->_actions[$function];
|
||||
} else {
|
||||
protected function _loadModifier($modifier, $template)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function
|
||||
* @param Fenom\Template $template
|
||||
* @return bool|string
|
||||
* @deprecated
|
||||
*/
|
||||
public function getFunction($function, Template $template = null)
|
||||
{
|
||||
return $this->getTag($function, $template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tag info
|
||||
*
|
||||
* @param string $tag
|
||||
* @param Fenom\Template $template
|
||||
* @return string|bool
|
||||
*/
|
||||
public function getTag($tag, Template $template = null)
|
||||
{
|
||||
if (isset($this->_actions[$tag])) {
|
||||
return $this->_actions[$tag];
|
||||
} else {
|
||||
return $this->_loadTag($tag, $template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tag
|
||||
* @param Fenom\Template $template
|
||||
* @return bool
|
||||
*/
|
||||
protected function _loadTag($tag, $template)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedFunction($function) {
|
||||
if($this->_options & self::DENY_INLINE_FUNCS) {
|
||||
public function isAllowedFunction($function)
|
||||
{
|
||||
if ($this->_options & self::DENY_NATIVE_FUNCS) {
|
||||
return isset($this->_allowed_funcs[$function]);
|
||||
} else {
|
||||
return is_callable($function);
|
||||
@ -501,7 +595,8 @@ class Fenom {
|
||||
* @param string $tag
|
||||
* @return array
|
||||
*/
|
||||
public function getTagOwners($tag) {
|
||||
public function getTagOwners($tag)
|
||||
{
|
||||
$tags = array();
|
||||
foreach ($this->_actions as $owner => $params) {
|
||||
if (isset($params["tags"][$tag])) {
|
||||
@ -517,20 +612,17 @@ class Fenom {
|
||||
* @param string $scm scheme name
|
||||
* @param Fenom\ProviderInterface $provider provider object
|
||||
*/
|
||||
public function addProvider($scm, \Fenom\ProviderInterface $provider) {
|
||||
public function addProvider($scm, \Fenom\ProviderInterface $provider)
|
||||
{
|
||||
$this->_providers[$scm] = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options. May be bitwise mask of constants DENY_METHODS, DENY_INLINE_FUNCS, DENY_SET_VARS, INCLUDE_SOURCES,
|
||||
* FORCE_COMPILE, CHECK_MTIME, or associative array with boolean values:
|
||||
* disable_methods - disable all calls method in template
|
||||
* disable_native_funcs - disable all native PHP functions in template
|
||||
* force_compile - recompile template every time (very slow!)
|
||||
* compile_check - check template modifications (slow!)
|
||||
* Set options
|
||||
* @param int|array $options
|
||||
*/
|
||||
public function setOptions($options) {
|
||||
public function setOptions($options)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
$options = self::_makeMask($options, self::$_options_list, $this->_options);
|
||||
}
|
||||
@ -542,7 +634,8 @@ class Fenom {
|
||||
* Get options as bits
|
||||
* @return int
|
||||
*/
|
||||
public function getOptions() {
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->_options;
|
||||
}
|
||||
|
||||
@ -551,7 +644,8 @@ class Fenom {
|
||||
* @return Fenom\ProviderInterface
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getProvider($scm = false) {
|
||||
public function getProvider($scm = false)
|
||||
{
|
||||
if ($scm) {
|
||||
if (isset($this->_providers[$scm])) {
|
||||
return $this->_providers[$scm];
|
||||
@ -568,8 +662,9 @@ class Fenom {
|
||||
*
|
||||
* @return Fenom\Template
|
||||
*/
|
||||
public function getRawTemplate() {
|
||||
return new \Fenom\Template($this, $this->_options);
|
||||
public function getRawTemplate()
|
||||
{
|
||||
return new Template($this, $this->_options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -579,7 +674,8 @@ class Fenom {
|
||||
* @param array $vars array of data for template
|
||||
* @return Fenom\Render
|
||||
*/
|
||||
public function display($template, array $vars = array()) {
|
||||
public function display($template, array $vars = array())
|
||||
{
|
||||
return $this->getTemplate($template)->display($vars);
|
||||
}
|
||||
|
||||
@ -589,7 +685,8 @@ class Fenom {
|
||||
* @param array $vars array of data for template
|
||||
* @return mixed
|
||||
*/
|
||||
public function fetch($template, array $vars = array()) {
|
||||
public function fetch($template, array $vars = array())
|
||||
{
|
||||
return $this->getTemplate($template)->fetch($vars);
|
||||
}
|
||||
|
||||
@ -602,7 +699,8 @@ class Fenom {
|
||||
* @param float $chunk
|
||||
* @return array
|
||||
*/
|
||||
public function pipe($template, $callback, array $vars = array(), $chunk = 1e6) {
|
||||
public function pipe($template, $callback, array $vars = array(), $chunk = 1e6)
|
||||
{
|
||||
ob_start($callback, $chunk, true);
|
||||
$data = $this->getTemplate($template)->display($vars);
|
||||
ob_end_flush();
|
||||
@ -616,7 +714,8 @@ class Fenom {
|
||||
* @param int $options additional options and flags
|
||||
* @return Fenom\Template
|
||||
*/
|
||||
public function getTemplate($template, $options = 0) {
|
||||
public function getTemplate($template, $options = 0)
|
||||
{
|
||||
$options |= $this->_options;
|
||||
$key = dechex($options) . "@" . $template;
|
||||
if (isset($this->_storage[$key])) {
|
||||
@ -639,7 +738,8 @@ class Fenom {
|
||||
* @param string $template
|
||||
* @return bool
|
||||
*/
|
||||
public function templateExists($template) {
|
||||
public function templateExists($template)
|
||||
{
|
||||
if ($provider = strstr($template, ":", true)) {
|
||||
if (isset($this->_providers[$provider])) {
|
||||
return $this->_providers[$provider]->templateExists(substr($template, strlen($provider) + 1));
|
||||
@ -657,7 +757,8 @@ class Fenom {
|
||||
* @param int $opts
|
||||
* @return Fenom\Render
|
||||
*/
|
||||
protected function _load($tpl, $opts) {
|
||||
protected function _load($tpl, $opts)
|
||||
{
|
||||
$file_name = $this->_getCacheName($tpl, $opts);
|
||||
if (!is_file($this->_compile_dir . "/" . $file_name)) {
|
||||
return $this->compile($tpl, true, $opts);
|
||||
@ -674,7 +775,8 @@ class Fenom {
|
||||
* @param int $options
|
||||
* @return string
|
||||
*/
|
||||
private function _getCacheName($tpl, $options) {
|
||||
private function _getCacheName($tpl, $options)
|
||||
{
|
||||
$hash = $tpl . ":" . $options;
|
||||
return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
|
||||
}
|
||||
@ -688,9 +790,10 @@ class Fenom {
|
||||
* @throws RuntimeException
|
||||
* @return \Fenom\Template
|
||||
*/
|
||||
public function compile($tpl, $store = true, $options = 0) {
|
||||
public function compile($tpl, $store = true, $options = 0)
|
||||
{
|
||||
$options = $this->_options | $options;
|
||||
$template = Template::factory($this, $options)->load($tpl);
|
||||
$template = $this->getRawTemplate()->load($tpl);
|
||||
if ($store) {
|
||||
$cache = $this->_getCacheName($tpl, $options);
|
||||
$tpl_tmp = tempnam($this->_compile_dir, $cache);
|
||||
@ -711,14 +814,16 @@ class Fenom {
|
||||
/**
|
||||
* Flush internal memory template cache
|
||||
*/
|
||||
public function flush() {
|
||||
public function flush()
|
||||
{
|
||||
$this->_storage = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all compiled templates
|
||||
*/
|
||||
public function clearAllCompiles() {
|
||||
public function clearAllCompiles()
|
||||
{
|
||||
\Fenom\Provider::clean($this->_compile_dir);
|
||||
}
|
||||
|
||||
@ -729,8 +834,9 @@ class Fenom {
|
||||
* @param string $name
|
||||
* @return Fenom\Template
|
||||
*/
|
||||
public function compileCode($code, $name = 'Runtime compile') {
|
||||
return Template::factory($this, $this->_options)->source($name, $code);
|
||||
public function compileCode($code, $name = 'Runtime compile')
|
||||
{
|
||||
return $this->getRawTemplate()->source($name, $code);
|
||||
}
|
||||
|
||||
|
||||
@ -743,7 +849,8 @@ class Fenom {
|
||||
* @return int result, ( $mask | a ) & ~b
|
||||
* @throws \RuntimeException if key from custom assoc doesn't exists into possible values
|
||||
*/
|
||||
private static function _makeMask(array $values, array $options, $mask = 0) {
|
||||
private static function _makeMask(array $values, array $options, $mask = 0)
|
||||
{
|
||||
foreach ($values as $key => $value) {
|
||||
if (isset($options[$key])) {
|
||||
if ($options[$key]) {
|
||||
|
@ -8,6 +8,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Fenom;
|
||||
use Fenom\Error\InvalidUsageException;
|
||||
use Fenom\Error\UnexpectedTokenException;
|
||||
use Fenom\Tokenizer;
|
||||
use Fenom\Template;
|
||||
use Fenom\Scope;
|
||||
@ -17,7 +19,8 @@ use Fenom\Scope;
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Compiler {
|
||||
class Compiler
|
||||
{
|
||||
/**
|
||||
* Tag {include ...}
|
||||
*
|
||||
@ -27,14 +30,24 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagInclude(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagInclude(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
$name = false;
|
||||
// if($tokens->is('[')) {
|
||||
// $tokens->next();
|
||||
// if(!$name && $tokens->is(T_CONSTANT_ENCAPSED_STRING)) {
|
||||
// if($tpl->getStorage()->templateExists($_name = substr($tokens->getAndNext(), 1, -1))) {
|
||||
// $name = $_name;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
$cname = $tpl->parsePlainArg($tokens, $name);
|
||||
$p = $tpl->parseParams($tokens);
|
||||
if ($p) { // if we have additionally variables
|
||||
if ($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
|
||||
$inc = $tpl->getStorage()->compile($name, false);
|
||||
$tpl->addDepend($inc);
|
||||
return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
|
||||
return '$_tpl = (array)$tpl; $tpl->exchangeArray(' . self::toArray($p) . '+$_tpl); ?>' . $inc->getBody() . '<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
|
||||
} else {
|
||||
return '$tpl->getStorage()->getTemplate(' . $cname . ')->display(' . self::toArray($p) . '+(array)$tpl);';
|
||||
}
|
||||
@ -42,7 +55,7 @@ class Compiler {
|
||||
if ($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
|
||||
$inc = $tpl->getStorage()->compile($name, false);
|
||||
$tpl->addDepend($inc);
|
||||
return '$_tpl = (array)$tpl; ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
|
||||
return '$_tpl = (array)$tpl; ?>' . $inc->getBody() . '<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
|
||||
} else {
|
||||
return '$tpl->getStorage()->getTemplate(' . $cname . ')->display((array)$tpl);';
|
||||
}
|
||||
@ -58,7 +71,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function ifOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function ifOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["else"] = false;
|
||||
return 'if(' . $scope->tpl->parseExp($tokens, true) . ') {';
|
||||
}
|
||||
@ -72,7 +86,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagElseIf(Tokenizer $tokens, Scope $scope) {
|
||||
public static function tagElseIf(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
if ($scope["else"]) {
|
||||
throw new InvalidUsageException('Incorrect use of the tag {elseif}');
|
||||
}
|
||||
@ -88,7 +103,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function tagElse(Tokenizer $tokens, Scope $scope) {
|
||||
public static function tagElse(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["else"] = true;
|
||||
return '} else {';
|
||||
}
|
||||
@ -103,7 +119,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function foreachOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function foreachOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$p = array("index" => false, "first" => false, "last" => false);
|
||||
$key = null;
|
||||
$before = $body = array();
|
||||
@ -172,7 +189,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function foreachElse($tokens, Scope $scope) {
|
||||
public static function foreachElse($tokens, Scope $scope)
|
||||
{
|
||||
$scope["no-break"] = $scope["no-continue"] = $scope["else"] = true;
|
||||
return " {$scope['after']} } } else {";
|
||||
}
|
||||
@ -185,7 +203,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function foreachClose($tokens, Scope $scope) {
|
||||
public static function foreachClose($tokens, Scope $scope)
|
||||
{
|
||||
if ($scope["else"]) {
|
||||
return '}';
|
||||
} else {
|
||||
@ -200,7 +219,8 @@ class Compiler {
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public static function forOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function forOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$p = array("index" => false, "first" => false, "last" => false, "step" => 1, "to" => false, "max" => false, "min" => false);
|
||||
$scope["after"] = $before = $body = array();
|
||||
$i = array('', '');
|
||||
@ -255,7 +275,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function forElse(Tokenizer $tokens, Scope $scope) {
|
||||
public static function forElse(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["no-break"] = $scope["no-continue"] = true;
|
||||
$scope["else"] = true;
|
||||
return " } if({$scope['else_cond']}) {";
|
||||
@ -267,7 +288,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function forClose($tokens, Scope $scope) {
|
||||
public static function forClose($tokens, Scope $scope)
|
||||
{
|
||||
if ($scope["else"]) {
|
||||
return '}';
|
||||
} else {
|
||||
@ -281,7 +303,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function whileOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function whileOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
return 'while(' . $scope->tpl->parseExp($tokens, true) . ') {';
|
||||
}
|
||||
|
||||
@ -293,7 +316,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function switchOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function switchOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["no-break"] = $scope["no-continue"] = true;
|
||||
$scope["switch"] = 'switch(' . $scope->tpl->parseExp($tokens, true) . ') {';
|
||||
// lazy init
|
||||
@ -308,7 +332,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function tagCase(Tokenizer $tokens, Scope $scope) {
|
||||
public static function tagCase(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$code = 'case ' . $scope->tpl->parseExp($tokens, true) . ': ';
|
||||
if ($scope["switch"]) {
|
||||
unset($scope["no-break"], $scope["no-continue"]);
|
||||
@ -327,7 +352,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagContinue($tokens, Scope $scope) {
|
||||
public static function tagContinue($tokens, Scope $scope)
|
||||
{
|
||||
if (empty($scope["no-continue"])) {
|
||||
return 'continue;';
|
||||
} else {
|
||||
@ -343,7 +369,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function tagDefault($tokens, Scope $scope) {
|
||||
public static function tagDefault($tokens, Scope $scope)
|
||||
{
|
||||
$code = 'default: ';
|
||||
if ($scope["switch"]) {
|
||||
unset($scope["no-break"], $scope["no-continue"]);
|
||||
@ -362,7 +389,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagBreak($tokens, Scope $scope) {
|
||||
public static function tagBreak($tokens, Scope $scope)
|
||||
{
|
||||
if (empty($scope["no-break"])) {
|
||||
return 'break;';
|
||||
} else {
|
||||
@ -377,7 +405,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagExtends(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagExtends(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
if (!empty($tpl->_extends)) {
|
||||
throw new InvalidUsageException("Only one {extends} allowed");
|
||||
} elseif ($tpl->getStackSize()) {
|
||||
@ -411,7 +440,8 @@ class Compiler {
|
||||
* @param string $body
|
||||
* @param Template $tpl
|
||||
*/
|
||||
public static function extendBody(&$body, $tpl) {
|
||||
public static function extendBody(&$body, $tpl)
|
||||
{
|
||||
$t = $tpl;
|
||||
if ($tpl->uses) {
|
||||
$tpl->blocks += $tpl->uses;
|
||||
@ -452,7 +482,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagUse(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagUse(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
if ($tpl->getStackSize()) {
|
||||
throw new InvalidUsageException("Tags {use} can not be nested");
|
||||
}
|
||||
@ -491,9 +522,9 @@ class Compiler {
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
if ($scope->level > 0) {
|
||||
var_dump("".$scope->tpl);
|
||||
$scope->tpl->_compatible = true;
|
||||
}
|
||||
$scope["cname"] = $scope->tpl->parsePlainArg($tokens, $name);
|
||||
@ -506,7 +537,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function tagBlockClose($tokens, Scope $scope) {
|
||||
public static function tagBlockClose($tokens, Scope $scope)
|
||||
{
|
||||
|
||||
$tpl = $scope->tpl;
|
||||
if (isset($tpl->_extends)) { // is child
|
||||
@ -562,7 +594,8 @@ class Compiler {
|
||||
|
||||
}
|
||||
|
||||
public static function tagParent($tokens, Scope $scope) {
|
||||
public static function tagParent($tokens, Scope $scope)
|
||||
{
|
||||
if (empty($scope->tpl->_extends)) {
|
||||
throw new InvalidUsageException("Tag {parent} may be declared in children");
|
||||
}
|
||||
@ -574,7 +607,8 @@ class Compiler {
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
public static function stdClose() {
|
||||
public static function stdClose()
|
||||
{
|
||||
return '}';
|
||||
}
|
||||
|
||||
@ -587,7 +621,8 @@ class Compiler {
|
||||
* @param Template $tpl
|
||||
* @return string
|
||||
*/
|
||||
public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) {
|
||||
public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
return "$function(" . self::toArray($tpl->parseParams($tokens)) . ', $tpl)';
|
||||
}
|
||||
|
||||
@ -600,7 +635,8 @@ class Compiler {
|
||||
* @param Template $tpl
|
||||
* @return string
|
||||
*/
|
||||
public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) {
|
||||
public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
if (strpos($function, "::")) {
|
||||
list($class, $method) = explode("::", $function, 2);
|
||||
$ref = new \ReflectionMethod($class, $method);
|
||||
@ -629,7 +665,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function stdFuncOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function stdFuncOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["params"] = self::toArray($scope->tpl->parseParams($tokens));
|
||||
return 'ob_start();';
|
||||
}
|
||||
@ -642,7 +679,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function stdFuncClose($tokens, Scope $scope) {
|
||||
public static function stdFuncClose($tokens, Scope $scope)
|
||||
{
|
||||
return $scope["function"] . '(' . $scope["params"] . ', ob_get_clean(), $tpl)';
|
||||
}
|
||||
|
||||
@ -651,7 +689,8 @@ class Compiler {
|
||||
* @param $params
|
||||
* @return string
|
||||
*/
|
||||
public static function toArray($params) {
|
||||
public static function toArray($params)
|
||||
{
|
||||
$_code = array();
|
||||
foreach ($params as $k => $v) {
|
||||
$_code[] = '"' . $k . '" => ' . $v;
|
||||
@ -665,7 +704,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function varOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function varOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$var = $scope->tpl->parseVariable($tokens, Template::DENY_MODS);
|
||||
if ($tokens->is('=')) { // inline tag {var ...}
|
||||
$scope->is_closed = true;
|
||||
@ -691,7 +731,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function varClose(Tokenizer $tokens, Scope $scope) {
|
||||
public static function varClose(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
return $scope["name"] . '=' . $scope["value"] . ';';
|
||||
}
|
||||
|
||||
@ -701,7 +742,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function filterOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function filterOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["filter"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()");
|
||||
return "ob_start();";
|
||||
}
|
||||
@ -711,7 +753,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function filterClose($tokens, Scope $scope) {
|
||||
public static function filterClose($tokens, Scope $scope)
|
||||
{
|
||||
return "echo " . $scope["filter"] . ";";
|
||||
}
|
||||
|
||||
@ -723,7 +766,8 @@ class Compiler {
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public static function tagCycle(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagCycle(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
if ($tokens->is("[")) {
|
||||
$exp = $tpl->parseArray($tokens);
|
||||
} else {
|
||||
@ -748,7 +792,8 @@ class Compiler {
|
||||
* @param $index
|
||||
* @return mixed
|
||||
*/
|
||||
public static function cycle($vals, $index) {
|
||||
public static function cycle($vals, $index)
|
||||
{
|
||||
return $vals[$index % count($vals)];
|
||||
}
|
||||
|
||||
@ -761,7 +806,8 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagImport(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagImport(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
$import = array();
|
||||
if ($tokens->is('[')) {
|
||||
$tokens->next();
|
||||
@ -809,7 +855,6 @@ class Compiler {
|
||||
if ($alias) {
|
||||
$name = $alias . '.' . $name;
|
||||
}
|
||||
|
||||
$tpl->macros[$name] = $macro;
|
||||
}
|
||||
$tpl->addDepend($donor);
|
||||
@ -825,10 +870,12 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public static function macroOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function macroOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope["name"] = $tokens->get(Tokenizer::MACRO_STRING);
|
||||
$scope["args"] = array();
|
||||
$scope["defaults"] = array();
|
||||
$scope["recursive"] = false;
|
||||
$args = array();
|
||||
$defaults = array();
|
||||
if (!$tokens->valid()) {
|
||||
return;
|
||||
}
|
||||
@ -836,12 +883,12 @@ class Compiler {
|
||||
if ($tokens->is(')')) {
|
||||
return;
|
||||
}
|
||||
while($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$scope["args"][] = $param = $tokens->getAndNext();
|
||||
while ($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) {
|
||||
$args[] = $param = $tokens->getAndNext();
|
||||
if ($tokens->is('=')) {
|
||||
$tokens->next();
|
||||
if ($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) {
|
||||
$scope["defaults"][ $param ] = $tokens->getAndNext();
|
||||
$defaults[$param] = $tokens->getAndNext();
|
||||
} else {
|
||||
throw new InvalidUsageException("Macro parameters may have only scalar defaults");
|
||||
}
|
||||
@ -849,7 +896,13 @@ class Compiler {
|
||||
$tokens->skipIf(',');
|
||||
}
|
||||
$tokens->skipIf(')');
|
||||
|
||||
$scope["macro"] = array(
|
||||
"name" => $scope["name"],
|
||||
"args" => $args,
|
||||
"defaults" => $defaults,
|
||||
"body" => "",
|
||||
"recursive" => false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -857,13 +910,13 @@ class Compiler {
|
||||
* @param Tokenizer $tokens
|
||||
* @param Scope $scope
|
||||
*/
|
||||
public static function macroClose(Tokenizer $tokens, Scope $scope) {
|
||||
$scope->tpl->macros[ $scope["name"] ] = array(
|
||||
"body" => $content = $scope->getContent(),
|
||||
"args" => $scope["args"],
|
||||
"defaults" => $scope["defaults"]
|
||||
);
|
||||
$scope->tpl->_body = substr($scope->tpl->_body, 0, strlen($scope->tpl->_body) - strlen($content));
|
||||
public static function macroClose(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
if ($scope["recursive"]) {
|
||||
$scope["macro"]["recursive"] = true;
|
||||
}
|
||||
$scope["macro"]["body"] = $scope->cutContent();
|
||||
$scope->tpl->macros[$scope["name"]] = $scope["macro"];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -874,12 +927,13 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagRaw(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagRaw(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
$escape = (bool)$tpl->escape;
|
||||
$tpl->escape = false;
|
||||
if ($tokens->is(':')) {
|
||||
$func = $tokens->getNext(Tokenizer::MACRO_STRING);
|
||||
$tag = $tpl->getStorage()->getFunction($func);
|
||||
$tag = $tpl->getStorage()->getTag($func, $tpl);
|
||||
if ($tag["type"] == \Fenom::INLINE_FUNCTION) {
|
||||
$code = $tpl->parseAct($tokens);
|
||||
} elseif ($tag["type"] == \Fenom::BLOCK_FUNCTION) {
|
||||
@ -900,7 +954,8 @@ class Compiler {
|
||||
* @param Tokenizer $tokens
|
||||
* @param Scope $scope
|
||||
*/
|
||||
public static function autoescapeOpen(Tokenizer $tokens, Scope $scope) {
|
||||
public static function autoescapeOpen(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$boolean = ($tokens->get(T_STRING) == "true" ? true : false);
|
||||
$scope["escape"] = $scope->tpl->escape;
|
||||
$scope->tpl->escape = $boolean;
|
||||
@ -911,7 +966,28 @@ class Compiler {
|
||||
* @param Tokenizer $tokens
|
||||
* @param Scope $scope
|
||||
*/
|
||||
public static function autoescapeClose(Tokenizer $tokens, Scope $scope) {
|
||||
public static function autoescapeClose(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope->tpl->escape = $scope["escape"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset present variables
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @param Template $tpl
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public static function tagUnset(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
$vars = array();
|
||||
while ($tokens->valid()) {
|
||||
$vars[] = $tpl->parseVar($tokens);
|
||||
}
|
||||
if (!$vars) {
|
||||
throw new InvalidUsageException("Unset must accept variable(s)");
|
||||
}
|
||||
return 'unset(' . implode(', ', $vars) . ')';
|
||||
}
|
||||
}
|
||||
|
18
src/Fenom/Error/CompileException.php
Normal file
18
src/Fenom/Error/CompileException.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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\Error;
|
||||
|
||||
/**
|
||||
* @package Fenom\Error
|
||||
*/
|
||||
class CompileException extends \ErrorException
|
||||
{
|
||||
}
|
18
src/Fenom/Error/InvalidUsageException.php
Normal file
18
src/Fenom/Error/InvalidUsageException.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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\Error;
|
||||
|
||||
/**
|
||||
* @package Fenom\Error
|
||||
*/
|
||||
class InvalidUsageException extends \LogicException
|
||||
{
|
||||
}
|
18
src/Fenom/Error/SecurityException.php
Normal file
18
src/Fenom/Error/SecurityException.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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\Error;
|
||||
|
||||
/**
|
||||
* @package Fenom\Error
|
||||
*/
|
||||
class SecurityException extends CompileException
|
||||
{
|
||||
}
|
18
src/Fenom/Error/TokenizeException.php
Normal file
18
src/Fenom/Error/TokenizeException.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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\Error;
|
||||
|
||||
/**
|
||||
* @package Fenom\Error
|
||||
*/
|
||||
class TokenizeException extends \RuntimeException
|
||||
{
|
||||
}
|
38
src/Fenom/Error/UnexpectedTokenException.php
Normal file
38
src/Fenom/Error/UnexpectedTokenException.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\Error;
|
||||
use Fenom\Tokenizer;
|
||||
|
||||
/**
|
||||
* Unexpected token
|
||||
*/
|
||||
class UnexpectedTokenException extends \RuntimeException
|
||||
{
|
||||
public function __construct(Tokenizer $tokens, $expect = null, $where = null)
|
||||
{
|
||||
if ($expect && count($expect) == 1 && is_string($expect[0])) {
|
||||
$expect = ", expect '" . $expect[0] . "'";
|
||||
} else {
|
||||
$expect = "";
|
||||
}
|
||||
if (!$tokens->curr) {
|
||||
$this->message = "Unexpected end of " . ($where ? : "expression") . "$expect";
|
||||
} elseif ($tokens->curr[0] === T_WHITESPACE) {
|
||||
$this->message = "Unexpected whitespace$expect";
|
||||
} elseif ($tokens->curr[0] === T_BAD_CHARACTER) {
|
||||
$this->message = "Unexpected bad characters (below ASCII 32 except \\t, \\n and \\r) in " . ($where ? : "expression") . "$expect";
|
||||
} else {
|
||||
$this->message = "Unexpected token '" . $tokens->current() . "' in " . ($where ? : "expression") . "$expect";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;
|
@ -13,7 +13,8 @@ namespace Fenom;
|
||||
* Collection of modifiers
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Modifier {
|
||||
class Modifier
|
||||
{
|
||||
|
||||
/**
|
||||
* Date format
|
||||
@ -22,7 +23,8 @@ class Modifier {
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public static function dateFormat($date, $format = "%b %e, %Y") {
|
||||
public static function dateFormat($date, $format = "%b %e, %Y")
|
||||
{
|
||||
if (is_string($date) && !is_numeric($date)) {
|
||||
$date = strtotime($date);
|
||||
if (!$date) $date = time();
|
||||
@ -35,7 +37,8 @@ class Modifier {
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public static function date($date, $format = "Y m d") {
|
||||
public static function date($date, $format = "Y m d")
|
||||
{
|
||||
if (is_string($date) && !is_numeric($date)) {
|
||||
$date = strtotime($date);
|
||||
if (!$date) $date = time();
|
||||
@ -50,7 +53,8 @@ class Modifier {
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public static function escape($text, $type = 'html') {
|
||||
public static function escape($text, $type = 'html')
|
||||
{
|
||||
switch (strtolower($type)) {
|
||||
case "url":
|
||||
return urlencode($text);
|
||||
@ -68,7 +72,8 @@ class Modifier {
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public static function unescape($text, $type = 'html') {
|
||||
public static function unescape($text, $type = 'html')
|
||||
{
|
||||
switch (strtolower($type)) {
|
||||
case "url":
|
||||
return urldecode($text);
|
||||
@ -89,7 +94,8 @@ class Modifier {
|
||||
* @param bool $middle
|
||||
* @return string
|
||||
*/
|
||||
public static function truncate($string, $length = 80, $etc = '...', $by_words = false, $middle = false) {
|
||||
public static function truncate($string, $length = 80, $etc = '...', $by_words = false, $middle = false)
|
||||
{
|
||||
if ($middle) {
|
||||
if (preg_match('#^(.{' . $length . '}).*?(.{' . $length . '})?$#usS', $string, $match)) {
|
||||
if (count($match) == 3) {
|
||||
@ -119,7 +125,8 @@ class Modifier {
|
||||
* @param bool $to_line strip line ends
|
||||
* @return string
|
||||
*/
|
||||
public static function strip($str, $to_line = false) {
|
||||
public static function strip($str, $to_line = false)
|
||||
{
|
||||
$str = trim($str);
|
||||
if ($to_line) {
|
||||
return preg_replace('#[\s]+#ms', ' ', $str);
|
||||
@ -133,7 +140,8 @@ class Modifier {
|
||||
* @param mixed $item
|
||||
* @return int
|
||||
*/
|
||||
public static function length($item) {
|
||||
public static function length($item)
|
||||
{
|
||||
if (is_string($item)) {
|
||||
return strlen(preg_replace('#[\x00-\x7F]|[\x80-\xDF][\x00-\xBF]|[\xE0-\xEF][\x00-\xBF]{2}#s', ' ', $item));
|
||||
} elseif (is_array($item)) {
|
||||
@ -151,7 +159,8 @@ class Modifier {
|
||||
* @param mixed $haystack
|
||||
* @return bool
|
||||
*/
|
||||
public static function in($value, $haystack) {
|
||||
public static function in($value, $haystack)
|
||||
{
|
||||
if (is_array($haystack)) {
|
||||
return in_array($value, $haystack) || array_key_exists($value, $haystack);
|
||||
} elseif (is_string($haystack)) {
|
||||
@ -164,7 +173,8 @@ class Modifier {
|
||||
* @param $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function isIterable($value) {
|
||||
public static function isIterable($value)
|
||||
{
|
||||
return is_array($value) || ($value instanceof \Iterator);
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,13 @@
|
||||
namespace Fenom;
|
||||
|
||||
use Fenom\ProviderInterface;
|
||||
|
||||
/**
|
||||
* Base template provider
|
||||
* @author Ivan Shalganov
|
||||
*/
|
||||
class Provider implements ProviderInterface {
|
||||
class Provider implements ProviderInterface
|
||||
{
|
||||
private $_path;
|
||||
|
||||
/**
|
||||
@ -22,7 +24,8 @@ class Provider implements ProviderInterface {
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public static function clean($path) {
|
||||
public static function clean($path)
|
||||
{
|
||||
if (is_file($path)) {
|
||||
unlink($path);
|
||||
} elseif (is_dir($path)) {
|
||||
@ -51,7 +54,8 @@ class Provider implements ProviderInterface {
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public static function rm($path) {
|
||||
public static function rm($path)
|
||||
{
|
||||
self::clean($path);
|
||||
if (is_dir($path)) {
|
||||
rmdir($path);
|
||||
@ -62,7 +66,8 @@ class Provider implements ProviderInterface {
|
||||
* @param string $template_dir directory of templates
|
||||
* @throws \LogicException if directory doesn't exists
|
||||
*/
|
||||
public function __construct($template_dir) {
|
||||
public function __construct($template_dir)
|
||||
{
|
||||
if ($_dir = realpath($template_dir)) {
|
||||
$this->_path = $_dir;
|
||||
} else {
|
||||
@ -76,7 +81,8 @@ class Provider implements ProviderInterface {
|
||||
* @param int $time load last modified time
|
||||
* @return string
|
||||
*/
|
||||
public function getSource($tpl, &$time) {
|
||||
public function getSource($tpl, &$time)
|
||||
{
|
||||
$tpl = $this->_getTemplatePath($tpl);
|
||||
clearstatcache(null, $tpl);
|
||||
$time = filemtime($tpl);
|
||||
@ -88,7 +94,8 @@ class Provider implements ProviderInterface {
|
||||
* @param string $tpl
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified($tpl) {
|
||||
public function getLastModified($tpl)
|
||||
{
|
||||
clearstatcache(null, $tpl = $this->_getTemplatePath($tpl));
|
||||
return filemtime($tpl);
|
||||
}
|
||||
@ -99,7 +106,8 @@ class Provider implements ProviderInterface {
|
||||
* @param string $extension all templates must have this extension, default .tpl
|
||||
* @return array|\Iterator
|
||||
*/
|
||||
public function getList($extension = "tpl") {
|
||||
public function getList($extension = "tpl")
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($this->_path,
|
||||
@ -122,7 +130,8 @@ class Provider implements ProviderInterface {
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function _getTemplatePath($tpl) {
|
||||
protected function _getTemplatePath($tpl)
|
||||
{
|
||||
if (($path = realpath($this->_path . "/" . $tpl)) && strpos($path, $this->_path) === 0) {
|
||||
return $path;
|
||||
} else {
|
||||
@ -134,7 +143,8 @@ class Provider implements ProviderInterface {
|
||||
* @param string $tpl
|
||||
* @return bool
|
||||
*/
|
||||
public function templateExists($tpl) {
|
||||
public function templateExists($tpl)
|
||||
{
|
||||
return file_exists($this->_path . "/" . $tpl);
|
||||
}
|
||||
|
||||
@ -144,7 +154,8 @@ class Provider implements ProviderInterface {
|
||||
* @param array $templates [template_name => modified, ...] By conversation, you may trust the template's name
|
||||
* @return bool
|
||||
*/
|
||||
public function verify(array $templates) {
|
||||
public function verify(array $templates)
|
||||
{
|
||||
foreach ($templates as $template => $mtime) {
|
||||
clearstatcache(null, $template = $this->_path . '/' . $template);
|
||||
if (@filemtime($template) !== $mtime) {
|
||||
|
@ -14,7 +14,8 @@ namespace Fenom;
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
interface ProviderInterface {
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $tpl
|
||||
* @return bool
|
||||
|
@ -14,13 +14,15 @@ use Fenom;
|
||||
* Primitive template
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Render extends \ArrayObject {
|
||||
class Render extends \ArrayObject
|
||||
{
|
||||
private static $_props = array(
|
||||
"name" => "runtime",
|
||||
"base_name" => "",
|
||||
"scm" => false,
|
||||
"time" => 0,
|
||||
"depends" => array()
|
||||
"depends" => array(),
|
||||
"macros" => array()
|
||||
);
|
||||
/**
|
||||
* @var \Closure
|
||||
@ -72,49 +74,77 @@ class Render extends \ArrayObject {
|
||||
* @param callable $code template body
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(Fenom $fenom, \Closure $code, $props = array()) {
|
||||
public function __construct(Fenom $fenom, \Closure $code, array $props = array())
|
||||
{
|
||||
$this->_fenom = $fenom;
|
||||
$props += self::$_props;
|
||||
$this->_name = $props["name"];
|
||||
// $this->_provider = $this->_fenom->getProvider($props["scm"]);
|
||||
$this->_scm = $props["scm"];
|
||||
$this->_time = $props["time"];
|
||||
$this->_depends = $props["depends"];
|
||||
$this->_macros = $props["macros"];
|
||||
$this->_code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template storage
|
||||
* @return Fenom
|
||||
* @return \Fenom
|
||||
*/
|
||||
public function getStorage() {
|
||||
public function getStorage()
|
||||
{
|
||||
return $this->_fenom;
|
||||
}
|
||||
|
||||
public function getDepends() {
|
||||
/**
|
||||
* Get depends list
|
||||
* @return array
|
||||
*/
|
||||
public function getDepends()
|
||||
{
|
||||
return $this->_depends;
|
||||
}
|
||||
|
||||
public function getScm() {
|
||||
/**
|
||||
* Get schema name
|
||||
* @return string
|
||||
*/
|
||||
public function getScm()
|
||||
{
|
||||
return $this->_scm;
|
||||
}
|
||||
|
||||
public function getProvider() {
|
||||
/**
|
||||
* Get provider of template source
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function getProvider()
|
||||
{
|
||||
return $this->_fenom->getProvider($this->_scm);
|
||||
}
|
||||
|
||||
public function getBaseName() {
|
||||
/**
|
||||
* Get name without schema
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseName()
|
||||
{
|
||||
return $this->_base_name;
|
||||
}
|
||||
|
||||
public function getOptions() {
|
||||
/**
|
||||
* Get parse options
|
||||
* @return int
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
public function __toString()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
@ -122,11 +152,13 @@ class Render extends \ArrayObject {
|
||||
* Get template name
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function getTime() {
|
||||
public function getTime()
|
||||
{
|
||||
return $this->_time;
|
||||
}
|
||||
|
||||
@ -135,7 +167,8 @@ class Render extends \ArrayObject {
|
||||
* Validate template
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid() {
|
||||
public function isValid()
|
||||
{
|
||||
if (count($this->_depends) === 1) { // if no external dependencies, only self
|
||||
$provider = $this->_fenom->getProvider($this->_scm);
|
||||
if ($provider->getLastModified($this->_name) !== $this->_time) {
|
||||
@ -152,12 +185,22 @@ class Render extends \ArrayObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get internal macro
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMacro($name) {
|
||||
return $this->_macros[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute template and write into output
|
||||
* @param array $values for template
|
||||
* @return Render
|
||||
*/
|
||||
public function display(array $values) {
|
||||
public function display(array $values)
|
||||
{
|
||||
$this->exchangeArray($values);
|
||||
$this->_code->__invoke($this);
|
||||
return $this->exchangeArray(array());
|
||||
@ -169,7 +212,8 @@ class Render extends \ArrayObject {
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function fetch(array $values) {
|
||||
public function fetch(array $values)
|
||||
{
|
||||
ob_start();
|
||||
try {
|
||||
$this->display($values);
|
||||
@ -186,7 +230,8 @@ class Render extends \ArrayObject {
|
||||
* @param $args
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function __call($method, $args) {
|
||||
public function __call($method, $args)
|
||||
{
|
||||
throw new \BadMethodCallException("Unknown method " . $method);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ namespace Fenom;
|
||||
*
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Scope extends \ArrayObject {
|
||||
class Scope extends \ArrayObject
|
||||
{
|
||||
|
||||
public $line = 0;
|
||||
public $name;
|
||||
@ -40,7 +41,8 @@ class Scope extends \ArrayObject {
|
||||
* @param int $level
|
||||
* @param $body
|
||||
*/
|
||||
public function __construct($name, $tpl, $line, $action, $level, &$body) {
|
||||
public function __construct($name, $tpl, $line, $action, $level, &$body)
|
||||
{
|
||||
$this->line = $line;
|
||||
$this->name = $name;
|
||||
$this->tpl = $tpl;
|
||||
@ -54,7 +56,8 @@ class Scope extends \ArrayObject {
|
||||
*
|
||||
* @param string $function
|
||||
*/
|
||||
public function setFuncName($function) {
|
||||
public function setFuncName($function)
|
||||
{
|
||||
$this["function"] = $function;
|
||||
$this->is_compiler = false;
|
||||
$this->escape = $this->tpl->escape;
|
||||
@ -66,7 +69,8 @@ class Scope extends \ArrayObject {
|
||||
* @param Tokenizer $tokenizer
|
||||
* @return mixed
|
||||
*/
|
||||
public function open($tokenizer) {
|
||||
public function open($tokenizer)
|
||||
{
|
||||
return call_user_func($this->_action["open"], $tokenizer, $this);
|
||||
}
|
||||
|
||||
@ -77,7 +81,8 @@ class Scope extends \ArrayObject {
|
||||
* @param int $level
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTag($tag, $level) {
|
||||
public function hasTag($tag, $level)
|
||||
{
|
||||
if (isset($this->_action["tags"][$tag])) {
|
||||
if ($level) {
|
||||
return isset($this->_action["float_tags"][$tag]);
|
||||
@ -95,7 +100,8 @@ class Scope extends \ArrayObject {
|
||||
* @param Tokenizer $tokenizer
|
||||
* @return string
|
||||
*/
|
||||
public function tag($tag, $tokenizer) {
|
||||
public function tag($tag, $tokenizer)
|
||||
{
|
||||
return call_user_func($this->_action["tags"][$tag], $tokenizer, $this);
|
||||
}
|
||||
|
||||
@ -105,7 +111,8 @@ class Scope extends \ArrayObject {
|
||||
* @param Tokenizer $tokenizer
|
||||
* @return string
|
||||
*/
|
||||
public function close($tokenizer) {
|
||||
public function close($tokenizer)
|
||||
{
|
||||
return call_user_func($this->_action["close"], $tokenizer, $this);
|
||||
}
|
||||
|
||||
@ -115,7 +122,8 @@ class Scope extends \ArrayObject {
|
||||
* @throws \LogicException
|
||||
* @return string
|
||||
*/
|
||||
public function getContent() {
|
||||
public function getContent()
|
||||
{
|
||||
return substr($this->_body, $this->_offset);
|
||||
}
|
||||
|
||||
@ -125,7 +133,8 @@ class Scope extends \ArrayObject {
|
||||
* @return string
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function cutContent() {
|
||||
public function cutContent()
|
||||
{
|
||||
$content = substr($this->_body, $this->_offset + 1);
|
||||
$this->_body = substr($this->_body, 0, $this->_offset);
|
||||
return $content;
|
||||
@ -136,12 +145,14 @@ class Scope extends \ArrayObject {
|
||||
*
|
||||
* @param $new_content
|
||||
*/
|
||||
public function replaceContent($new_content) {
|
||||
public function replaceContent($new_content)
|
||||
{
|
||||
$this->cutContent();
|
||||
$this->_body .= $new_content;
|
||||
}
|
||||
|
||||
public function unEscapeContent() {
|
||||
public function unEscapeContent()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -9,6 +9,11 @@
|
||||
*/
|
||||
namespace Fenom;
|
||||
use Fenom;
|
||||
use Fenom\Error\UnexpectedTokenException;
|
||||
use Fenom\Error\CompileException;
|
||||
use Fenom\Error\InvalidUsageException;
|
||||
use Fenom\Error\SecurityException;
|
||||
use Fenom\Error\TokenizeException;
|
||||
|
||||
/**
|
||||
* Template compiler
|
||||
@ -16,7 +21,8 @@ use Fenom;
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Template extends Render {
|
||||
class Template extends Render
|
||||
{
|
||||
|
||||
/**
|
||||
* Disable array parser.
|
||||
@ -38,12 +44,6 @@ class Template extends Render {
|
||||
* @var int shared counter
|
||||
*/
|
||||
public $i = 1;
|
||||
/**
|
||||
* Template PHP code
|
||||
* @var string
|
||||
*/
|
||||
public $_body;
|
||||
|
||||
/**
|
||||
* @var array of macros
|
||||
*/
|
||||
@ -63,10 +63,17 @@ class Template extends Render {
|
||||
* @var bool
|
||||
*/
|
||||
public $escape = false;
|
||||
|
||||
public $_extends;
|
||||
public $_extended = false;
|
||||
public $_compatible;
|
||||
|
||||
/**
|
||||
* Template PHP code
|
||||
* @var string
|
||||
*/
|
||||
private $_body;
|
||||
|
||||
/**
|
||||
* Call stack
|
||||
* @var Scope[]
|
||||
@ -88,7 +95,9 @@ class Template extends Render {
|
||||
*/
|
||||
private $_ignore = false;
|
||||
|
||||
private $_filter = array();
|
||||
private $_before;
|
||||
|
||||
private $_filters = array();
|
||||
|
||||
private static $_checkers = array(
|
||||
'integer' => 'is_int(%s)',
|
||||
@ -118,32 +127,24 @@ class Template extends Render {
|
||||
'third' => '!(%s %% 3)'
|
||||
);
|
||||
|
||||
/**
|
||||
* Just factory
|
||||
*
|
||||
* @param \Fenom $fenom
|
||||
* @param $options
|
||||
* @return Template
|
||||
*/
|
||||
public static function factory(Fenom $fenom, $options) {
|
||||
return new static($fenom, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Fenom $fenom Template storage
|
||||
* @param int $options
|
||||
* @return \Fenom\Template
|
||||
*/
|
||||
public function __construct(Fenom $fenom, $options) {
|
||||
public function __construct(Fenom $fenom, $options)
|
||||
{
|
||||
$this->_fenom = $fenom;
|
||||
$this->_options = $options;
|
||||
$this->_filters = $this->_fenom->getFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag stack size
|
||||
* @return int
|
||||
*/
|
||||
public function getStackSize() {
|
||||
public function getStackSize()
|
||||
{
|
||||
return count($this->_stack);
|
||||
}
|
||||
|
||||
@ -151,9 +152,10 @@ class Template extends Render {
|
||||
* Load source from provider
|
||||
* @param string $name
|
||||
* @param bool $compile
|
||||
* @return $this
|
||||
* @return self
|
||||
*/
|
||||
public function load($name, $compile = true) {
|
||||
public function load($name, $compile = true)
|
||||
{
|
||||
$this->_name = $name;
|
||||
if ($provider = strstr($name, ":", true)) {
|
||||
$this->_scm = $provider;
|
||||
@ -176,7 +178,8 @@ class Template extends Render {
|
||||
* @param bool $compile
|
||||
* @return \Fenom\Template
|
||||
*/
|
||||
public function source($name, $src, $compile = true) {
|
||||
public function source($name, $src, $compile = true)
|
||||
{
|
||||
$this->_name = $name;
|
||||
$this->_src = $src;
|
||||
if ($compile) {
|
||||
@ -190,13 +193,21 @@ class Template extends Render {
|
||||
*
|
||||
* @throws CompileException
|
||||
*/
|
||||
public function compile() {
|
||||
public function compile()
|
||||
{
|
||||
$end = $pos = 0;
|
||||
$this->escape = $this->_options & Fenom::AUTO_ESCAPE;
|
||||
foreach ($this->_fenom->getPreFilters() as $filter) {
|
||||
$this->_src = call_user_func($filter, $this->_src, $this);
|
||||
}
|
||||
|
||||
while (($start = strpos($this->_src, '{', $pos)) !== false) { // search open-symbol of tags
|
||||
switch ($this->_src[$start + 1]) { // check next character
|
||||
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
|
||||
case "\n":
|
||||
case "\r":
|
||||
case "\t":
|
||||
case " ":
|
||||
case "}": // ignore the tag
|
||||
$this->_appendText(substr($this->_src, $pos, $start - $pos + 2));
|
||||
$end = $start + 1;
|
||||
break;
|
||||
@ -215,7 +226,7 @@ class Template extends Render {
|
||||
$this->_appendText(substr($this->_src, $pos, $start - $pos));
|
||||
$end = $start + 1;
|
||||
do {
|
||||
$need_next_close_symbol = false;
|
||||
$need_more = false;
|
||||
$end = strpos($this->_src, '}', $end + 1); // search close-symbol of the tag
|
||||
if ($end === false) { // if unexpected end of template
|
||||
throw new CompileException("Unclosed tag in line {$this->_line}", 0, 1, $this->_name, $this->_line);
|
||||
@ -233,7 +244,7 @@ class Template extends Render {
|
||||
} else {
|
||||
$tokens = new Tokenizer($_tag); // tokenize the tag
|
||||
if ($tokens->isIncomplete()) { // all strings finished?
|
||||
$need_next_close_symbol = true;
|
||||
$need_more = true;
|
||||
} else {
|
||||
$this->_appendCode($this->parseTag($tokens), $tag); // start the tag lexer
|
||||
if ($tokens->key()) { // if tokenizer have tokens - throws exceptions
|
||||
@ -241,7 +252,7 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($need_next_close_symbol);
|
||||
} while ($need_more);
|
||||
unset($_tag, $tag); // cleanup
|
||||
break;
|
||||
}
|
||||
@ -268,13 +279,27 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
$this->addDepend($this); // for 'verify' performance
|
||||
foreach ($this->_fenom->getPostFilters() as $filter) {
|
||||
$this->_body = call_user_func($filter, $this->_body, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute some code at loading cache
|
||||
* @param $code
|
||||
* @return void
|
||||
*/
|
||||
public function before($code)
|
||||
{
|
||||
$this->_before .= $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate temporary internal template variable
|
||||
* @return string
|
||||
*/
|
||||
public function tmpVar() {
|
||||
public function tmpVar()
|
||||
{
|
||||
return '$t' . ($this->i++);
|
||||
}
|
||||
|
||||
@ -283,17 +308,21 @@ class Template extends Render {
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
private function _appendText($text) {
|
||||
private function _appendText($text)
|
||||
{
|
||||
$this->_line += substr_count($text, "\n");
|
||||
if($this->_filter) {
|
||||
if ($this->_filters) {
|
||||
if (strpos($text, "<?") === false) {
|
||||
foreach ($this->_filters as $filter) {
|
||||
$text = call_user_func($filter, $text, $this);
|
||||
}
|
||||
$this->_body .= $text;
|
||||
} else {
|
||||
$fragments = explode("<?", $text);
|
||||
foreach ($fragments as &$fragment) {
|
||||
if ($fragment) {
|
||||
foreach($this->_filter as $filter) {
|
||||
$fragment = call_user_func($filter, $fragment);
|
||||
foreach ($this->_filters as $filter) {
|
||||
$fragment = call_user_func($filter, $fragment, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -309,7 +338,8 @@ class Template extends Render {
|
||||
* @param int $code
|
||||
* @return string
|
||||
*/
|
||||
private function _escapeCode($code) {
|
||||
private function _escapeCode($code)
|
||||
{
|
||||
$c = "";
|
||||
foreach (token_get_all($code) as $token) {
|
||||
if (is_string($token)) {
|
||||
@ -329,7 +359,8 @@ class Template extends Render {
|
||||
* @param string $code
|
||||
* @param $source
|
||||
*/
|
||||
private function _appendCode($code, $source) {
|
||||
private function _appendCode($code, $source)
|
||||
{
|
||||
if (!$code) {
|
||||
return;
|
||||
} else {
|
||||
@ -344,7 +375,8 @@ class Template extends Render {
|
||||
/**
|
||||
* @param callable[] $cb
|
||||
*/
|
||||
public function addPostCompile($cb) {
|
||||
public function addPostCompile($cb)
|
||||
{
|
||||
$this->_post[] = $cb;
|
||||
}
|
||||
|
||||
@ -353,7 +385,8 @@ class Template extends Render {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBody() {
|
||||
public function getBody()
|
||||
{
|
||||
return $this->_body;
|
||||
}
|
||||
|
||||
@ -362,24 +395,41 @@ class Template extends Render {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateCode() {
|
||||
public function getTemplateCode()
|
||||
{
|
||||
|
||||
if($this->macros) {
|
||||
$macros = array();
|
||||
foreach($this->macros as $m) {
|
||||
if($m["recursive"]) {
|
||||
$macros[] = "\t\t'".$m["name"]."' => function (\$tpl) {\n?>".$m["body"]."<?php\n}";
|
||||
}
|
||||
}
|
||||
$macros = "\n".implode(",\n", $macros);
|
||||
} else {
|
||||
$macros = "";
|
||||
}
|
||||
$before = $this->_before ? $this->_before . "\n" : "";
|
||||
return "<?php \n" .
|
||||
"/** Fenom template '" . $this->_name . "' compiled at " . date('Y-m-d H:i:s') . " */\n" .
|
||||
"return new Fenom\\Render(\$fenom, ".$this->_getClosureSource().", ".var_export(array(
|
||||
"options" => $this->_options,
|
||||
"provider" => $this->_scm,
|
||||
"name" => $this->_name,
|
||||
"base_name" => $this->_base_name,
|
||||
"time" => $this->_time,
|
||||
"depends" => $this->_depends
|
||||
), true).");\n";
|
||||
$before . // some code 'before' template
|
||||
"return new Fenom\\Render(\$fenom, " . $this->_getClosureSource() . ", array(\n".
|
||||
"\t'options' => {$this->_options},\n".
|
||||
"\t'provider' => ".var_export($this->_scm, true).",\n".
|
||||
"\t'name' => ".var_export($this->_name, true).",\n".
|
||||
"\t'base_name' => ".var_export($this->_base_name, true).",\n".
|
||||
"\t'time' => {$this->_time},\n".
|
||||
"\t'depends' => ".var_export($this->_base_name, true).",\n".
|
||||
"\t'macros' => array({$macros}),
|
||||
));\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return closure code
|
||||
* @return string
|
||||
*/
|
||||
private function _getClosureSource() {
|
||||
private function _getClosureSource()
|
||||
{
|
||||
return "function (\$tpl) {\n?>{$this->_body}<?php\n}";
|
||||
}
|
||||
|
||||
@ -390,7 +440,8 @@ class Template extends Render {
|
||||
* @throws CompileException
|
||||
* @return Render
|
||||
*/
|
||||
public function display(array $values) {
|
||||
public function display(array $values)
|
||||
{
|
||||
if (!$this->_code) {
|
||||
// evaluate template's code
|
||||
eval("\$this->_code = " . $this->_getClosureSource() . ";");
|
||||
@ -406,7 +457,8 @@ class Template extends Render {
|
||||
* Add depends from template
|
||||
* @param Render $tpl
|
||||
*/
|
||||
public function addDepend(Render $tpl) {
|
||||
public function addDepend(Render $tpl)
|
||||
{
|
||||
$this->_depends[$tpl->getScm()][$tpl->getName()] = $tpl->getTime();
|
||||
}
|
||||
|
||||
@ -416,13 +468,15 @@ class Template extends Render {
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function out($data) {
|
||||
public function out($data)
|
||||
{
|
||||
if ($this->escape) {
|
||||
return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8');";
|
||||
} else {
|
||||
return "echo $data;";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag router
|
||||
* @param Tokenizer $tokens
|
||||
@ -431,7 +485,8 @@ class Template extends Render {
|
||||
* @throws CompileException
|
||||
* @return string executable PHP code
|
||||
*/
|
||||
public function parseTag(Tokenizer $tokens) {
|
||||
public function parseTag(Tokenizer $tokens)
|
||||
{
|
||||
try {
|
||||
if ($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
if ($tokens->current() === "ignore") {
|
||||
@ -464,7 +519,8 @@ class Template extends Render {
|
||||
* @return string
|
||||
* @throws TokenizeException
|
||||
*/
|
||||
public function parseEndTag(Tokenizer $tokens) {
|
||||
public function parseEndTag(Tokenizer $tokens)
|
||||
{
|
||||
$name = $tokens->getNext(Tokenizer::MACRO_STRING);
|
||||
$tokens->next();
|
||||
if (!$this->_stack) {
|
||||
@ -488,7 +544,8 @@ class Template extends Render {
|
||||
* Get current scope
|
||||
* @return Scope
|
||||
*/
|
||||
public function getLastScope() {
|
||||
public function getLastScope()
|
||||
{
|
||||
return end($this->_stack);
|
||||
}
|
||||
|
||||
@ -501,7 +558,8 @@ class Template extends Render {
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
*/
|
||||
public function parseAct(Tokenizer $tokens) {
|
||||
public function parseAct(Tokenizer $tokens)
|
||||
{
|
||||
if ($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$action = $tokens->getAndNext();
|
||||
} else {
|
||||
@ -517,25 +575,25 @@ class Template extends Render {
|
||||
if ($action !== "macro") {
|
||||
$name = $action . "." . $name;
|
||||
}
|
||||
return $this->parseMacro($tokens, $name);
|
||||
return $this->parseMacroCall($tokens, $name);
|
||||
}
|
||||
|
||||
if($act = $this->_fenom->getFunction($action)) { // call some function
|
||||
switch($act["type"]) {
|
||||
if ($tag = $this->_fenom->getTag($action, $this)) { // call some function
|
||||
switch ($tag["type"]) {
|
||||
case Fenom::BLOCK_COMPILER:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
||||
$scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body);
|
||||
$code = $scope->open($tokens);
|
||||
if (!$scope->is_closed) {
|
||||
array_push($this->_stack, $scope);
|
||||
}
|
||||
return $code;
|
||||
case Fenom::INLINE_COMPILER:
|
||||
return call_user_func($act["parser"], $tokens, $this);
|
||||
return call_user_func($tag["parser"], $tokens, $this);
|
||||
case Fenom::INLINE_FUNCTION:
|
||||
return $this->out(call_user_func($act["parser"], $act["function"], $tokens, $this));
|
||||
return $this->out(call_user_func($tag["parser"], $tag["function"], $tokens, $this));
|
||||
case Fenom::BLOCK_FUNCTION:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
||||
$scope->setFuncName($act["function"]);
|
||||
$scope = new Scope($action, $this, $this->_line, $tag, count($this->_stack), $this->_body);
|
||||
$scope->setFuncName($tag["function"]);
|
||||
array_push($this->_stack, $scope);
|
||||
$scope->escape = $this->escape;
|
||||
$this->escape = false;
|
||||
@ -558,132 +616,174 @@ class Template extends Render {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse expressions. The mix of math operations, boolean operations, scalars, arrays and variables.
|
||||
* Parse expressions. The mix of operations and terms.
|
||||
*
|
||||
* @static
|
||||
* @param Tokenizer $tokens
|
||||
* @param bool $required
|
||||
* @throws \LogicException
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
* @throws Error\UnexpectedTokenException
|
||||
*/
|
||||
public function parseExp(Tokenizer $tokens, $required = false) {
|
||||
$_exp = array(); // expression as PHP code
|
||||
$term = false; // last item was variable or value.
|
||||
// 0 - was operator, but trem required
|
||||
// false - was operator or no one term
|
||||
// true - was trem
|
||||
// 1 - term is strict varaible
|
||||
$cond = false; // last item was operator
|
||||
public function parseExp(Tokenizer $tokens, $required = false)
|
||||
{
|
||||
$exp = array();
|
||||
$var = false; // last term was: true - variable, false - mixed
|
||||
$op = false; // last exp was operator
|
||||
$cond = false; // was comparison operator
|
||||
while ($tokens->valid()) {
|
||||
if(!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) { // like quoted string
|
||||
$_exp[] = $this->parseScalar($tokens, true);
|
||||
$term = true;
|
||||
} elseif(!$term && $tokens->is(Tokenizer::MACRO_INCDEC)) { // like variable
|
||||
$_exp[] = $this->parseVariable($tokens);
|
||||
$term = true;
|
||||
} elseif(!$term && $tokens->is(T_VARIABLE)) { // like variable too
|
||||
$var = $this->parseVar($tokens);
|
||||
if($tokens->is(Tokenizer::MACRO_EQUALS)) {
|
||||
$_exp[] = $var;
|
||||
if($tokens->isLast()) {
|
||||
break;
|
||||
}
|
||||
$_exp[] = $tokens->getAndNext();
|
||||
$term = 0;
|
||||
} elseif($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?", '(')) {
|
||||
$_exp[] = $this->parseVariable($tokens, 0, $var);
|
||||
$term = true;
|
||||
} else {
|
||||
$_exp[] = $var;
|
||||
$term = 1;
|
||||
}
|
||||
} elseif(!$term && $tokens->is("(")) { // open bracket
|
||||
$tokens->next();
|
||||
$_exp[] = "(".$this->parseExp($tokens, true).")";
|
||||
$tokens->get(")");
|
||||
$tokens->next();
|
||||
$term = 1;
|
||||
} elseif($tokens->is(T_STRING)) {
|
||||
if($term) { // parse 'in' or 'is' operators
|
||||
if(!$_exp) {
|
||||
break;
|
||||
}
|
||||
$operator = $tokens->current();
|
||||
if($operator == "is") {
|
||||
$item = array_pop($_exp);
|
||||
$_exp[] = $this->parseIs($tokens, $item, $term === 1);
|
||||
} elseif($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) {
|
||||
$item = array_pop($_exp);
|
||||
$_exp[] = $this->parseIn($tokens, $item, $term === 1);
|
||||
// parse term
|
||||
$term = $this->parseTerm($tokens, $var); // term of the expression
|
||||
if ($term !== false) {
|
||||
$exp[] = $term;
|
||||
$op = false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else { // function or special value
|
||||
if($tokens->isSpecialVal()) {
|
||||
$_exp[] = $tokens->getAndNext();
|
||||
} elseif($tokens->isNext("(") && !$tokens->getWhitespace()) {
|
||||
$func = $this->_fenom->getModifier($tokens->current());
|
||||
$tokens->next();
|
||||
$func = $func.$this->parseArgs($tokens);
|
||||
if($tokens->is('|')) {
|
||||
$_exp[] = $this->parseModifier($tokens, $func);
|
||||
} else {
|
||||
$_exp[] = $func;
|
||||
}
|
||||
} else {
|
||||
if (!$tokens->valid()) {
|
||||
break;
|
||||
}
|
||||
$term = true;
|
||||
}
|
||||
} elseif(!$term && $tokens->is(T_ISSET, T_EMPTY)) { // empty and isset operators
|
||||
$func = $tokens->getAndNext();
|
||||
if($tokens->is("(") && $tokens->isNext(T_VARIABLE)) {
|
||||
$tokens->next();
|
||||
$_exp[] = $func."(".$this->parseVar($tokens).")";
|
||||
$tokens->need(')')->next();
|
||||
} else {
|
||||
throw new TokenizeException("Unexpected token ".$tokens->getNext().", isset() and empty() accept only variables");
|
||||
}
|
||||
$term = true;
|
||||
} elseif(!$term && $tokens->is(Tokenizer::MACRO_UNARY)) {
|
||||
if(!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY, '(')) {
|
||||
break;
|
||||
}
|
||||
$_exp[] = $tokens->getAndNext();
|
||||
$term = 0;
|
||||
} elseif($tokens->is(Tokenizer::MACRO_BINARY)) { // like binary operator, see Tokenizer::MACRO_BINARY
|
||||
if(!$term) {
|
||||
throw new UnexpectedTokenException($tokens);
|
||||
}
|
||||
if($tokens->isLast()) {
|
||||
break;
|
||||
}
|
||||
if($tokens->is(Tokenizer::MACRO_COND)) {
|
||||
// parse operator
|
||||
if ($tokens->is(Tokenizer::MACRO_BINARY)) { // binary operator: $a + $b, $a <= $b, ...
|
||||
if ($tokens->is(Tokenizer::MACRO_COND)) { // comparison operator
|
||||
if ($cond) {
|
||||
break;
|
||||
}
|
||||
$cond = true;
|
||||
} elseif ($tokens->is(Tokenizer::MACRO_BOOLEAN)) {
|
||||
$cond = false;
|
||||
}
|
||||
$_exp[] = " ".$tokens->getAndNext()." ";
|
||||
$term = 0;
|
||||
} elseif(!$term && $tokens->is('[')) {
|
||||
$_exp[] = $this->parseArray($tokens);
|
||||
$term = 1;
|
||||
$op = $tokens->getAndNext();
|
||||
} elseif ($tokens->is(Tokenizer::MACRO_EQUALS)) { // assignment operator: $a = 4, $a += 3, ...
|
||||
if (!$var) {
|
||||
break;
|
||||
}
|
||||
$op = $tokens->getAndNext();
|
||||
} elseif ($tokens->is(T_STRING)) { // test or containment operator: $a in $b, $a is set, ...
|
||||
if (!$exp) {
|
||||
break;
|
||||
}
|
||||
$operator = $tokens->current();
|
||||
if ($operator == "is") {
|
||||
$item = array_pop($exp);
|
||||
$exp[] = $this->parseIs($tokens, $item, $var);
|
||||
} elseif ($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) {
|
||||
$item = array_pop($exp);
|
||||
$exp[] = $this->parseIn($tokens, $item, $var);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} elseif ($tokens->is('~')) { // string concatenation operator: 'asd' ~ $var
|
||||
$concat = array(array_pop($exp));
|
||||
while($tokens->is('~')) {
|
||||
$tokens->next();
|
||||
if($tokens->is(T_LNUMBER, T_DNUMBER)) {
|
||||
$concat[] = "strval(".$this->parseTerm($tokens).")";
|
||||
} else {
|
||||
$concat[] = $this->parseTerm($tokens);
|
||||
}
|
||||
if($term === 0) {
|
||||
}
|
||||
$exp[] = "(".implode(".", $concat).")";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if ($op) {
|
||||
$exp[] = $op;
|
||||
}
|
||||
}
|
||||
|
||||
if ($op) {
|
||||
throw new UnexpectedTokenException($tokens);
|
||||
}
|
||||
if($required && !$_exp) {
|
||||
if ($required && !$exp) {
|
||||
throw new UnexpectedTokenException($tokens);
|
||||
}
|
||||
return implode('',$_exp);
|
||||
return implode(' ', $exp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse any term: -2, ++$var, 'adf'|mod:4
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @param bool $is_var
|
||||
* @return bool|string
|
||||
* @throws Error\UnexpectedTokenException
|
||||
* @throws Error\TokenizeException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function parseTerm(Tokenizer $tokens, &$is_var = false)
|
||||
{
|
||||
$is_var = false;
|
||||
$unary = "";
|
||||
term: {
|
||||
if ($tokens->is(T_LNUMBER, T_DNUMBER)) {
|
||||
return $unary . $this->parseScalar($tokens, true);
|
||||
} elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) {
|
||||
if ($unary) {
|
||||
throw new UnexpectedTokenException($tokens->back());
|
||||
}
|
||||
return $this->parseScalar($tokens, true);
|
||||
} elseif ($tokens->is(T_VARIABLE)) {
|
||||
$var = $this->parseVar($tokens);
|
||||
if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) {
|
||||
return $unary . $this->parseVariable($tokens, 0, $var);
|
||||
} elseif ($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call
|
||||
return $unary . $this->parseVariable($tokens, 0, $var);
|
||||
} elseif ($unary) {
|
||||
return $unary . $var;
|
||||
} else {
|
||||
$is_var = true;
|
||||
return $var;
|
||||
}
|
||||
} elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) {
|
||||
return $unary . $this->parseVariable($tokens);
|
||||
} elseif ($tokens->is("(")) {
|
||||
$tokens->next();
|
||||
$exp = $unary . "(" . $this->parseExp($tokens, true) . ")";
|
||||
$tokens->need(")")->next();
|
||||
return $exp;
|
||||
} elseif ($tokens->is(Tokenizer::MACRO_UNARY)) {
|
||||
if ($unary) {
|
||||
throw new UnexpectedTokenException($tokens);
|
||||
}
|
||||
$unary = $tokens->getAndNext();
|
||||
goto term;
|
||||
} elseif ($tokens->is(T_STRING)) {
|
||||
if ($tokens->isSpecialVal()) {
|
||||
return $unary . $tokens->getAndNext();
|
||||
} elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) {
|
||||
$func = $this->_fenom->getModifier($tokens->current(), $this);
|
||||
if (!$func) {
|
||||
throw new \Exception("Function " . $tokens->getAndNext() . " not found");
|
||||
}
|
||||
$tokens->next();
|
||||
$func = $func . $this->parseArgs($tokens);
|
||||
if ($tokens->is('|')) {
|
||||
return $unary . $this->parseModifier($tokens, $func);
|
||||
} else {
|
||||
return $unary . $func;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} elseif ($tokens->is(T_ISSET, T_EMPTY)) {
|
||||
$func = $tokens->getAndNext();
|
||||
if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) {
|
||||
$tokens->next();
|
||||
$exp = $func . "(" . $this->parseVar($tokens) . ")";
|
||||
$tokens->need(')')->next();
|
||||
return $unary . $exp;
|
||||
} else {
|
||||
throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables");
|
||||
}
|
||||
} elseif ($tokens->is('[')) {
|
||||
if ($unary) {
|
||||
throw new UnexpectedTokenException($tokens->back());
|
||||
}
|
||||
return $this->parseArray($tokens);
|
||||
} elseif ($unary) {
|
||||
$tokens->back();
|
||||
throw new UnexpectedTokenException($tokens);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -693,7 +793,8 @@ class Template extends Render {
|
||||
* @param int $options
|
||||
* @return string
|
||||
*/
|
||||
public function parseVar(Tokenizer $tokens, $options = 0) {
|
||||
public function parseVar(Tokenizer $tokens, $options = 0)
|
||||
{
|
||||
$var = $tokens->get(T_VARIABLE);
|
||||
$_var = '$tpl["' . substr($var, 1) . '"]';
|
||||
$tokens->next();
|
||||
@ -757,7 +858,8 @@ class Template extends Render {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public function parseVariable(Tokenizer $tokens, $options = 0, $var = null) {
|
||||
public function parseVariable(Tokenizer $tokens, $options = 0, $var = null)
|
||||
{
|
||||
$stained = false;
|
||||
if (!$var) {
|
||||
if ($tokens->is(Tokenizer::MACRO_INCDEC)) {
|
||||
@ -806,7 +908,8 @@ class Template extends Render {
|
||||
* @return string
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function parseTernary(Tokenizer $tokens, $var, $type) {
|
||||
public function parseTernary(Tokenizer $tokens, $var, $type)
|
||||
{
|
||||
$empty = ($type === "?");
|
||||
$tokens->next();
|
||||
if ($tokens->is(":")) {
|
||||
@ -824,9 +927,7 @@ class Template extends Render {
|
||||
}
|
||||
} else {
|
||||
$expr1 = $this->parseExp($tokens, true);
|
||||
if(!$tokens->is(":")) {
|
||||
throw new UnexpectedTokenException($tokens, null, "ternary operator");
|
||||
}
|
||||
$tokens->need(':')->skip();
|
||||
$expr2 = $this->parseExp($tokens, true);
|
||||
if ($empty) {
|
||||
return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')';
|
||||
@ -837,7 +938,7 @@ class Template extends Render {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'is' and 'is not' operator
|
||||
* Parse 'is' and 'is not' operators
|
||||
* @see $_checkers
|
||||
* @param Tokenizer $tokens
|
||||
* @param string $value
|
||||
@ -845,7 +946,8 @@ class Template extends Render {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public function parseIs(Tokenizer $tokens, $value, $variable = false) {
|
||||
public function parseIs(Tokenizer $tokens, $value, $variable = false)
|
||||
{
|
||||
$tokens->next();
|
||||
if ($tokens->current() == 'not') {
|
||||
$invert = '!';
|
||||
@ -890,7 +992,8 @@ class Template extends Render {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return string
|
||||
*/
|
||||
public function parseIn(Tokenizer $tokens, $value) {
|
||||
public function parseIn(Tokenizer $tokens, $value)
|
||||
{
|
||||
$checkers = array(
|
||||
"string" => 'is_int(strpos(%2$s, %1$s))',
|
||||
"list" => "in_array(%s, %s)",
|
||||
@ -944,7 +1047,8 @@ class Template extends Render {
|
||||
* @param Tokenizer $tokens
|
||||
* @return string
|
||||
*/
|
||||
public function parseName(Tokenizer $tokens) {
|
||||
public function parseName(Tokenizer $tokens)
|
||||
{
|
||||
$tokens->skipIf(T_NS_SEPARATOR);
|
||||
$name = "";
|
||||
if ($tokens->is(T_STRING)) {
|
||||
@ -965,7 +1069,8 @@ class Template extends Render {
|
||||
* @return string
|
||||
* @throws TokenizeException
|
||||
*/
|
||||
public function parseScalar(Tokenizer $tokens, $allow_mods = true) {
|
||||
public function parseScalar(Tokenizer $tokens, $allow_mods = true)
|
||||
{
|
||||
$_scalar = "";
|
||||
if ($token = $tokens->key()) {
|
||||
switch ($token) {
|
||||
@ -976,7 +1081,7 @@ class Template extends Render {
|
||||
break;
|
||||
case T_ENCAPSED_AND_WHITESPACE:
|
||||
case '"':
|
||||
$_scalar .= $this->parseSubstr($tokens);
|
||||
$_scalar .= $this->parseQuote($tokens);
|
||||
break;
|
||||
default:
|
||||
throw new TokenizeException("Unexpected scalar token '" . $tokens->current() . "'");
|
||||
@ -995,7 +1100,8 @@ class Template extends Render {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return string
|
||||
*/
|
||||
public function parseSubstr(Tokenizer $tokens) {
|
||||
public function parseQuote(Tokenizer $tokens)
|
||||
{
|
||||
if ($tokens->is('"', "`")) {
|
||||
$stop = $tokens->current();
|
||||
$_str = '"';
|
||||
@ -1052,6 +1158,17 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tokenizer $tokens
|
||||
* @param null $first_member
|
||||
*/
|
||||
public function parseConcat(Tokenizer $tokens, $first_member = null)
|
||||
{
|
||||
$concat = array();
|
||||
if ($first_member) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse modifiers
|
||||
* |modifier:1:2.3:'string':false:$var:(4+5*$var3)|modifier2:"str {$var+3} ing":$arr.item
|
||||
@ -1062,10 +1179,13 @@ class Template extends Render {
|
||||
* @throws \Exception
|
||||
* @return string
|
||||
*/
|
||||
public function parseModifier(Tokenizer $tokens, $value) {
|
||||
public function parseModifier(Tokenizer $tokens, $value)
|
||||
{
|
||||
while ($tokens->is("|")) {
|
||||
$mods = $this->_fenom->getModifier( $modifier_name = $tokens->getNext(Tokenizer::MACRO_STRING) );
|
||||
|
||||
$mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING), $this);
|
||||
if (!$mods) {
|
||||
throw new \Exception("Modifier " . $tokens->current() . " not found");
|
||||
}
|
||||
$tokens->next();
|
||||
$args = array();
|
||||
|
||||
@ -1078,7 +1198,7 @@ class Template extends Render {
|
||||
} elseif ($tokens->is(T_VARIABLE)) {
|
||||
$args[] = $this->parseVariable($tokens, self::DENY_MODS);
|
||||
} elseif ($tokens->is('"', '`', T_ENCAPSED_AND_WHITESPACE)) {
|
||||
$args[] = $this->parseSubstr($tokens);
|
||||
$args[] = $this->parseQuote($tokens);
|
||||
} elseif ($tokens->is('(')) {
|
||||
$args[] = $this->parseExp($tokens, true);
|
||||
} elseif ($tokens->is('[')) {
|
||||
@ -1091,7 +1211,7 @@ class Template extends Render {
|
||||
}
|
||||
|
||||
if (!is_string($mods)) { // dynamic modifier
|
||||
$mods = 'call_user_func($tpl->getStorage()->getModifier("'.$modifier_name.'"), ';
|
||||
$mods = 'call_user_func($tpl->getStorage()->getModifier("' . $mods . '"), ';
|
||||
} else {
|
||||
$mods .= "(";
|
||||
}
|
||||
@ -1112,7 +1232,8 @@ class Template extends Render {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return string
|
||||
*/
|
||||
public function parseArray(Tokenizer $tokens) {
|
||||
public function parseArray(Tokenizer $tokens)
|
||||
{
|
||||
if ($tokens->is("[")) {
|
||||
$_arr = "array(";
|
||||
$key = $val = false;
|
||||
@ -1127,7 +1248,7 @@ class Template extends Render {
|
||||
$key = false;
|
||||
$val = true;
|
||||
} elseif ($tokens->is('"') && !$val) {
|
||||
$_arr .= $this->parseSubstr($tokens);
|
||||
$_arr .= $this->parseQuote($tokens);
|
||||
$key = false;
|
||||
$val = true;
|
||||
} elseif ($tokens->is(T_DOUBLE_ARROW) && $val) {
|
||||
@ -1157,7 +1278,8 @@ class Template extends Render {
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public function parseConst(Tokenizer $tokens) {
|
||||
public function parseConst(Tokenizer $tokens)
|
||||
{
|
||||
$tokens->get('#');
|
||||
$name = $tokens->getNext(T_STRING);
|
||||
$tokens->next();
|
||||
@ -1184,25 +1306,44 @@ class Template extends Render {
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public function parseMacro(Tokenizer $tokens, $name) {
|
||||
public function parseMacroCall(Tokenizer $tokens, $name)
|
||||
{
|
||||
$recursive = false;
|
||||
$macro = false;
|
||||
if (isset($this->macros[$name])) {
|
||||
$macro = $this->macros[$name];
|
||||
} else {
|
||||
foreach ($this->_stack as $scope) {
|
||||
if ($scope->name == 'macro' && $scope['name'] == $name) { // invoke recursive
|
||||
$recursive = $scope;
|
||||
$macro = $scope['macro'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$macro) {
|
||||
throw new InvalidUsageException("Undefined macro '$name'");
|
||||
}
|
||||
}
|
||||
$tokens->next();
|
||||
$p = $this->parseParams($tokens);
|
||||
$args = array();
|
||||
foreach($macro["args"] as $arg) {
|
||||
foreach ($macro['args'] as $arg) {
|
||||
if (isset($p[$arg])) {
|
||||
$args[$arg] = $p[$arg];
|
||||
} elseif(isset($macro["defaults"][ $arg ])) {
|
||||
$args[ $arg ] = $macro["defaults"][ $arg ];
|
||||
} elseif (isset($macro['defaults'][$arg])) {
|
||||
$args[$arg] = $macro['defaults'][$arg];
|
||||
} else {
|
||||
throw new InvalidUsageException("Macro '$name' require '$arg' argument");
|
||||
}
|
||||
}
|
||||
$args = $args ? '$tpl = '.Compiler::toArray($args).';' : '';
|
||||
return '$_tpl = $tpl; '.$args.' ?>'.$macro["body"].'<?php $tpl = $_tpl; unset($_tpl);';
|
||||
$n = $this->i++;
|
||||
if ($recursive) {
|
||||
$recursive['recursive'] = true;
|
||||
$body = '$tpl->getMacro("'.$name.'")->__invoke($tpl);';
|
||||
} else {
|
||||
throw new InvalidUsageException("Undefined macro '$name'");
|
||||
$body = '?>'.$macro["body"].'<?php';
|
||||
}
|
||||
return '$_tpl'.$n.' = $tpl->exchangeArray(' . Compiler::toArray($args) . ');' . PHP_EOL . $body . PHP_EOL . '$tpl->exchangeArray($_tpl'.$n.'); unset($_tpl'.$n.');';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1214,7 +1355,8 @@ class Template extends Render {
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
*/
|
||||
public function parseArgs(Tokenizer $tokens) {
|
||||
public function parseArgs(Tokenizer $tokens)
|
||||
{
|
||||
$_args = "(";
|
||||
$tokens->next();
|
||||
$arg = $colon = false;
|
||||
@ -1249,7 +1391,8 @@ class Template extends Render {
|
||||
* @param string $static
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function parsePlainArg(Tokenizer $tokens, &$static) {
|
||||
public function parsePlainArg(Tokenizer $tokens, &$static)
|
||||
{
|
||||
if ($tokens->is(T_CONSTANT_ENCAPSED_STRING)) {
|
||||
if ($tokens->isNext('|')) {
|
||||
return $this->parseExp($tokens, true);
|
||||
@ -1266,6 +1409,7 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse parameters as $key=$value
|
||||
* param1=$var param2=3 ...
|
||||
@ -1276,7 +1420,8 @@ class Template extends Render {
|
||||
* @throws \Exception
|
||||
* @return array
|
||||
*/
|
||||
public function parseParams(Tokenizer $tokens, array $defaults = null) {
|
||||
public function parseParams(Tokenizer $tokens, array $defaults = null)
|
||||
{
|
||||
$params = array();
|
||||
while ($tokens->valid()) {
|
||||
if ($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
@ -1303,8 +1448,3 @@ class Template extends Render {
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
|
||||
class CompileException extends \ErrorException {}
|
||||
class SecurityException extends CompileException {}
|
||||
class InvalidUsageException extends \LogicException {}
|
||||
class TokenizeException extends \RuntimeException {}
|
@ -9,16 +9,18 @@
|
||||
*/
|
||||
namespace Fenom;
|
||||
|
||||
use Fenom\Error\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* for <PHP 5.4 compatible
|
||||
* 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
|
||||
* for PHP <5.5 compatible
|
||||
*/
|
||||
defined('T_YIELD') || define('T_YIELD', 370);
|
||||
defined('T_YIELD') || define('T_YIELD', 390);
|
||||
|
||||
/**
|
||||
* Each token have structure
|
||||
@ -35,7 +37,8 @@ defined('T_YIELD') || define('T_YIELD', 370);
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Tokenizer {
|
||||
class Tokenizer
|
||||
{
|
||||
const TOKEN = 0;
|
||||
const TEXT = 1;
|
||||
const WHITESPACE = 2;
|
||||
@ -159,7 +162,8 @@ class Tokenizer {
|
||||
/**
|
||||
* @param $query
|
||||
*/
|
||||
public function __construct($query) {
|
||||
public function __construct($query)
|
||||
{
|
||||
$tokens = array(-1 => array(\T_WHITESPACE, '', '', 1));
|
||||
$_tokens = token_get_all("<?php " . $query);
|
||||
$line = 1;
|
||||
@ -202,7 +206,8 @@ class Tokenizer {
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function isIncomplete() {
|
||||
public function isIncomplete()
|
||||
{
|
||||
return ($this->quotes % 2) || ($this->tokens[$this->_max][0] === T_ENCAPSED_AND_WHITESPACE);
|
||||
}
|
||||
|
||||
@ -212,7 +217,8 @@ class Tokenizer {
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current() {
|
||||
public function current()
|
||||
{
|
||||
return $this->curr[1];
|
||||
}
|
||||
|
||||
@ -222,7 +228,8 @@ class Tokenizer {
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
* @return Tokenizer
|
||||
*/
|
||||
public function next() {
|
||||
public function next()
|
||||
{
|
||||
if ($this->p > $this->_max) {
|
||||
return $this;
|
||||
}
|
||||
@ -238,7 +245,8 @@ class Tokenizer {
|
||||
* @param string|int $token
|
||||
* @return bool
|
||||
*/
|
||||
private function _valid($expects, $token) {
|
||||
private function _valid($expects, $token)
|
||||
{
|
||||
foreach ($expects as $expect) {
|
||||
if (is_string($expect) || $expect < 1000) {
|
||||
if ($expect === $token) {
|
||||
@ -260,7 +268,8 @@ class Tokenizer {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return mixed
|
||||
*/
|
||||
public function _next($tokens) {
|
||||
public function _next($tokens)
|
||||
{
|
||||
$this->next();
|
||||
if (!$this->curr) {
|
||||
throw new UnexpectedTokenException($this, $tokens);
|
||||
@ -279,7 +288,8 @@ class Tokenizer {
|
||||
* Fetch next specified token or throw an exception
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNext(/*int|string $token1, int|string $token2, ... */) {
|
||||
public function getNext( /*int|string $token1, int|string $token2, ... */)
|
||||
{
|
||||
$this->_next(func_get_args());
|
||||
return $this->current();
|
||||
}
|
||||
@ -288,7 +298,8 @@ class Tokenizer {
|
||||
* @param $token
|
||||
* @return bool
|
||||
*/
|
||||
public function isNextToken($token) {
|
||||
public function isNextToken($token)
|
||||
{
|
||||
return $this->next ? $this->next[1] == $token : false;
|
||||
}
|
||||
|
||||
@ -298,7 +309,8 @@ class Tokenizer {
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
public function getSubstr($offset, $limit = 0) {
|
||||
public function getSubstr($offset, $limit = 0)
|
||||
{
|
||||
$str = '';
|
||||
if (!$limit) {
|
||||
$limit = $this->_max;
|
||||
@ -316,7 +328,8 @@ class Tokenizer {
|
||||
* @return mixed
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function getAndNext() {
|
||||
public function getAndNext( /* $token1, ... */)
|
||||
{
|
||||
if ($this->curr) {
|
||||
$cur = $this->curr[1];
|
||||
$this->next();
|
||||
@ -331,7 +344,8 @@ class Tokenizer {
|
||||
* @param $token1
|
||||
* @return bool
|
||||
*/
|
||||
public function isNext($token1/*, ...*/) {
|
||||
public function isNext($token1 /*, ...*/)
|
||||
{
|
||||
return $this->next && $this->_valid(func_get_args(), $this->next[0]);
|
||||
}
|
||||
|
||||
@ -340,7 +354,8 @@ class Tokenizer {
|
||||
* @param $token1
|
||||
* @return bool
|
||||
*/
|
||||
public function is($token1/*, ...*/) {
|
||||
public function is($token1 /*, ...*/)
|
||||
{
|
||||
return $this->curr && $this->_valid(func_get_args(), $this->curr[0]);
|
||||
}
|
||||
|
||||
@ -349,7 +364,8 @@ class Tokenizer {
|
||||
* @param $token1
|
||||
* @return bool
|
||||
*/
|
||||
public function isPrev($token1/*, ...*/) {
|
||||
public function isPrev($token1 /*, ...*/)
|
||||
{
|
||||
return $this->prev && $this->_valid(func_get_args(), $this->prev[0]);
|
||||
}
|
||||
|
||||
@ -360,7 +376,8 @@ class Tokenizer {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($token1 /*, $token2 ...*/) {
|
||||
public function get($token1 /*, $token2 ...*/)
|
||||
{
|
||||
if ($this->curr && $this->_valid(func_get_args(), $this->curr[0])) {
|
||||
return $this->curr[1];
|
||||
} else {
|
||||
@ -372,7 +389,8 @@ class Tokenizer {
|
||||
* Step back
|
||||
* @return Tokenizer
|
||||
*/
|
||||
public function back() {
|
||||
public function back()
|
||||
{
|
||||
if ($this->p === 0) {
|
||||
return $this;
|
||||
}
|
||||
@ -385,7 +403,8 @@ class Tokenizer {
|
||||
* @param $token1
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBackList($token1 /*, $token2 ...*/) {
|
||||
public function hasBackList($token1 /*, $token2 ...*/)
|
||||
{
|
||||
$tokens = func_get_args();
|
||||
$c = $this->p;
|
||||
foreach ($tokens as $token) {
|
||||
@ -403,7 +422,8 @@ class Tokenizer {
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key) {
|
||||
public function __get($key)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'curr':
|
||||
return $this->curr = ($this->p <= $this->_max) ? $this->tokens[$this->p] : null;
|
||||
@ -416,7 +436,8 @@ class Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
public function count() {
|
||||
public function count()
|
||||
{
|
||||
return $this->_max;
|
||||
}
|
||||
|
||||
@ -424,7 +445,8 @@ class Tokenizer {
|
||||
* Return the key of the current element
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key() {
|
||||
public function key()
|
||||
{
|
||||
return $this->curr ? $this->curr[0] : null;
|
||||
}
|
||||
|
||||
@ -433,7 +455,8 @@ class Tokenizer {
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
*/
|
||||
public function valid() {
|
||||
public function valid()
|
||||
{
|
||||
return (bool)$this->curr;
|
||||
}
|
||||
|
||||
@ -443,7 +466,8 @@ class Tokenizer {
|
||||
* @param int|string $token
|
||||
* @return string
|
||||
*/
|
||||
public static function getName($token) {
|
||||
public static function getName($token)
|
||||
{
|
||||
if (is_string($token)) {
|
||||
return $token;
|
||||
} elseif (is_integer($token)) {
|
||||
@ -461,7 +485,8 @@ class Tokenizer {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return Tokenizer
|
||||
*/
|
||||
public function skip(/*$token1, $token2, ...*/) {
|
||||
public function skip( /*$token1, $token2, ...*/)
|
||||
{
|
||||
if (func_num_args()) {
|
||||
if ($this->_valid(func_get_args(), $this->curr[0])) {
|
||||
$this->next();
|
||||
@ -481,7 +506,8 @@ class Tokenizer {
|
||||
* @param int|string $token1
|
||||
* @return Tokenizer
|
||||
*/
|
||||
public function skipIf($token1/*, $token2, ...*/) {
|
||||
public function skipIf($token1 /*, $token2, ...*/)
|
||||
{
|
||||
if ($this->_valid(func_get_args(), $this->curr[0])) {
|
||||
$this->next();
|
||||
}
|
||||
@ -495,7 +521,8 @@ class Tokenizer {
|
||||
* @return Tokenizer
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function need($token1/*, $token2, ...*/) {
|
||||
public function need($token1 /*, $token2, ...*/)
|
||||
{
|
||||
if ($this->_valid(func_get_args(), $this->curr[0])) {
|
||||
return $this;
|
||||
} else {
|
||||
@ -509,7 +536,8 @@ class Tokenizer {
|
||||
* @param int $after count tokens after current token
|
||||
* @return array
|
||||
*/
|
||||
public function getSnippet($before = 0, $after = 0) {
|
||||
public function getSnippet($before = 0, $after = 0)
|
||||
{
|
||||
$from = 0;
|
||||
$to = $this->p;
|
||||
if ($before > 0) {
|
||||
@ -551,7 +579,8 @@ class Tokenizer {
|
||||
* @param int $after
|
||||
* @return string
|
||||
*/
|
||||
public function getSnippetAsString($before = 0, $after = 0) {
|
||||
public function getSnippetAsString($before = 0, $after = 0)
|
||||
{
|
||||
$str = "";
|
||||
foreach ($this->getSnippet($before, $after) as $token) {
|
||||
$str .= $token[1] . $token[2];
|
||||
@ -563,7 +592,8 @@ class Tokenizer {
|
||||
* Check if current is special value: true, TRUE, false, FALSE, null, NULL
|
||||
* @return bool
|
||||
*/
|
||||
public function isSpecialVal() {
|
||||
public function isSpecialVal()
|
||||
{
|
||||
return isset(self::$spec[$this->current()]);
|
||||
}
|
||||
|
||||
@ -571,14 +601,16 @@ class Tokenizer {
|
||||
* Check if the token is last
|
||||
* @return bool
|
||||
*/
|
||||
public function isLast() {
|
||||
public function isLast()
|
||||
{
|
||||
return $this->p === $this->_max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move pointer to the end
|
||||
*/
|
||||
public function end() {
|
||||
public function end()
|
||||
{
|
||||
$this->p = $this->_max;
|
||||
}
|
||||
|
||||
@ -586,7 +618,8 @@ class Tokenizer {
|
||||
* Return line number of the current token
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLine() {
|
||||
public function getLine()
|
||||
{
|
||||
return $this->curr ? $this->curr[3] : $this->_last_no;
|
||||
}
|
||||
|
||||
@ -594,33 +627,13 @@ class Tokenizer {
|
||||
* Is current token whitespaced, means previous token has whitespace characters
|
||||
* @return bool
|
||||
*/
|
||||
public function isWhiteSpaced() {
|
||||
public function isWhiteSpaced()
|
||||
{
|
||||
return $this->prev ? (bool)$this->prev[2] : false;
|
||||
}
|
||||
|
||||
public function getWhitespace() {
|
||||
public function getWhitespace()
|
||||
{
|
||||
return $this->curr ? $this->curr[2] : false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unexpected token
|
||||
*/
|
||||
class UnexpectedTokenException extends \RuntimeException {
|
||||
public function __construct(Tokenizer $tokens, $expect = null, $where = null) {
|
||||
if($expect && count($expect) == 1 && is_string($expect[0])) {
|
||||
$expect = ", expect '".$expect[0]."'";
|
||||
} else {
|
||||
$expect = "";
|
||||
}
|
||||
if(!$tokens->curr) {
|
||||
$this->message = "Unexpected end of ".($where?:"expression")."$expect";
|
||||
} elseif($tokens->curr[0] === T_WHITESPACE) {
|
||||
$this->message = "Unexpected whitespace$expect";
|
||||
} elseif($tokens->curr[0] === T_BAD_CHARACTER) {
|
||||
$this->message = "Unexpected bad characters (below ASCII 32 except \\t, \\n and \\r) in ".($where?:"expression")."$expect";
|
||||
} else {
|
||||
$this->message = "Unexpected token '".$tokens->current()."' in ".($where?:"expression")."$expect";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,7 +2,8 @@
|
||||
namespace Fenom;
|
||||
use Fenom, Fenom\Provider as FS;
|
||||
|
||||
class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
class TestCase extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var Fenom
|
||||
*/
|
||||
@ -21,7 +22,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
3 => "three value",
|
||||
);
|
||||
|
||||
public static function getVars() {
|
||||
public static function getVars()
|
||||
{
|
||||
return array(
|
||||
"zero" => 0,
|
||||
"one" => 1,
|
||||
@ -32,7 +34,9 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
"obj" => new \StdClass,
|
||||
"list" => array(
|
||||
"a" => 1,
|
||||
"b" => 2
|
||||
"one" => 1,
|
||||
"b" => 2,
|
||||
"two" => 2
|
||||
),
|
||||
0 => "empty value",
|
||||
1 => "one value",
|
||||
@ -41,7 +45,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
public function setUp()
|
||||
{
|
||||
if (!file_exists(FENOM_RESOURCES . '/compile')) {
|
||||
mkdir(FENOM_RESOURCES . '/compile', 0777, true);
|
||||
} else {
|
||||
@ -55,23 +60,28 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
$this->fenom->addBlockFunction('test_block_function', __CLASS__ . '::blockFunction');
|
||||
}
|
||||
|
||||
public static function dots($value) {
|
||||
public static function dots($value)
|
||||
{
|
||||
return $value . "...";
|
||||
}
|
||||
|
||||
public static function concat() {
|
||||
public static function concat()
|
||||
{
|
||||
return call_user_func_array('var_export', func_get_args());
|
||||
}
|
||||
|
||||
public static function inlineFunction($params) {
|
||||
public static function inlineFunction($params)
|
||||
{
|
||||
return isset($params["text"]) ? $params["text"] : "";
|
||||
}
|
||||
|
||||
public static function blockFunction($params, $text) {
|
||||
public static function blockFunction($params, $text)
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
if (!file_exists(FENOM_RESOURCES . '/template')) {
|
||||
mkdir(FENOM_RESOURCES . '/template', 0777, true);
|
||||
} else {
|
||||
@ -79,7 +89,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public function tpl($name, $code) {
|
||||
public function tpl($name, $code)
|
||||
{
|
||||
$dir = dirname($name);
|
||||
if ($dir != "." && !is_dir(FENOM_RESOURCES . '/template/' . $dir)) {
|
||||
mkdir(FENOM_RESOURCES . '/template/' . $dir, 0777, true);
|
||||
@ -96,7 +107,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
* @param int $options
|
||||
* @param bool $dump dump source and result code (for debug)
|
||||
*/
|
||||
public function exec($code, $vars, $result, $options = 0, $dump = false) {
|
||||
public function exec($code, $vars, $result, $options = 0, $dump = false)
|
||||
{
|
||||
$this->fenom->setOptions($options);
|
||||
$tpl = $this->fenom->compileCode($code, "runtime.tpl");
|
||||
if ($dump) {
|
||||
@ -105,7 +117,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code");
|
||||
}
|
||||
|
||||
public function execTpl($name, $code, $vars, $result, $dump = false) {
|
||||
public function execTpl($name, $code, $vars, $result, $dump = false)
|
||||
{
|
||||
$this->tpl($name, $code);
|
||||
$tpl = $this->fenom->getTemplate($name);
|
||||
if ($dump) {
|
||||
@ -121,7 +134,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
* @param string $message exception message
|
||||
* @param int $options Fenom's options
|
||||
*/
|
||||
public function execError($code, $exception, $message, $options = 0) {
|
||||
public function execError($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$opt = $this->fenom->getOptions();
|
||||
$this->fenom->setOptions($options);
|
||||
try {
|
||||
@ -136,7 +150,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
$this->fail("Code $code must be invalid");
|
||||
}
|
||||
|
||||
public function assertRender($tpl, $result, $debug = false) {
|
||||
public function assertRender($tpl, $result, $debug = false)
|
||||
{
|
||||
$template = $this->fenom->compileCode($tpl);
|
||||
if ($debug) {
|
||||
print_r("$tpl:\n" . $template->getBody());
|
||||
@ -145,7 +160,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
|
||||
public static function providerNumbers() {
|
||||
public static function providerNumbers()
|
||||
{
|
||||
return array(
|
||||
array('0', 0),
|
||||
array('77', 77),
|
||||
@ -157,7 +173,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerStrings() {
|
||||
public static function providerStrings()
|
||||
{
|
||||
return array(
|
||||
array('"str"', 'str'),
|
||||
array('"str\nand\nmany\nlines"', "str\nand\nmany\nlines"),
|
||||
@ -187,15 +204,22 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function providerVariables() {
|
||||
public function providerVariables()
|
||||
{
|
||||
return array(
|
||||
array('$one', 1),
|
||||
array('$list.one', 1),
|
||||
array('$list[$$.DEVELOP]', 1),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerObjects()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public static function providerObjects() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public static function providerArrays() {
|
||||
public static function providerArrays()
|
||||
{
|
||||
$scalars = array();
|
||||
$data = array(
|
||||
array('[]', array()),
|
||||
@ -221,14 +245,16 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function providerScalars() {
|
||||
public static function providerScalars()
|
||||
{
|
||||
return array_merge(
|
||||
self::providerNumbers(),
|
||||
self::providerStrings()
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerValues() {
|
||||
public static function providerValues()
|
||||
{
|
||||
return array_merge(
|
||||
self::providerScalars(),
|
||||
self::providerArrays(),
|
||||
@ -238,14 +264,17 @@ class TestCase extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
class Fake implements \ArrayAccess {
|
||||
class Fake implements \ArrayAccess
|
||||
{
|
||||
public $vars;
|
||||
|
||||
public function offsetExists($offset) {
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function offsetGet($offset) {
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if ($offset == "object") {
|
||||
return new self();
|
||||
} else {
|
||||
@ -253,15 +282,18 @@ class Fake implements \ArrayAccess {
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value) {
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->vars[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->vars[$offset]);
|
||||
}
|
||||
|
||||
public function proxy() {
|
||||
public function proxy()
|
||||
{
|
||||
return implode(", ", func_get_args());
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,23 @@
|
||||
require_once __DIR__ . "/../vendor/autoload.php";
|
||||
|
||||
|
||||
|
||||
define('FENOM_RESOURCES', __DIR__ . "/resources");
|
||||
|
||||
require_once FENOM_RESOURCES . "/actions.php";
|
||||
require_once __DIR__ . "/TestCase.php";
|
||||
|
||||
function drop() {
|
||||
ini_set('date.timezone', 'Europe/Moscow');
|
||||
|
||||
function drop()
|
||||
{
|
||||
call_user_func_array("var_dump", func_get_args());
|
||||
$e = new Exception();
|
||||
echo "-------\nDump trace: \n" . $e->getTraceAsString() . "\n";
|
||||
exit();
|
||||
}
|
||||
|
||||
function dump() {
|
||||
function dump()
|
||||
{
|
||||
foreach (func_get_args() as $arg) {
|
||||
fwrite(STDERR, "DUMP: " . call_user_func("print_r", $arg, true) . "\n");
|
||||
|
||||
|
@ -3,9 +3,11 @@
|
||||
namespace Fenom;
|
||||
|
||||
|
||||
class AutoEscapeTest extends TestCase {
|
||||
class AutoEscapeTest extends TestCase
|
||||
{
|
||||
|
||||
public static function providerHTML() {
|
||||
public static function providerHTML()
|
||||
{
|
||||
$html = "<script>alert('injection');</script>";
|
||||
$escaped = htmlspecialchars($html, ENT_COMPAT, 'UTF-8');
|
||||
$vars = array(
|
||||
@ -56,7 +58,8 @@ class AutoEscapeTest extends TestCase {
|
||||
/**
|
||||
* @dataProvider providerHTML
|
||||
*/
|
||||
public function testEscaping($tpl, $result, $vars, $options) {
|
||||
public function testEscaping($tpl, $result, $vars, $options)
|
||||
{
|
||||
$this->values = $vars;
|
||||
$this->fenom->setOptions($options);
|
||||
$this->assertRender($tpl, $result);
|
||||
|
@ -3,25 +3,29 @@
|
||||
namespace Fenom;
|
||||
|
||||
|
||||
class CommentTest extends TestCase {
|
||||
class CommentTest extends TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @dataProvider providerScalars
|
||||
*/
|
||||
public function testInline($tpl_val) {
|
||||
public function testInline($tpl_val)
|
||||
{
|
||||
$this->assertRender("before {* $tpl_val *} after", "before after");
|
||||
$this->assertRender("before {* {{$tpl_val}} {{$tpl_val}} *} after", "before after");
|
||||
$this->assertRender("before {*{{$tpl_val}}*} after", "before after");
|
||||
}
|
||||
|
||||
public function testError() {
|
||||
$this->execError('{* ', 'Fenom\CompileException', "Unclosed comment block in line");
|
||||
public function testError()
|
||||
{
|
||||
$this->execError('{* ', 'Fenom\Error\CompileException', "Unclosed comment block in line");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerScalars
|
||||
*/
|
||||
public function testMultiLine($tpl_val) {
|
||||
public function testMultiLine($tpl_val)
|
||||
{
|
||||
$this->assertRender(
|
||||
"before-1\nbefore-2 {* before-3\nbefore-4 $tpl_val after-1\nafter-2 *} after-3\nafter-4{* dummy *}\nafter-5",
|
||||
"before-1\nbefore-2 after-3\nafter-4\nafter-5"
|
||||
|
@ -3,14 +3,17 @@
|
||||
namespace Fenom;
|
||||
|
||||
|
||||
class CustomProviderTest extends TestCase {
|
||||
class CustomProviderTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp() {
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->fenom->addProvider("my", new Provider(FENOM_RESOURCES . '/provider'));
|
||||
}
|
||||
|
||||
public function testCustom() {
|
||||
public function testCustom()
|
||||
{
|
||||
$this->assertRender("start: {include 'my:include.tpl'}", 'start: include template');
|
||||
//$this->assertRender("start: {import 'my:macros.tpl' as ops} {ops.add a=3 b=6}");
|
||||
}
|
||||
|
@ -2,9 +2,11 @@
|
||||
namespace Fenom;
|
||||
use Fenom, Fenom\TestCase;
|
||||
|
||||
class ExtendsTemplateTest extends TestCase {
|
||||
class ExtendsTemplateTest extends TestCase
|
||||
{
|
||||
|
||||
public function _testSandbox() {
|
||||
public function _testSandbox()
|
||||
{
|
||||
$this->fenom = Fenom::factory(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile');
|
||||
try {
|
||||
print_r($this->fenom->getTemplate('use/child.tpl')->getBody());
|
||||
@ -19,7 +21,8 @@ class ExtendsTemplateTest extends TestCase {
|
||||
* @param array $vars
|
||||
* @return array
|
||||
*/
|
||||
public static function templates(array $vars) {
|
||||
public static function templates(array $vars)
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
"name" => "level.0.tpl",
|
||||
@ -82,7 +85,8 @@ class ExtendsTemplateTest extends TestCase {
|
||||
* @param array $skels
|
||||
* @return array
|
||||
*/
|
||||
public static function generate($block_mask, $extend_mask, $skels) {
|
||||
public static function generate($block_mask, $extend_mask, $skels)
|
||||
{
|
||||
$t = array();
|
||||
foreach ($skels as $level => $tpl) {
|
||||
$src = 'level#' . $level . ' ';
|
||||
@ -102,7 +106,8 @@ class ExtendsTemplateTest extends TestCase {
|
||||
return $t;
|
||||
}
|
||||
|
||||
public function _testTemplateExtends() {
|
||||
public function _testTemplateExtends()
|
||||
{
|
||||
$vars = array(
|
||||
"b1" => "b1",
|
||||
"b2" => "b2",
|
||||
@ -138,12 +143,14 @@ class ExtendsTemplateTest extends TestCase {
|
||||
/**
|
||||
* @group use
|
||||
*/
|
||||
public function testUse() {
|
||||
public function testUse()
|
||||
{
|
||||
$this->fenom = Fenom::factory(FENOM_RESOURCES . '/provider', FENOM_RESOURCES . '/compile');
|
||||
$this->assertSame("<html>\n block 1 blocks \n block 2 child \n</html>", $this->fenom->fetch('use/child.tpl'));
|
||||
}
|
||||
|
||||
public function _testParent() {
|
||||
public function _testParent()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,28 @@
|
||||
<?php
|
||||
namespace Fenom;
|
||||
|
||||
class FunctionsTest extends TestCase {
|
||||
class FunctionsTest extends TestCase
|
||||
{
|
||||
|
||||
const FUNCTION_ARGUMENT_CONSTANT = 1;
|
||||
|
||||
public static function functionSum($of = array()) {
|
||||
public static function functionSum($of = array())
|
||||
{
|
||||
return array_sum($of);
|
||||
}
|
||||
|
||||
public static function functionPow($a, $n = 2) {
|
||||
public static function functionPow($a, $n = 2)
|
||||
{
|
||||
return pow($a, $n);
|
||||
}
|
||||
|
||||
public static function functionInc($a, $i = self::FUNCTION_ARGUMENT_CONSTANT) {
|
||||
public static function functionInc($a, $i = self::FUNCTION_ARGUMENT_CONSTANT)
|
||||
{
|
||||
return $a + $i;
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->fenom->addFunctionSmart('sum', __CLASS__ . '::functionSum');
|
||||
$this->fenom->addFunctionSmart('pow', __CLASS__ . '::functionPow');
|
||||
@ -32,37 +37,44 @@ class FunctionsTest extends TestCase {
|
||||
$this->tpl('function_array_param_pos.tpl', '{sum [1, 2, 3, 4, 5]}');
|
||||
}
|
||||
|
||||
public function testFunctionWithParams() {
|
||||
public function testFunctionWithParams()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_params_scalar.tpl');
|
||||
$this->assertEquals('8', $output);
|
||||
}
|
||||
|
||||
public function testFunctionWithDynamicParams() {
|
||||
public function testFunctionWithDynamicParams()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_params_dynamic.tpl', array('a' => 3, 'n' => 4));
|
||||
$this->assertEquals('81', $output);
|
||||
}
|
||||
|
||||
public function testFunctionWithDefaultParamScalar() {
|
||||
public function testFunctionWithDefaultParamScalar()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_default_param_scalar.tpl');
|
||||
$this->assertEquals('4', $output);
|
||||
}
|
||||
|
||||
public function testFunctionWithDefaultParamArray() {
|
||||
public function testFunctionWithDefaultParamArray()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_default_param_empty_array.tpl');
|
||||
$this->assertEquals('0', $output);
|
||||
}
|
||||
|
||||
public function testFunctionWithDefaultParamConst() {
|
||||
public function testFunctionWithDefaultParamConst()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_default_param_const.tpl');
|
||||
$this->assertEquals('2', $output);
|
||||
}
|
||||
|
||||
public function testFunctionWithArrayNamedParam() {
|
||||
public function testFunctionWithArrayNamedParam()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_array_param.tpl');
|
||||
$this->assertEquals('15', $output);
|
||||
}
|
||||
|
||||
public function testFunctionWithArrayPositionalParam() {
|
||||
public function testFunctionWithArrayPositionalParam()
|
||||
{
|
||||
$output = $this->fenom->fetch('function_array_param_pos.tpl');
|
||||
$this->assertEquals('15', $output);
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace Fenom;
|
||||
|
||||
class MacrosTest extends TestCase {
|
||||
class MacrosTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp() {
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tpl("math.tpl", '
|
||||
{macro plus(x, y)}
|
||||
@ -42,22 +44,46 @@ class MacrosTest extends TestCase {
|
||||
|
||||
a: {macro.plus x=5 y=3}.
|
||||
');
|
||||
|
||||
$this->tpl("macro_recursive.tpl", '{macro factorial(num)}
|
||||
{if $num}
|
||||
{$num} {macro.factorial num=$num-1} {$num}
|
||||
{/if}
|
||||
{/macro}
|
||||
|
||||
{macro.factorial num=10}');
|
||||
}
|
||||
|
||||
public function testMacros() {
|
||||
public function _testSandbox()
|
||||
{
|
||||
try {
|
||||
$this->fenom->compile("macro_recursive.tpl");
|
||||
// $this->fenom->flush();
|
||||
// var_dump($this->fenom->fetch("macro_recursive.tpl", []));
|
||||
var_dump( $this->fenom->compile("macro_recursive.tpl")->getTemplateCode());
|
||||
} catch (\Exception $e) {
|
||||
var_dump($e->getMessage() . ": " . $e->getTraceAsString());
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
public function testMacros()
|
||||
{
|
||||
$tpl = $this->fenom->compile('math.tpl');
|
||||
|
||||
$this->assertStringStartsWith('x + y = ', trim($tpl->macros["plus"]["body"]));
|
||||
$this->assertSame('Math: x + y = 5 , x - y - z = 6', Modifier::strip($tpl->fetch(array()), true));
|
||||
}
|
||||
|
||||
public function testImport() {
|
||||
public function testImport()
|
||||
{
|
||||
$tpl = $this->fenom->compile('import.tpl');
|
||||
|
||||
$this->assertSame('Imp: x + y = 3 , x - y - z = 3', Modifier::strip($tpl->fetch(array()), true));
|
||||
}
|
||||
|
||||
public function testImportCustom() {
|
||||
public function testImportCustom()
|
||||
{
|
||||
$tpl = $this->fenom->compile('import_custom.tpl');
|
||||
|
||||
$this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true));
|
||||
@ -65,11 +91,20 @@ class MacrosTest extends TestCase {
|
||||
|
||||
/**
|
||||
* @expectedExceptionMessage Undefined macro 'plus'
|
||||
* @expectedException \Fenom\CompileException
|
||||
* @expectedException \Fenom\Error\CompileException
|
||||
*/
|
||||
public function testImportMiss() {
|
||||
public function testImportMiss()
|
||||
{
|
||||
$tpl = $this->fenom->compile('import_miss.tpl');
|
||||
|
||||
$this->assertSame('a: x + y = 3 , x - y - z = 3 , new minus macros .', Modifier::strip($tpl->fetch(array()), true));
|
||||
}
|
||||
|
||||
public function testRecursive()
|
||||
{
|
||||
$this->fenom->compile('macro_recursive.tpl');
|
||||
$this->fenom->flush();
|
||||
$tpl = $this->fenom->getTemplate('macro_recursive.tpl');
|
||||
$this->assertSame("10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10", Modifier::strip($tpl->fetch(array()), true));
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@
|
||||
namespace Fenom;
|
||||
|
||||
|
||||
class ModifiersTest extends TestCase {
|
||||
class ModifiersTest extends TestCase
|
||||
{
|
||||
|
||||
public static function providerTruncate() {
|
||||
public static function providerTruncate()
|
||||
{
|
||||
$lorem = 'Lorem ipsum dolor sit amet'; // en
|
||||
$uni = 'Лорем ипсум долор сит амет'; // ru
|
||||
return array(
|
||||
@ -33,7 +35,8 @@ class ModifiersTest extends TestCase {
|
||||
* @param bool $by_words
|
||||
* @param bool $middle
|
||||
*/
|
||||
public function testTruncate($in, $out, $count, $delim = '...', $by_words = false, $middle = false) {
|
||||
public function testTruncate($in, $out, $count, $delim = '...', $by_words = false, $middle = false)
|
||||
{
|
||||
$tpl = $this->fenom->compileCode('{$text|truncate:$count:$delim:$by_words:$middle}');
|
||||
$this->assertEquals($out, $tpl->fetch(array(
|
||||
"text" => $in,
|
||||
@ -44,7 +47,8 @@ class ModifiersTest extends TestCase {
|
||||
)));
|
||||
}
|
||||
|
||||
public static function providerUpLow() {
|
||||
public static function providerUpLow()
|
||||
{
|
||||
return array(
|
||||
array("up", "lorem", "LOREM"),
|
||||
array("up", "Lorem", "LOREM"),
|
||||
@ -64,14 +68,16 @@ class ModifiersTest extends TestCase {
|
||||
* @param $in
|
||||
* @param $out
|
||||
*/
|
||||
public function testUpLow($modifier, $in, $out) {
|
||||
public function testUpLow($modifier, $in, $out)
|
||||
{
|
||||
$tpl = $this->fenom->compileCode('{$text|' . $modifier . '}');
|
||||
$this->assertEquals($out, $tpl->fetch(array(
|
||||
"text" => $in,
|
||||
)));
|
||||
}
|
||||
|
||||
public static function providerLength() {
|
||||
public static function providerLength()
|
||||
{
|
||||
return array(
|
||||
array("length", 6),
|
||||
array("длина", 5),
|
||||
@ -90,7 +96,8 @@ class ModifiersTest extends TestCase {
|
||||
* @param $in
|
||||
* @param $out
|
||||
*/
|
||||
public function testLength($in, $out) {
|
||||
public function testLength($in, $out)
|
||||
{
|
||||
$tpl = $this->fenom->compileCode('{$data|length}');
|
||||
$this->assertEquals($out, $tpl->fetch(array(
|
||||
"data" => $in,
|
||||
|
@ -3,13 +3,15 @@ namespace Fenom;
|
||||
use Fenom;
|
||||
use Fenom\TestCase;
|
||||
|
||||
class ProviderTest extends TestCase {
|
||||
class ProviderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Provider
|
||||
*/
|
||||
public $provider;
|
||||
|
||||
public function setUp() {
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tpl("template1.tpl", 'Template 1 {$a}');
|
||||
$this->tpl("template2.tpl", 'Template 2 {$a}');
|
||||
@ -18,13 +20,15 @@ class ProviderTest extends TestCase {
|
||||
clearstatcache();
|
||||
}
|
||||
|
||||
public function testIsTemplateExists() {
|
||||
public function testIsTemplateExists()
|
||||
{
|
||||
clearstatcache();
|
||||
$this->assertTrue($this->provider->templateExists("template1.tpl"));
|
||||
$this->assertFalse($this->provider->templateExists("unexists.tpl"));
|
||||
}
|
||||
|
||||
public function testGetSource() {
|
||||
public function testGetSource()
|
||||
{
|
||||
clearstatcache();
|
||||
$src = $this->provider->getSource("template1.tpl", $time);
|
||||
clearstatcache();
|
||||
@ -35,11 +39,13 @@ class ProviderTest extends TestCase {
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testGetSourceInvalid() {
|
||||
public function testGetSourceInvalid()
|
||||
{
|
||||
$this->provider->getSource("unexists.tpl", $time);
|
||||
}
|
||||
|
||||
public function testGetLastModified() {
|
||||
public function testGetLastModified()
|
||||
{
|
||||
$time = $this->provider->getLastModified("template1.tpl");
|
||||
clearstatcache();
|
||||
$this->assertEquals(filemtime(FENOM_RESOURCES . '/template/template1.tpl'), $time);
|
||||
@ -48,11 +54,13 @@ class ProviderTest extends TestCase {
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testGetLastModifiedInvalid() {
|
||||
public function testGetLastModifiedInvalid()
|
||||
{
|
||||
$this->provider->getLastModified("unexists.tpl");
|
||||
}
|
||||
|
||||
public function testVerify() {
|
||||
public function testVerify()
|
||||
{
|
||||
$templates = array(
|
||||
"template1.tpl" => filemtime(FENOM_RESOURCES . '/template/template1.tpl'),
|
||||
"template2.tpl" => filemtime(FENOM_RESOURCES . '/template/template2.tpl')
|
||||
@ -68,7 +76,8 @@ class ProviderTest extends TestCase {
|
||||
$this->assertTrue($this->provider->verify($templates));
|
||||
}
|
||||
|
||||
public function testVerifyInvalid() {
|
||||
public function testVerifyInvalid()
|
||||
{
|
||||
$templates = array(
|
||||
"template1.tpl" => filemtime(FENOM_RESOURCES . '/template/template1.tpl'),
|
||||
"template2.tpl" => filemtime(FENOM_RESOURCES . '/template/template2.tpl') + 1
|
||||
@ -83,7 +92,8 @@ class ProviderTest extends TestCase {
|
||||
$this->assertFalse($this->provider->verify($templates));
|
||||
}
|
||||
|
||||
public function testGetAll() {
|
||||
public function testGetAll()
|
||||
{
|
||||
$list = $this->provider->getList();
|
||||
sort($list);
|
||||
$this->assertSame(array(
|
||||
|
@ -3,14 +3,16 @@ namespace Fenom;
|
||||
use Fenom,
|
||||
Fenom\Render;
|
||||
|
||||
class RenderTest extends \PHPUnit_Framework_TestCase {
|
||||
class RenderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Render
|
||||
*/
|
||||
public static $render;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::$render = new Render(Fenom::factory("."), function ($tpl) {
|
||||
echo "It is render's function " . $tpl["render"];
|
||||
}, array(
|
||||
@ -18,7 +20,8 @@ class RenderTest extends \PHPUnit_Framework_TestCase {
|
||||
));
|
||||
}
|
||||
|
||||
public function testCreate() {
|
||||
public function testCreate()
|
||||
{
|
||||
$r = new Render(Fenom::factory("."), function () {
|
||||
echo "Test render";
|
||||
}, array(
|
||||
@ -27,14 +30,16 @@ class RenderTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertSame("Test render", $r->fetch(array()));
|
||||
}
|
||||
|
||||
public function testDisplay() {
|
||||
public function testDisplay()
|
||||
{
|
||||
ob_start();
|
||||
self::$render->display(array("render" => "display"));
|
||||
$out = ob_get_clean();
|
||||
$this->assertSame("It is render's function display", $out);
|
||||
}
|
||||
|
||||
public function testFetch() {
|
||||
public function testFetch()
|
||||
{
|
||||
$this->assertSame("It is render's function fetch", self::$render->fetch(array("render" => "fetch")));
|
||||
}
|
||||
|
||||
@ -42,7 +47,8 @@ class RenderTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage template error
|
||||
*/
|
||||
public function testFetchException() {
|
||||
public function testFetchException()
|
||||
{
|
||||
$render = new Render(Fenom::factory("."), function () {
|
||||
echo "error";
|
||||
throw new \RuntimeException("template error");
|
||||
|
@ -1,22 +1,26 @@
|
||||
<?php
|
||||
namespace Fenom;
|
||||
|
||||
class ScopeTest extends TestCase {
|
||||
public function openTag($tokenizer, $scope) {
|
||||
class ScopeTest extends TestCase
|
||||
{
|
||||
public function openTag($tokenizer, $scope)
|
||||
{
|
||||
$this->assertInstanceOf('Fenom\Tokenizer', $tokenizer);
|
||||
$this->assertInstanceOf('Fenom\Scope', $scope);
|
||||
$scope["value"] = true;
|
||||
return "open-tag";
|
||||
}
|
||||
|
||||
public function closeTag($tokenizer, $scope) {
|
||||
public function closeTag($tokenizer, $scope)
|
||||
{
|
||||
$this->assertInstanceOf('Fenom\Tokenizer', $tokenizer);
|
||||
$this->assertInstanceOf('Fenom\Scope', $scope);
|
||||
$this->assertTrue($scope["value"]);
|
||||
return "close-tag";
|
||||
}
|
||||
|
||||
public function testBlock() {
|
||||
public function testBlock()
|
||||
{
|
||||
/*$scope = new Scope($this->fenom, new Template($this->fenom), 1, array(
|
||||
"open" => array($this, "openTag"),
|
||||
"close" => array($this, "closeTag")
|
||||
|
@ -3,9 +3,11 @@
|
||||
namespace Fenom;
|
||||
|
||||
|
||||
class TagsTest extends TestCase {
|
||||
class TagsTest extends TestCase
|
||||
{
|
||||
|
||||
public function _testSandbox() {
|
||||
public function _testSandbox()
|
||||
{
|
||||
try {
|
||||
var_dump($this->fenom->compileCode('{var $a=Fenom\TestCase::dots("asd")}')->getBody());
|
||||
} catch (\Exception $e) {
|
||||
@ -17,39 +19,45 @@ class TagsTest extends TestCase {
|
||||
/**
|
||||
* @dataProvider providerScalars
|
||||
*/
|
||||
public function testVar($tpl_val, $val) {
|
||||
public function testVar($tpl_val, $val)
|
||||
{
|
||||
$this->assertRender("{var \$a=$tpl_val}\nVar: {\$a}", "\nVar: " . $val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerScalars
|
||||
*/
|
||||
public function testVarBlock($tpl_val, $val) {
|
||||
public function testVarBlock($tpl_val, $val)
|
||||
{
|
||||
$this->assertRender("{var \$a}before {{$tpl_val}} after{/var}\nVar: {\$a}", "\nVar: before " . $val . " after");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerScalars
|
||||
*/
|
||||
public function testVarBlockModified($tpl_val, $val) {
|
||||
public function testVarBlockModified($tpl_val, $val)
|
||||
{
|
||||
$this->assertRender("{var \$a|low|dots}before {{$tpl_val}} after{/var}\nVar: {\$a}", "\nVar: " . strtolower("before " . $val . " after") . "...");
|
||||
}
|
||||
|
||||
public function testCycle() {
|
||||
public function testCycle()
|
||||
{
|
||||
$this->assertRender('{for $i=0 to=4}{cycle ["one", "two"]}, {/for}', "one, two, one, two, one, ");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testCycleIndex() {
|
||||
public function testCycleIndex()
|
||||
{
|
||||
$this->assertRender('{var $a=["one", "two"]}{for $i=1 to=5}{cycle $a index=$i}, {/for}', "two, one, two, one, two, ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerScalars
|
||||
*/
|
||||
public function testFilter($tpl_val, $val) {
|
||||
public function testFilter($tpl_val, $val)
|
||||
{
|
||||
$this->assertRender("{filter|up} before {{$tpl_val}} after {/filter}", strtoupper(" before {$val} after "));
|
||||
}
|
||||
|
||||
|
@ -9,14 +9,17 @@ use Fenom\Template,
|
||||
*
|
||||
* @package Fenom
|
||||
*/
|
||||
class TemplateTest extends TestCase {
|
||||
class TemplateTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp() {
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tpl('welcome.tpl', '<b>Welcome, {$username} ({$email})</b>');
|
||||
}
|
||||
|
||||
public static function providerVars() {
|
||||
public static function providerVars()
|
||||
{
|
||||
$a = array("a" => "World");
|
||||
$obj = new \stdClass;
|
||||
$obj->name = "Object";
|
||||
@ -75,20 +78,22 @@ class TemplateTest extends TestCase {
|
||||
}
|
||||
|
||||
|
||||
public static function providerVarsInvalid() {
|
||||
public static function providerVarsInvalid()
|
||||
{
|
||||
return array(
|
||||
array('hello, {$a.}!', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('hello, {$b[c}!', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('hello, {$b.c]}!', 'Fenom\CompileException', "Unexpected token ']'"),
|
||||
array('hello, {$b[ ]}!', 'Fenom\CompileException', "Unexpected token ']'"),
|
||||
array('hello, {$b[9/].c}!', 'Fenom\CompileException', "Unexpected token ']'"),
|
||||
array('hello, {$b[3]$c}!', 'Fenom\CompileException', "Unexpected token '\$c'"),
|
||||
array('hello, {$b[3]c}!', 'Fenom\CompileException', "Unexpected token 'c'"),
|
||||
array('hello, {$b.obj->valid()}!', 'Fenom\SecurityException', "Forbidden to call methods", Fenom::DENY_METHODS),
|
||||
array('hello, {$a.}!', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('hello, {$b[c}!', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('hello, {$b.c]}!', 'Fenom\Error\CompileException', "Unexpected token ']'"),
|
||||
array('hello, {$b[ ]}!', 'Fenom\Error\CompileException', "Unexpected token ']'"),
|
||||
array('hello, {$b[9/].c}!', 'Fenom\Error\CompileException', "Unexpected token ']'"),
|
||||
array('hello, {$b[3]$c}!', 'Fenom\Error\CompileException', "Unexpected token '\$c'"),
|
||||
array('hello, {$b[3]c}!', 'Fenom\Error\CompileException', "Unexpected token 'c'"),
|
||||
array('hello, {$b.obj->valid()}!', 'Fenom\Error\SecurityException', "Forbidden to call methods", Fenom::DENY_METHODS),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerModifiers() {
|
||||
public static function providerModifiers()
|
||||
{
|
||||
$b = array(
|
||||
"a" => "World",
|
||||
"b" => array(
|
||||
@ -127,18 +132,20 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerModifiersInvalid() {
|
||||
public static function providerModifiersInvalid()
|
||||
{
|
||||
return array(
|
||||
array('Mod: {$lorem|}!', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Mod: {$lorem|str_rot13}!', 'Fenom\CompileException', "Modifier str_rot13 not found", Fenom::DENY_INLINE_FUNCS),
|
||||
array('Mod: {$lorem|my_encode}!', 'Fenom\CompileException', "Modifier my_encode not found"),
|
||||
array('Mod: {$lorem|truncate:}!', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Mod: {$lorem|truncate:abs}!', 'Fenom\CompileException', "Unexpected token 'abs'"),
|
||||
array('Mod: {$lorem|truncate:80|}!', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Mod: {$lorem|}!', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Mod: {$lorem|str_rot13}!', 'Fenom\Error\CompileException', "Modifier str_rot13 not found", Fenom::DENY_INLINE_FUNCS),
|
||||
array('Mod: {$lorem|my_encode}!', 'Fenom\Error\CompileException', "Modifier my_encode not found"),
|
||||
array('Mod: {$lorem|truncate:}!', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Mod: {$lorem|truncate:abs}!', 'Fenom\Error\CompileException', "Unexpected token 'abs'"),
|
||||
array('Mod: {$lorem|truncate:80|}!', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerExpressions() {
|
||||
public static function providerExpressions()
|
||||
{
|
||||
$b = array(
|
||||
"x" => $x = 9,
|
||||
"y" => 27,
|
||||
@ -175,25 +182,28 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerExpressionsInvalid() {
|
||||
public static function providerExpressionsInvalid()
|
||||
{
|
||||
return array(
|
||||
array('If: {-"hi"} end', 'Fenom\CompileException', "Unexpected token '-'"),
|
||||
array('If: {($a++)++} end', 'Fenom\CompileException', "Unexpected token '++'"),
|
||||
array('If: {$a + * $c} end', 'Fenom\CompileException', "Unexpected token '*'"),
|
||||
array('If: {$a + } end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('If: {$a + =} end', 'Fenom\CompileException', "Unexpected token '='"),
|
||||
array('If: {$a + 1 =} end', 'Fenom\CompileException', "Unexpected token '='"),
|
||||
array('If: {$a + 1 = 6} end', 'Fenom\CompileException', "Unexpected token '='"),
|
||||
array('If: {/$a} end', 'Fenom\CompileException', "Unexpected token '\$a'"),
|
||||
array('If: {$a == 5 > 4} end', 'Fenom\CompileException', "Unexpected token '>'"),
|
||||
array('If: {$a != 5 <= 4} end', 'Fenom\CompileException', "Unexpected token '<='"),
|
||||
array('If: {$a != 5 => 4} end', 'Fenom\CompileException', "Unexpected token '=>'"),
|
||||
array('If: {$a + (*6)} end', 'Fenom\CompileException', "Unexpected token '*'"),
|
||||
array('If: {$a + ( 6} end', 'Fenom\CompileException', "Unexpected end of expression, expect ')'"),
|
||||
array('If: {-"hi"} end', 'Fenom\Error\CompileException', "Unexpected token '-'"),
|
||||
array('If: {-[1,2]} end', 'Fenom\Error\CompileException', "Unexpected token '-'"),
|
||||
array('If: {($a++)++} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||
array('If: {$a + * $c} end', 'Fenom\Error\CompileException', "Unexpected token '*'"),
|
||||
array('If: {$a + } end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('If: {$a + =} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||
array('If: {$a + 1 =} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||
array('If: {$a + 1 = 6} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||
array('If: {/$a} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"),
|
||||
array('If: {$a == 5 > 4} end', 'Fenom\Error\CompileException', "Unexpected token '>'"),
|
||||
array('If: {$a != 5 <= 4} end', 'Fenom\Error\CompileException', "Unexpected token '<='"),
|
||||
array('If: {$a != 5 => 4} end', 'Fenom\Error\CompileException', "Unexpected token '=>'"),
|
||||
array('If: {$a + (*6)} end', 'Fenom\Error\CompileException', "Unexpected token '*'"),
|
||||
array('If: {$a + ( 6} end', 'Fenom\Error\CompileException', "Unexpected end of expression, expect ')'"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerInclude() {
|
||||
public static function providerInclude()
|
||||
{
|
||||
$a = array(
|
||||
"name" => "welcome",
|
||||
"tpl" => "welcome.tpl",
|
||||
@ -224,14 +234,16 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerIncludeInvalid() {
|
||||
public static function providerIncludeInvalid()
|
||||
{
|
||||
return array(
|
||||
array('Include {include} template', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Include {include another="welcome.tpl"} template', 'Fenom\CompileException', "Unexpected token '='"),
|
||||
array('Include {include} template', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Include {include another="welcome.tpl"} template', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerIf() {
|
||||
public static function providerIf()
|
||||
{
|
||||
$a = array(
|
||||
"val1" => 1,
|
||||
"val0" => 0,
|
||||
@ -268,16 +280,18 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerIfInvalid() {
|
||||
public static function providerIfInvalid()
|
||||
{
|
||||
return array(
|
||||
array('If: {if} block1 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('If: {if 1} block1 {elseif} block2 {/if} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('If: {if 1} block1 {else} block2 {elseif 0} block3 {/if} end', 'Fenom\CompileException', "Incorrect use of the tag {elseif}"),
|
||||
array('If: {if 1} block1 {else} block2 {/if} block3 {elseif 0} end', 'Fenom\CompileException', "Unexpected tag 'elseif' (this tag can be used with 'if')"),
|
||||
array('If: {if} block1 {/if} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('If: {if 1} block1 {elseif} block2 {/if} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('If: {if 1} block1 {else} block2 {elseif 0} block3 {/if} end', 'Fenom\Error\CompileException', "Incorrect use of the tag {elseif}"),
|
||||
array('If: {if 1} block1 {else} block2 {/if} block3 {elseif 0} end', 'Fenom\Error\CompileException', "Unexpected tag 'elseif' (this tag can be used with 'if')"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerCreateVar() {
|
||||
public static function providerCreateVar()
|
||||
{
|
||||
$a = array(
|
||||
"x" => 9,
|
||||
"y" => 27,
|
||||
@ -309,27 +323,29 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerCreateVarInvalid() {
|
||||
public static function providerCreateVarInvalid()
|
||||
{
|
||||
return array(
|
||||
array('Create: {var $v} Result: {$v} end', 'Fenom\CompileException', "Unclosed tag: {var} opened"),
|
||||
array('Create: {var $v = } Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Create: {var $v = 1++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = c} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 'c'"),
|
||||
array('Create: {var $v = ($a)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\CompileException', "Can not use two increments and/or decrements for one variable"),
|
||||
array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\CompileException', "Modifier max not found", Fenom::DENY_INLINE_FUNCS),
|
||||
array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '*'"),
|
||||
array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\CompileException', "Unexpected token '\$a'"),
|
||||
array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"),
|
||||
array('Create: {var $v = isset(2)} Result: {$v} end', 'Fenom\CompileException', "Unexpected token 2, isset() and empty() accept only variables"),
|
||||
array('Create: {var $v} Result: {$v} end', 'Fenom\Error\CompileException', "Unclosed tag: {var} opened"),
|
||||
array('Create: {var $v = } Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Create: {var $v = 1++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = c} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 'c'"),
|
||||
array('Create: {var $v = ($a)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = --$a++} Result: {$v} end', 'Fenom\Error\CompileException', "Can not use two increments and/or decrements for one variable"),
|
||||
array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||
array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\Error\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS),
|
||||
array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"),
|
||||
array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"),
|
||||
array('Create: {var $v = isset(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerTernary() {
|
||||
public static function providerTernary()
|
||||
{
|
||||
$a = array(
|
||||
"a" => 1,
|
||||
"em" => "empty",
|
||||
@ -373,6 +389,8 @@ class TemplateTest extends TestCase {
|
||||
array('{$empty.bool?:"empty"}', $a, "empty"),
|
||||
array('{$empty.unexist?:"empty"}', $a, "empty"),
|
||||
// ? ... : ....
|
||||
array('{$unexists ? "no way" : "right"}', $a),
|
||||
array('{$a ? "right" : "no way"}', $a),
|
||||
// !
|
||||
array('{if $a!} right {/if}', $a),
|
||||
array('{if $unexists!} no way {else} right {/if}', $a),
|
||||
@ -388,11 +406,16 @@ class TemplateTest extends TestCase {
|
||||
array('{if $nonempty.double!} right {/if}', $a),
|
||||
array('{if $nonempty.bool!} right {/if}', $a),
|
||||
// ! ... : ...
|
||||
array('{$unexists ! "no way" : "right"}', $a),
|
||||
array('{$a ! "right" : "no way"}', $a),
|
||||
// !: ...
|
||||
array('{$unexists !: "right"}', $a),
|
||||
array('{$a !: "right"}', $a, '1'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerForeach() {
|
||||
public static function providerForeach()
|
||||
{
|
||||
$a = array(
|
||||
"list" => array(1 => "one", 2 => "two", 3 => "three"),
|
||||
"empty" => array()
|
||||
@ -417,33 +440,35 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerForeachInvalid() {
|
||||
public static function providerForeachInvalid()
|
||||
{
|
||||
return array(
|
||||
array('Foreach: {foreach} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of tag {foreach}"),
|
||||
array('Foreach: {foreach $list} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach array_random() as $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'array_random'"),
|
||||
array('Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $k+1 => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as max($i,1) => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as max($e,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '=>'"),
|
||||
array('Foreach: {foreach $list $k => $e} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '\$k'"),
|
||||
array('Foreach: {foreach $list as $k =>} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'last' in tag {foreach}"),
|
||||
array('Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unknown parameter 'unknown'"),
|
||||
array('Foreach: {foreach $list as $e index=$i+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $e first=$f+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $e last=$l+1} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $e index=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as $e first=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as $e last=max($i,1)} {$e}, {/foreach} end', 'Fenom\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {break} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {break}"),
|
||||
array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {continue} {/foreach} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"),
|
||||
array('Foreach: {foreach} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected end of tag {foreach}"),
|
||||
array('Foreach: {foreach $list} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Foreach: {foreach $list+1 as $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach array_random() as $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'array_random'"),
|
||||
array('Foreach: {foreach $list as $e+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $k+1 => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as max($i,1) => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as max($e,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '=>'"),
|
||||
array('Foreach: {foreach $list $k => $e} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '\$k'"),
|
||||
array('Foreach: {foreach $list as $k =>} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Foreach: {foreach last=$l $list as $e } {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'last' in tag {foreach}"),
|
||||
array('Foreach: {foreach $list as $e unknown=1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unknown parameter 'unknown'"),
|
||||
array('Foreach: {foreach $list as $e index=$i+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $e first=$f+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $e last=$l+1} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('Foreach: {foreach $list as $e index=max($i,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as $e first=max($i,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as $e last=max($i,1)} {$e}, {/foreach} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"),
|
||||
array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {break} {/foreach} end', 'Fenom\Error\CompileException', "Improper usage of the tag {break}"),
|
||||
array('Foreach: {foreach $list as $e} {$e}, {foreachelse} {continue} {/foreach} end', 'Fenom\Error\CompileException', "Improper usage of the tag {continue}"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerIgnores() {
|
||||
public static function providerIgnores()
|
||||
{
|
||||
$a = array("a" => "lit. A");
|
||||
return array(
|
||||
array('{if 0}none{/if} literal: {$a} end', $a, 'literal: lit. A end'),
|
||||
@ -456,7 +481,8 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerSwitch() {
|
||||
public static function providerSwitch()
|
||||
{
|
||||
$code1 = 'Switch: {switch $a}
|
||||
{case 1} one {break}
|
||||
{case 2} two {break}
|
||||
@ -483,15 +509,17 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerSwitchInvalid() {
|
||||
public static function providerSwitchInvalid()
|
||||
{
|
||||
return array(
|
||||
array('Switch: {switch}{case 1} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Switch: {switch 1}{case} one {break}{/switch} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('Switch: {switch 1}{break}{case} one {/switch} end', 'Fenom\CompileException', "Improper usage of the tag {break}"),
|
||||
array('Switch: {switch}{case 1} one {break}{/switch} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Switch: {switch 1}{case} one {break}{/switch} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('Switch: {switch 1}{break}{case} one {/switch} end', 'Fenom\Error\CompileException', "Improper usage of the tag {break}"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerWhile() {
|
||||
public static function providerWhile()
|
||||
{
|
||||
$a = array("a" => 3);
|
||||
return array(
|
||||
array('While: {while false} block {/while} end', $a, 'While: end'),
|
||||
@ -501,13 +529,15 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerWhileInvalid() {
|
||||
public static function providerWhileInvalid()
|
||||
{
|
||||
return array(
|
||||
array('While: {while} block {/while} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('While: {while} block {/while} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerFor() {
|
||||
public static function providerFor()
|
||||
{
|
||||
$a = array("c" => 1, "s" => 1, "m" => 3);
|
||||
return array(
|
||||
array('For: {for $a=4 to=6} $a: {$a}, {/for} end', $a, 'For: $a: 4, $a: 5, $a: 6, end'),
|
||||
@ -529,35 +559,38 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerForInvalid() {
|
||||
public static function providerForInvalid()
|
||||
{
|
||||
return array(
|
||||
array('For: {for} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('For: {for $a=} block1 {/for} end', 'Fenom\CompileException', "Unexpected end of expression"),
|
||||
array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token '+'"),
|
||||
array('For: {for max($a,$b)=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'max'"),
|
||||
array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'to'"),
|
||||
array('For: {for index=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'index'"),
|
||||
array('For: {for first=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'first'"),
|
||||
array('For: {for last=$i $a=3 to=6} block1 {/for} end', 'Fenom\CompileException', "Unexpected token 'last'"),
|
||||
array('For: {for $a=4 to=6 unk=4} block1 {/for} end', 'Fenom\CompileException', "Unknown parameter 'unk'"),
|
||||
array('For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {break}"),
|
||||
array('For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end', 'Fenom\CompileException', "Improper usage of the tag {continue}"),
|
||||
array('For: {for} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('For: {for $a=} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||
array('For: {for $a+1=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
||||
array('For: {for max($a,$b)=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'max'"),
|
||||
array('For: {for to=6 $a=3} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'to'"),
|
||||
array('For: {for index=$i $a=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'index'"),
|
||||
array('For: {for first=$i $a=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'first'"),
|
||||
array('For: {for last=$i $a=3 to=6} block1 {/for} end', 'Fenom\Error\CompileException', "Unexpected token 'last'"),
|
||||
array('For: {for $a=4 to=6 unk=4} block1 {/for} end', 'Fenom\Error\CompileException', "Unknown parameter 'unk'"),
|
||||
array('For: {for $a=4 to=6} $a: {$a}, {forelse} {break} {/for} end', 'Fenom\Error\CompileException', "Improper usage of the tag {break}"),
|
||||
array('For: {for $a=4 to=6} $a: {$a}, {forelse} {continue} {/for} end', 'Fenom\Error\CompileException', "Improper usage of the tag {continue}"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerLayersInvalid() {
|
||||
public static function providerLayersInvalid()
|
||||
{
|
||||
return array(
|
||||
array('Layers: {foreach $list as $e} block1 {if 1} {foreachelse} {/if} {/foreach} end', 'Fenom\CompileException', "Unexpected tag 'foreachelse' (this tag can be used with 'foreach')"),
|
||||
array('Layers: {foreach $list as $e} block1 {if 1} {/foreach} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'foreach'"),
|
||||
array('Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Fenom\CompileException', "Unexpected tag 'forelse' (this tag can be used with 'for')"),
|
||||
array('Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Fenom\CompileException', "Unexpected closing of the tag 'for'"),
|
||||
array('Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Fenom\CompileException', "Unexpected tag 'case' (this tag can be used with 'switch')"),
|
||||
array('Layers: {/switch} end', 'Fenom\CompileException', "Unexpected closing of the tag 'switch'"),
|
||||
array('Layers: {if 1} end', 'Fenom\CompileException', "Unclosed tag: {if}"),
|
||||
array('Layers: {foreach $list as $e} block1 {if 1} {foreachelse} {/if} {/foreach} end', 'Fenom\Error\CompileException', "Unexpected tag 'foreachelse' (this tag can be used with 'foreach')"),
|
||||
array('Layers: {foreach $list as $e} block1 {if 1} {/foreach} {/if} end', 'Fenom\Error\CompileException', "Unexpected closing of the tag 'foreach'"),
|
||||
array('Layers: {for $a=4 to=6} block1 {if 1} {forelse} {/if} {/for} end', 'Fenom\Error\CompileException', "Unexpected tag 'forelse' (this tag can be used with 'for')"),
|
||||
array('Layers: {for $a=4 to=6} block1 {if 1} {/for} {/if} end', 'Fenom\Error\CompileException', "Unexpected closing of the tag 'for'"),
|
||||
array('Layers: {switch 1} {if 1} {case 1} {/if} {/switch} end', 'Fenom\Error\CompileException', "Unexpected tag 'case' (this tag can be used with 'switch')"),
|
||||
array('Layers: {/switch} end', 'Fenom\Error\CompileException', "Unexpected closing of the tag 'switch'"),
|
||||
array('Layers: {if 1} end', 'Fenom\Error\CompileException', "Unclosed tag: {if}"),
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerExtends() {
|
||||
public static function providerExtends()
|
||||
{
|
||||
return array(
|
||||
array('{extends file="parent.tpl"}{block name="bk1"} block1 {/block}', "Template extended by block1"),
|
||||
array('{extends "parent.tpl"}{block "bk1"} block1 {/block}', "Template extended by block1"),
|
||||
@ -568,7 +601,8 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerIsOperator() {
|
||||
public static function providerIsOperator()
|
||||
{
|
||||
return array(
|
||||
// is {$type}
|
||||
array('{if $one is int} block1 {else} block2 {/if}', 'block1'),
|
||||
@ -624,7 +658,8 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public static function providerInOperator() {
|
||||
public static function providerInOperator()
|
||||
{
|
||||
return array(
|
||||
array('{if $one in "qwertyuiop 1"} block1 {else} block2 {/if}', 'block1'),
|
||||
array('{if $one in string "qwertyuiop 1"} block1 {else} block2 {/if}', 'block1'),
|
||||
@ -641,9 +676,20 @@ class TemplateTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function _testSandbox() {
|
||||
public static function providerConcat() {
|
||||
return array(
|
||||
array('{"string" ~ $one ~ up("end")}', "string1END"),
|
||||
array('{"string" ~ $one++ ~ "end"}', "string1end"),
|
||||
array('{"string" ~ ++$one ~ "end"}', "string2end"),
|
||||
array('{"string" ~ "one" ~ "end"}', "stringoneend"),
|
||||
array('{"string" ~ 1 ~ "end"}', "string1end"),
|
||||
);
|
||||
}
|
||||
|
||||
public function _testSandbox()
|
||||
{
|
||||
try {
|
||||
var_dump($this->fenom->compileCode('{$one.two->three[e]()}')->getBody());
|
||||
var_dump($this->fenom->compileCode('{$a++~"hi"~time("Y:m:d")}')->getBody());
|
||||
} catch (\Exception $e) {
|
||||
print_r($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
}
|
||||
@ -653,28 +699,32 @@ class TemplateTest extends TestCase {
|
||||
/**
|
||||
* @dataProvider providerVars
|
||||
*/
|
||||
public function testVars($code, $vars, $result) {
|
||||
public function testVars($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerVarsInvalid
|
||||
*/
|
||||
public function testVarsInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testVarsInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerModifiers
|
||||
*/
|
||||
public function testModifiers($code, $vars, $result) {
|
||||
public function testModifiers($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerModifiersInvalid
|
||||
*/
|
||||
public function testModifiersInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testModifiersInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
@ -682,14 +732,16 @@ class TemplateTest extends TestCase {
|
||||
* @group expression
|
||||
* @dataProvider providerExpressions
|
||||
*/
|
||||
public function testExpressions($code, $vars, $result) {
|
||||
public function testExpressions($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerExpressionsInvalid
|
||||
*/
|
||||
public function testExpressionsInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testExpressionsInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
@ -697,14 +749,16 @@ class TemplateTest extends TestCase {
|
||||
* @group include
|
||||
* @dataProvider providerInclude
|
||||
*/
|
||||
public function testInclude($code, $vars, $result) {
|
||||
public function testInclude($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerIncludeInvalid
|
||||
*/
|
||||
public function testIncludeInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testIncludeInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
@ -712,28 +766,32 @@ class TemplateTest extends TestCase {
|
||||
* @dataProvider providerIf
|
||||
* @group test-if
|
||||
*/
|
||||
public function testIf($code, $vars, $result, $options = 0) {
|
||||
public function testIf($code, $vars, $result, $options = 0)
|
||||
{
|
||||
$this->exec($code, $vars, $result, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerIfInvalid
|
||||
*/
|
||||
public function testIfInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testIfInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerCreateVar
|
||||
*/
|
||||
public function testCreateVar($code, $vars, $result) {
|
||||
public function testCreateVar($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerCreateVarInvalid
|
||||
*/
|
||||
public function testCreateVarInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testCreateVarInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
@ -741,77 +799,88 @@ class TemplateTest extends TestCase {
|
||||
* @group ternary
|
||||
* @dataProvider providerTernary
|
||||
*/
|
||||
public function testTernary($code, $vars, $result = 'right') {
|
||||
public function testTernary($code, $vars, $result = 'right')
|
||||
{
|
||||
$this->exec(__FUNCTION__ . ": $code end", $vars, __FUNCTION__ . ": $result end");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerForeach
|
||||
*/
|
||||
public function testForeach($code, $vars, $result) {
|
||||
public function testForeach($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerForeachInvalid
|
||||
*/
|
||||
public function testForeachInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testForeachInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerFor
|
||||
*/
|
||||
public function testFor($code, $vars, $result) {
|
||||
public function testFor($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerForInvalid
|
||||
*/
|
||||
public function testForInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testForInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerIgnores
|
||||
*/
|
||||
public function testIgnores($code, $vars, $result) {
|
||||
public function testIgnores($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerSwitch
|
||||
*/
|
||||
public function testSwitch($code, $vars, $result) {
|
||||
public function testSwitch($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerSwitchInvalid
|
||||
*/
|
||||
public function testSwitchInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testSwitchInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerWhile
|
||||
*/
|
||||
public function testWhile($code, $vars, $result) {
|
||||
public function testWhile($code, $vars, $result)
|
||||
{
|
||||
$this->exec($code, $vars, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerWhileInvalid
|
||||
*/
|
||||
public function testWhileInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testWhileInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerLayersInvalid
|
||||
*/
|
||||
public function testLayersInvalid($code, $exception, $message, $options = 0) {
|
||||
public function testLayersInvalid($code, $exception, $message, $options = 0)
|
||||
{
|
||||
$this->execError($code, $exception, $message, $options);
|
||||
}
|
||||
|
||||
@ -819,7 +888,8 @@ class TemplateTest extends TestCase {
|
||||
* @group is_operator
|
||||
* @dataProvider providerIsOperator
|
||||
*/
|
||||
public function testIsOperator($code, $result) {
|
||||
public function testIsOperator($code, $result)
|
||||
{
|
||||
$this->exec($code, self::getVars(), $result);
|
||||
}
|
||||
|
||||
@ -827,7 +897,16 @@ class TemplateTest extends TestCase {
|
||||
* @group in_operator
|
||||
* @dataProvider providerInOperator
|
||||
*/
|
||||
public function testInOperator($code, $result) {
|
||||
public function testInOperator($code, $result)
|
||||
{
|
||||
$this->exec($code, self::getVars(), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerConcat
|
||||
*/
|
||||
public function testConcat($code, $result)
|
||||
{
|
||||
$this->exec($code, self::getVars(), $result);
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@
|
||||
namespace Fenom;
|
||||
use Fenom\Tokenizer;
|
||||
|
||||
class TokenizerTest extends \PHPUnit_Framework_TestCase {
|
||||
class TokenizerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testTokens() {
|
||||
public function testTokens()
|
||||
{
|
||||
$code = 'hello, please resolve this example: sin($x)+tan($x*$t) = {U|[0,1]}';
|
||||
$tokens = new Tokenizer($code);
|
||||
$this->assertSame(T_STRING, $tokens->key());
|
||||
@ -46,7 +48,8 @@ class TokenizerTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertSame("+", $tokens->getNext($tokens::MACRO_BINARY));
|
||||
}
|
||||
|
||||
public function testSkip() {
|
||||
public function testSkip()
|
||||
{
|
||||
$text = "1 foo: bar ( 3 + double ) ";
|
||||
$tokens = new Tokenizer($text);
|
||||
|
||||
@ -54,7 +57,7 @@ class TokenizerTest extends \PHPUnit_Framework_TestCase {
|
||||
try {
|
||||
$tokens->skip(T_STRING)->skip('(')->skip(':');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertInstanceOf('Fenom\UnexpectedTokenException', $e);
|
||||
$this->assertInstanceOf('Fenom\Error\UnexpectedTokenException', $e);
|
||||
$this->assertStringStartsWith("Unexpected token '3' in expression, expect ':'", $e->getMessage());
|
||||
}
|
||||
$this->assertTrue($tokens->valid());
|
||||
|
@ -3,9 +3,11 @@
|
||||
use Fenom\Render,
|
||||
Fenom\Provider as FS;
|
||||
|
||||
class FenomTest extends \Fenom\TestCase {
|
||||
class FenomTest extends \Fenom\TestCase
|
||||
{
|
||||
|
||||
public static function providerOptions() {
|
||||
public static function providerOptions()
|
||||
{
|
||||
return array(
|
||||
array("disable_methods", Fenom::DENY_METHODS),
|
||||
array("disable_native_funcs", Fenom::DENY_INLINE_FUNCS),
|
||||
@ -18,7 +20,8 @@ class FenomTest extends \Fenom\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testCompileFile() {
|
||||
public function testCompileFile()
|
||||
{
|
||||
$a = array(
|
||||
"a" => "a",
|
||||
"b" => "b"
|
||||
@ -32,14 +35,16 @@ class FenomTest extends \Fenom\TestCase {
|
||||
$this->assertSame(3, iterator_count(new FilesystemIterator(FENOM_RESOURCES . '/compile')));
|
||||
}
|
||||
|
||||
public function testStorage() {
|
||||
public function testStorage()
|
||||
{
|
||||
$this->tpl('custom.tpl', 'Custom template');
|
||||
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
|
||||
$this->tpl('custom.tpl', 'Custom template 2');
|
||||
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
|
||||
}
|
||||
|
||||
public function testCheckMTime() {
|
||||
public function testCheckMTime()
|
||||
{
|
||||
$this->fenom->setOptions(Fenom::FORCE_COMPILE);
|
||||
$this->tpl('custom.tpl', 'Custom template');
|
||||
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
|
||||
@ -49,7 +54,8 @@ class FenomTest extends \Fenom\TestCase {
|
||||
$this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array()));
|
||||
}
|
||||
|
||||
public function testForceCompile() {
|
||||
public function testForceCompile()
|
||||
{
|
||||
$this->fenom->setOptions(Fenom::FORCE_COMPILE);
|
||||
$this->tpl('custom.tpl', 'Custom template');
|
||||
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
|
||||
@ -57,7 +63,8 @@ class FenomTest extends \Fenom\TestCase {
|
||||
$this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array()));
|
||||
}
|
||||
|
||||
public function testSetModifier() {
|
||||
public function testSetModifier()
|
||||
{
|
||||
$this->fenom->addModifier("mymod", "myMod");
|
||||
$this->tpl('custom.tpl', 'Custom modifier {$a|mymod}');
|
||||
$this->assertSame("Custom modifier (myMod)Custom(/myMod)", $this->fenom->fetch('custom.tpl', array("a" => "Custom")));
|
||||
@ -66,7 +73,8 @@ class FenomTest extends \Fenom\TestCase {
|
||||
/**
|
||||
* @group add_functions
|
||||
*/
|
||||
public function testSetFunctions() {
|
||||
public function testSetFunctions()
|
||||
{
|
||||
$this->fenom->setOptions(Fenom::FORCE_COMPILE);
|
||||
$this->fenom->addFunction("myfunc", "myFunc");
|
||||
$this->fenom->addBlockFunction("myblockfunc", "myBlockFunc");
|
||||
@ -76,7 +84,8 @@ class FenomTest extends \Fenom\TestCase {
|
||||
$this->assertSame("Custom function Block:foo:this block1:Block", $this->fenom->fetch('custom.tpl', array()));
|
||||
}
|
||||
|
||||
public function testSetCompilers() {
|
||||
public function testSetCompilers()
|
||||
{
|
||||
$this->fenom->setOptions(Fenom::FORCE_COMPILE);
|
||||
$this->fenom->addCompiler("mycompiler", 'myCompiler');
|
||||
$this->fenom->addBlockCompiler("myblockcompiler", 'myBlockCompilerOpen', 'myBlockCompilerClose', array(
|
||||
@ -91,7 +100,8 @@ class FenomTest extends \Fenom\TestCase {
|
||||
/**
|
||||
* @dataProvider providerOptions
|
||||
*/
|
||||
public function testOptions($code, $option) {
|
||||
public function testOptions($code, $option)
|
||||
{
|
||||
static $options = array();
|
||||
static $flags = 0;
|
||||
$options[$code] = true;
|
||||
@ -104,4 +114,25 @@ class FenomTest extends \Fenom\TestCase {
|
||||
// printf("remove %010b from option %010b, flags %010b\n", $option, $this->fenom->getOptions(), $flags & ~$option);
|
||||
// $this->assertSame($this->fenom->getOptions(), $flags & ~$option);
|
||||
}
|
||||
|
||||
public function testFilter()
|
||||
{
|
||||
$punit = $this;
|
||||
$this->fenom->addPreFilter(function ($src, $tpl) use ($punit) {
|
||||
$this->assertInstanceOf('Fenom\Template', $tpl);
|
||||
return "== $src ==";
|
||||
});
|
||||
|
||||
$this->fenom->addPostFilter(function ($code, $tpl) use ($punit) {
|
||||
$this->assertInstanceOf('Fenom\Template', $tpl);
|
||||
return "+++ $code +++";
|
||||
});
|
||||
|
||||
$this->fenom->addFilter(function ($text, $tpl) use ($punit) {
|
||||
$this->assertInstanceOf('Fenom\Template', $tpl);
|
||||
return "|--- $text ---|";
|
||||
});
|
||||
|
||||
$this->assertSame('+++ |--- == hello ---||--- world == ---| +++', $this->fenom->compileCode('hello {var $user} god {/var} world')->fetch(array()));
|
||||
}
|
||||
}
|
@ -1,31 +1,38 @@
|
||||
<?php
|
||||
|
||||
function myMod($str) {
|
||||
function myMod($str)
|
||||
{
|
||||
return "(myMod)" . $str . "(/myMod)";
|
||||
}
|
||||
|
||||
function myFunc($params) {
|
||||
function myFunc($params)
|
||||
{
|
||||
return "MyFunc:" . $params["name"];
|
||||
}
|
||||
|
||||
function myBlockFunc($params, $content) {
|
||||
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\Template $tpl)
|
||||
{
|
||||
$p = $tpl->parseParams($tokenizer);
|
||||
return 'echo "PHP_VERSION: ".PHP_VERSION." (for ".' . $p["name"] . '.")";';
|
||||
}
|
||||
|
||||
function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) {
|
||||
function myBlockCompilerOpen(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope)
|
||||
{
|
||||
return myCompiler($tokenizer, $scope->tpl);
|
||||
}
|
||||
|
||||
function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) {
|
||||
function myBlockCompilerClose(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope)
|
||||
{
|
||||
return 'echo "End of compiler";';
|
||||
}
|
||||
|
||||
function myBlockCompilerTag(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope) {
|
||||
function myBlockCompilerTag(Fenom\Tokenizer $tokenizer, Fenom\Scope $scope)
|
||||
{
|
||||
$p = $scope->tpl->parseParams($tokenizer);
|
||||
return 'echo "Tag ".' . $p["name"] . '." of compiler";';
|
||||
}
|
Loading…
Reference in New Issue
Block a user