fix Fenom

This commit is contained in:
Nikita 2013-07-04 03:09:47 +04:00
parent ec1dba923c
commit 91bf32757e

View File

@ -7,30 +7,31 @@
* For the full copyright and license information, please view the license.md * For the full copyright and license information, please view the license.md
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Fenom\Template, use Fenom\ProviderInterface;
Fenom\ProviderInterface; use Fenom\Template;
/** /**
* Fenom Template Engine * Fenom Template Engine
* *
* @author Ivan Shalganov <a.cobest@gmail.com> * @author Ivan Shalganov <a.cobest@gmail.com>
*/ */
class Fenom { class Fenom
{
const VERSION = '1.0'; const VERSION = '1.0';
/* Compiler types */ /* Compiler types */
const INLINE_COMPILER = 1; const INLINE_COMPILER = 1;
const BLOCK_COMPILER = 2; const BLOCK_COMPILER = 2;
const INLINE_FUNCTION = 3; const INLINE_FUNCTION = 3;
const BLOCK_FUNCTION = 4; const BLOCK_FUNCTION = 4;
const MODIFIER = 5; const MODIFIER = 5;
/* Options */ /* Options */
const DENY_METHODS = 0x10; const DENY_METHODS = 0x10;
const DENY_INLINE_FUNCS = 0x20; const DENY_INLINE_FUNCS = 0x20;
const FORCE_INCLUDE = 0x40; const FORCE_INCLUDE = 0x40;
const AUTO_RELOAD = 0x80; const AUTO_RELOAD = 0x80;
const FORCE_COMPILE = 0xF0; const FORCE_COMPILE = 0xF0;
const DISABLE_CACHE = 0x1F0; const DISABLE_CACHE = 0x1F0;
const AUTO_ESCAPE = 0x200; const AUTO_ESCAPE = 0x200;
@ -40,20 +41,18 @@ class Fenom {
const DEFAULT_FUNC_OPEN = 'Fenom\Compiler::stdFuncOpen'; const DEFAULT_FUNC_OPEN = 'Fenom\Compiler::stdFuncOpen';
const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose'; const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose';
const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser'; const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser';
/** /**
* @var int[] of possible options, as associative array * @var int[] of possible options, as associative array
* @see setOptions, addOptions, delOptions * @see setOptions, addOptions, delOptions
*/ */
private static $_option_list = array( private static $_option_list = array(
"disable_methods" => self::DENY_METHODS, "disable_methods" => self::DENY_METHODS,
"disable_native_funcs" => self::DENY_INLINE_FUNCS, "disable_native_funcs" => self::DENY_INLINE_FUNCS,
"disable_cache" => self::DISABLE_CACHE, "disable_cache" => self::DISABLE_CACHE,
"force_compile" => self::FORCE_COMPILE, "force_compile" => self::FORCE_COMPILE,
"auto_reload" => self::AUTO_RELOAD, "auto_reload" => self::AUTO_RELOAD,
"force_include" => self::FORCE_INCLUDE, "force_include" => self::FORCE_INCLUDE,
); );
/** /**
* @var Fenom\Render[] Templates storage * @var Fenom\Render[] Templates storage
*/ */
@ -62,25 +61,17 @@ class Fenom {
* @var string compile directory * @var string compile directory
*/ */
protected $_compile_dir = "/tmp"; protected $_compile_dir = "/tmp";
/** /**
* @var int masked options * @var int masked options
*/ */
protected $_options = 0; protected $_options = 0;
protected $_on_pre_cmp = array(); protected $_on_pre_cmp = array();
protected $_on_cmp = array(); protected $_on_cmp = array();
protected $_on_post_cmp = array(); protected $_on_post_cmp = array();
/**
* @var ProviderInterface
*/
private $_provider;
/** /**
* @var Fenom\ProviderInterface[] * @var Fenom\ProviderInterface[]
*/ */
protected $_providers = array(); protected $_providers = array();
/** /**
* @var string[] list of modifiers [modifier_name => callable] * @var string[] list of modifiers [modifier_name => callable]
*/ */
@ -99,136 +90,147 @@ class Fenom {
"length" => 'Fenom\Modifier::length', "length" => 'Fenom\Modifier::length',
"default" => 'Fenom\Modifier::defaultValue' "default" => 'Fenom\Modifier::defaultValue'
); );
/** /**
* @var array of allowed PHP functions * @var array of allowed PHP functions
*/ */
protected $_allowed_funcs = array( protected $_allowed_funcs = array(
"count" => 1, "is_string" => 1, "is_array" => 1, "is_numeric" => 1, "is_int" => 1, "count" => 1, "is_string" => 1, "is_array" => 1, "is_numeric" => 1, "is_int" => 1,
"is_object" => 1, "strtotime" => 1, "gettype" => 1, "is_double" => 1, "json_encode" => 1, "json_decode" => 1, "is_object" => 1, "strtotime" => 1, "gettype" => 1, "is_double" => 1, "json_encode" => 1, "json_decode" => 1,
"ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1 "ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1
); );
/** /**
* @var array[] of compilers and functions * @var array[] of compilers and functions
*/ */
protected $_actions = array( protected $_actions = array(
'foreach' => array( // {foreach ...} {break} {continue} {foreachelse} {/foreach} 'foreach' => array( // {foreach ...} {break} {continue} {foreachelse} {/foreach}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::foreachOpen', 'open' => 'Fenom\Compiler::foreachOpen',
'close' => 'Fenom\Compiler::foreachClose', 'close' => 'Fenom\Compiler::foreachClose',
'tags' => array( 'tags' => array(
'foreachelse' => 'Fenom\Compiler::foreachElse', 'foreachelse' => 'Fenom\Compiler::foreachElse',
'break' => 'Fenom\Compiler::tagBreak', 'break' => 'Fenom\Compiler::tagBreak',
'continue' => 'Fenom\Compiler::tagContinue', 'continue' => 'Fenom\Compiler::tagContinue',
), ),
'float_tags' => array('break' => 1, 'continue' => 1) 'float_tags' => array('break' => 1, 'continue' => 1)
), ),
'if' => array( // {if ...} {elseif ...} {else} {/if} 'if' => array( // {if ...} {elseif ...} {else} {/if}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::ifOpen', 'open' => 'Fenom\Compiler::ifOpen',
'close' => 'Fenom\Compiler::stdClose', 'close' => 'Fenom\Compiler::stdClose',
'tags' => array( 'tags' => array(
'elseif' => 'Fenom\Compiler::tagElseIf', 'elseif' => 'Fenom\Compiler::tagElseIf',
'else' => 'Fenom\Compiler::tagElse', 'else' => 'Fenom\Compiler::tagElse',
) )
), ),
'switch' => array( // {switch ...} {case ...} {break} {default} {/switch} 'switch' => array( // {switch ...} {case ...} {break} {default} {/switch}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::switchOpen', 'open' => 'Fenom\Compiler::switchOpen',
'close' => 'Fenom\Compiler::stdClose', 'close' => 'Fenom\Compiler::stdClose',
'tags' => array( 'tags' => array(
'case' => 'Fenom\Compiler::tagCase', 'case' => 'Fenom\Compiler::tagCase',
'default' => 'Fenom\Compiler::tagDefault', 'default' => 'Fenom\Compiler::tagDefault',
'break' => 'Fenom\Compiler::tagBreak', 'break' => 'Fenom\Compiler::tagBreak',
), ),
'float_tags' => array('break' => 1) 'float_tags' => array('break' => 1)
), ),
'for' => array( // {for ...} {break} {continue} {/for} 'for' => array( // {for ...} {break} {continue} {/for}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::forOpen', 'open' => 'Fenom\Compiler::forOpen',
'close' => 'Fenom\Compiler::forClose', 'close' => 'Fenom\Compiler::forClose',
'tags' => array( 'tags' => array(
'forelse' => 'Fenom\Compiler::forElse', 'forelse' => 'Fenom\Compiler::forElse',
'break' => 'Fenom\Compiler::tagBreak', 'break' => 'Fenom\Compiler::tagBreak',
'continue' => 'Fenom\Compiler::tagContinue', 'continue' => 'Fenom\Compiler::tagContinue',
), ),
'float_tags' => array('break' => 1, 'continue' => 1) 'float_tags' => array('break' => 1, 'continue' => 1)
), ),
'while' => array( // {while ...} {break} {continue} {/while} 'while' => array( // {while ...} {break} {continue} {/while}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::whileOpen', 'open' => 'Fenom\Compiler::whileOpen',
'close' => 'Fenom\Compiler::stdClose', 'close' => 'Fenom\Compiler::stdClose',
'tags' => array( 'tags' => array(
'break' => 'Fenom\Compiler::tagBreak', 'break' => 'Fenom\Compiler::tagBreak',
'continue' => 'Fenom\Compiler::tagContinue', 'continue' => 'Fenom\Compiler::tagContinue',
), ),
'float_tags' => array('break' => 1, 'continue' => 1) 'float_tags' => array('break' => 1, 'continue' => 1)
), ),
'include' => array( // {include ...} 'include' => array( // {include ...}
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagInclude' 'parser' => 'Fenom\Compiler::tagInclude'
), ),
'var' => array( // {var ...} 'var' => array( // {var ...}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::varOpen', 'open' => 'Fenom\Compiler::varOpen',
'close' => 'Fenom\Compiler::varClose' 'close' => 'Fenom\Compiler::varClose'
), ),
'block' => array( // {block ...} {parent} {/block} 'block' => array( // {block ...} {parent} {/block}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::tagBlockOpen', 'open' => 'Fenom\Compiler::tagBlockOpen',
'close' => 'Fenom\Compiler::tagBlockClose', 'close' => 'Fenom\Compiler::tagBlockClose',
'tags' => array( 'tags' => array(
'parent' => 'Fenom\Compiler::tagParent' 'parent' => 'Fenom\Compiler::tagParent'
), ),
'float_tags' => array('parent' => 1) 'float_tags' => array('parent' => 1)
), ),
'extends' => array( // {extends ...} 'extends' => array( // {extends ...}
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagExtends' 'parser' => 'Fenom\Compiler::tagExtends'
), ),
'use' => array( // {use} 'use' => array( // {use}
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagUse' 'parser' => 'Fenom\Compiler::tagUse'
), ),
'capture' => array( // {capture ...} {/capture} 'capture' => array( // {capture ...} {/capture}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::captureOpen', 'open' => 'Fenom\Compiler::captureOpen',
'close' => 'Fenom\Compiler::captureClose' 'close' => 'Fenom\Compiler::captureClose'
), ),
'filter' => array( // {filter} ... {/filter} 'filter' => array( // {filter} ... {/filter}
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::filterOpen', 'open' => 'Fenom\Compiler::filterOpen',
'close' => 'Fenom\Compiler::filterClose' 'close' => 'Fenom\Compiler::filterClose'
), ),
'macro' => array( 'macro' => array(
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::macroOpen', 'open' => 'Fenom\Compiler::macroOpen',
'close' => 'Fenom\Compiler::macroClose' 'close' => 'Fenom\Compiler::macroClose'
), ),
'import' => array( 'import' => array(
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagImport' 'parser' => 'Fenom\Compiler::tagImport'
), ),
'cycle' => array( 'cycle' => array(
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagCycle' 'parser' => 'Fenom\Compiler::tagCycle'
) )
); );
/**
* @var ProviderInterface
*/
private $_provider;
/**
* @param Fenom\ProviderInterface $provider
*/
public function __construct(Fenom\ProviderInterface $provider)
{
$this->_provider = $provider;
}
/** /**
* Just factory * Just factory
* *
* @param string|Fenom\ProviderInterface $source path to templates or custom provider * @param string|Fenom\ProviderInterface $source path to templates or custom provider
* @param string $compile_dir path to compiled files * @param string $compile_dir path to compiled files
* @param int $options * @param int $options
* @throws InvalidArgumentException * @throws InvalidArgumentException
* @return Fenom * @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)) { {
if (is_string($source)) {
$provider = new Fenom\Provider($source); $provider = new Fenom\Provider($source);
} elseif($source instanceof ProviderInterface) { } elseif ($source instanceof ProviderInterface) {
$provider = $source; $provider = $source;
} else { } else {
throw new InvalidArgumentException("Source must be a valid path or provider object"); throw new InvalidArgumentException("Source must be a valid path or provider object");
@ -236,17 +238,37 @@ class Fenom {
$fenom = new static($provider); $fenom = new static($provider);
/* @var Fenom $fytro */ /* @var Fenom $fytro */
$fenom->setCompileDir($compile_dir); $fenom->setCompileDir($compile_dir);
if($options) { if ($options) {
$fenom->setOptions($options); $fenom->setOptions($options);
} }
return $fenom; return $fenom;
} }
/** /**
* @param Fenom\ProviderInterface $provider * Create bit-mask from associative array use fully associative array possible keys with bit values
* @static
* @param array $values custom assoc array, ["a" => true, "b" => false]
* @param array $options possible values, ["a" => 0b001, "b" => 0b010, "c" => 0b100]
* @param int $mask the initial value of the mask
* @return int result, ( $mask | a ) & ~b
* @throws \RuntimeException if key from custom assoc doesn't exists into possible values
*/ */
public function __construct(Fenom\ProviderInterface $provider) { private static function _makeMask(array $values, array $options, $mask = 0)
$this->_provider = $provider; {
foreach ($values as $value) {
if (isset($options[$value])) {
if ($options[$value]) {
$mask |= $options[$value];
} else {
$mask &= ~$options[$value];
}
} else {
throw new \RuntimeException("Undefined parameter $value");
}
}
return $mask;
} }
/** /**
@ -255,8 +277,10 @@ class Fenom {
* @param string $dir directory to store compiled templates in * @param string $dir directory to store compiled templates in
* @return Fenom * @return Fenom
*/ */
public function setCompileDir($dir) { public function setCompileDir($dir)
{
$this->_compile_dir = $dir; $this->_compile_dir = $dir;
return $this; return $this;
} }
@ -264,7 +288,8 @@ class Fenom {
* *
* @param callable $cb * @param callable $cb
*/ */
public function addPreCompileFilter($cb) { public function addPreCompileFilter($cb)
{
$this->_on_pre_cmp[] = $cb; $this->_on_pre_cmp[] = $cb;
} }
@ -272,14 +297,16 @@ class Fenom {
* *
* @param callable $cb * @param callable $cb
*/ */
public function addPostCompileFilter($cb) { public function addPostCompileFilter($cb)
{
$this->_on_post_cmp[] = $cb; $this->_on_post_cmp[] = $cb;
} }
/** /**
* @param callable $cb * @param callable $cb
*/ */
public function addCompileFilter($cb) { public function addCompileFilter($cb)
{
$this->_on_cmp[] = $cb; $this->_on_cmp[] = $cb;
} }
@ -290,141 +317,157 @@ class Fenom {
* @param string $callback the modifier callback * @param string $callback the modifier callback
* @return Fenom * @return Fenom
*/ */
public function addModifier($modifier, $callback) { public function addModifier($modifier, $callback)
{
$this->_modifiers[$modifier] = $callback; $this->_modifiers[$modifier] = $callback;
return $this; return $this;
} }
/** /**
* Add inline tag compiler * Add inline tag compiler
* *
* @param string $compiler * @param string $compiler
* @param callable $parser * @param callable $parser
* @return Fenom * @return Fenom
*/ */
public function addCompiler($compiler, $parser) { public function addCompiler($compiler, $parser)
{
$this->_actions[$compiler] = array( $this->_actions[$compiler] = array(
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => $parser 'parser' => $parser
); );
return $this; return $this;
} }
/** /**
* @param string $compiler * @param string $compiler
* @param string|object $storage * @param string|object $storage
* @return $this * @return $this
*/ */
public function addCompilerSmart($compiler, $storage) { public function addCompilerSmart($compiler, $storage)
if(method_exists($storage, "tag".$compiler)) { {
if (method_exists($storage, "tag" . $compiler)) {
$this->_actions[$compiler] = array( $this->_actions[$compiler] = array(
'type' => self::INLINE_COMPILER, 'type' => self::INLINE_COMPILER,
'parser' => array($storage, "tag".$compiler) 'parser' => array($storage, "tag" . $compiler)
); );
} }
return $this; return $this;
} }
/** /**
* Add block compiler * Add block compiler
* *
* @param string $compiler * @param string $compiler
* @param callable $open_parser * @param callable $open_parser
* @param callable|string $close_parser * @param callable|string $close_parser
* @param array $tags * @param array $tags
* @return Fenom * @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( $this->_actions[$compiler] = array(
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
'open' => $open_parser, 'open' => $open_parser,
'close' => $close_parser ?: self::DEFAULT_CLOSE_COMPILER, 'close' => $close_parser ? : self::DEFAULT_CLOSE_COMPILER,
'tags' => $tags, 'tags' => $tags,
); );
return $this; return $this;
} }
/** /**
* @param $compiler * @param $compiler
* @param $storage * @param $storage
* @param array $tags * @param array $tags
* @param array $floats * @param array $floats
* @throws LogicException * @throws LogicException
* @return Fenom * @return Fenom
*/ */
public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array()) { public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array())
{
$c = array( $c = array(
'type' => self::BLOCK_COMPILER, 'type' => self::BLOCK_COMPILER,
"tags" => array(), "tags" => array(),
"float_tags" => array() "float_tags" => array()
); );
if(method_exists($storage, $compiler."Open")) { if (method_exists($storage, $compiler . "Open")) {
$c["open"] = $compiler."Open"; $c["open"] = $compiler . "Open";
} else { } else {
throw new \LogicException("Open compiler {$compiler}Open not found"); throw new \LogicException("Open compiler {$compiler}Open not found");
} }
if(method_exists($storage, $compiler."Close")) { if (method_exists($storage, $compiler . "Close")) {
$c["close"] = $compiler."Close"; $c["close"] = $compiler . "Close";
} else { } else {
throw new \LogicException("Close compiler {$compiler}Close not found"); throw new \LogicException("Close compiler {$compiler}Close not found");
} }
foreach($tags as $tag) { foreach ($tags as $tag) {
if(method_exists($storage, "tag".$tag)) { if (method_exists($storage, "tag" . $tag)) {
$c["tags"][ $tag ] = "tag".$tag; $c["tags"][$tag] = "tag" . $tag;
if($floats && in_array($tag, $floats)) { if ($floats && in_array($tag, $floats)) {
$c['float_tags'][ $tag ] = 1; $c['float_tags'][$tag] = 1;
} }
} else { } else {
throw new \LogicException("Tag compiler $tag (tag{$compiler}) not found"); throw new \LogicException("Tag compiler $tag (tag{$compiler}) not found");
} }
} }
$this->_actions[$compiler] = $c; $this->_actions[$compiler] = $c;
return $this; return $this;
} }
/** /**
* @param string $function * @param string $function
* @param callable $callback * @param callable $callback
* @param callable|string $parser * @param callable|string $parser
* @return Fenom * @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( $this->_actions[$function] = array(
'type' => self::INLINE_FUNCTION, 'type' => self::INLINE_FUNCTION,
'parser' => $parser, 'parser' => $parser,
'function' => $callback, 'function' => $callback,
); );
return $this; return $this;
} }
/** /**
* @param string $function * @param string $function
* @param callable $callback * @param callable $callback
* @return Fenom * @return Fenom
*/ */
public function addFunctionSmart($function, $callback) { public function addFunctionSmart($function, $callback)
{
$this->_actions[$function] = array( $this->_actions[$function] = array(
'type' => self::INLINE_FUNCTION, 'type' => self::INLINE_FUNCTION,
'parser' => self::SMART_FUNC_PARSER, 'parser' => self::SMART_FUNC_PARSER,
'function' => $callback, 'function' => $callback,
); );
return $this; return $this;
} }
/** /**
* @param string $function * @param string $function
* @param callable $callback * @param callable $callback
* @param callable|string $parser_open * @param callable|string $parser_open
* @param callable|string $parser_close * @param callable|string $parser_close
* @return Fenom * @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( $this->_actions[$function] = array(
'type' => self::BLOCK_FUNCTION, 'type' => self::BLOCK_FUNCTION,
'open' => $parser_open, 'open' => $parser_open,
'close' => $parser_close, 'close' => $parser_close,
'function' => $callback, 'function' => $callback,
); );
return $this; return $this;
} }
@ -432,8 +475,10 @@ class Fenom {
* @param array $funcs * @param array $funcs
* @return Fenom * @return Fenom
*/ */
public function addAllowedFunctions(array $funcs) { public function addAllowedFunctions(array $funcs)
{
$this->_allowed_funcs = $this->_allowed_funcs + array_flip($funcs); $this->_allowed_funcs = $this->_allowed_funcs + array_flip($funcs);
return $this; return $this;
} }
@ -444,10 +489,11 @@ class Fenom {
* @return mixed * @return mixed
* @throws \Exception * @throws \Exception
*/ */
public function getModifier($modifier) { public function getModifier($modifier)
if(isset($this->_modifiers[$modifier])) { {
if (isset($this->_modifiers[$modifier])) {
return $this->_modifiers[$modifier]; return $this->_modifiers[$modifier];
} elseif($this->isAllowedFunction($modifier)) { } elseif ($this->isAllowedFunction($modifier)) {
return $modifier; return $modifier;
} else { } else {
throw new \Exception("Modifier $modifier not found"); throw new \Exception("Modifier $modifier not found");
@ -460,8 +506,9 @@ class Fenom {
* @param string $function * @param string $function
* @return string|bool * @return string|bool
*/ */
public function getFunction($function) { public function getFunction($function)
if(isset($this->_actions[$function])) { {
if (isset($this->_actions[$function])) {
return $this->_actions[$function]; return $this->_actions[$function];
} else { } else {
return false; return false;
@ -472,8 +519,9 @@ class Fenom {
* @param string $function * @param string $function
* @return bool * @return bool
*/ */
public function isAllowedFunction($function) { public function isAllowedFunction($function)
if($this->_options & self::DENY_INLINE_FUNCS) { {
if ($this->_options & self::DENY_INLINE_FUNCS) {
return isset($this->_allowed_funcs[$function]); return isset($this->_allowed_funcs[$function]);
} else { } else {
return is_callable($function); return is_callable($function);
@ -484,26 +532,38 @@ class Fenom {
* @param string $tag * @param string $tag
* @return array * @return array
*/ */
public function getTagOwners($tag) { public function getTagOwners($tag)
{
$tags = array(); $tags = array();
foreach($this->_actions as $owner => $params) { foreach ($this->_actions as $owner => $params) {
if(isset($params["tags"][$tag])) { if (isset($params["tags"][$tag])) {
$tags[] = $owner; $tags[] = $owner;
} }
} }
return $tags; return $tags;
} }
/** /**
* Add source template provider by scheme * Add source template provider by scheme
* *
* @param string $scm scheme name * @param string $scm scheme name
* @param Fenom\ProviderInterface $provider provider object * @param Fenom\ProviderInterface $provider provider object
*/ */
public function addProvider($scm, \Fenom\ProviderInterface $provider) { public function addProvider($scm, \Fenom\ProviderInterface $provider)
{
$this->_providers[$scm] = $provider; $this->_providers[$scm] = $provider;
} }
/**
* Get options as bits
* @return int
*/
public function getOptions()
{
return $this->_options;
}
/** /**
* Set options. May be bitwise mask of constants DENY_METHODS, DENY_INLINE_FUNCS, DENY_SET_VARS, INCLUDE_SOURCES, * 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: * FORCE_COMPILE, CHECK_MTIME, or associative array with boolean values:
@ -513,30 +573,24 @@ class Fenom {
* compile_check - check template modifications (slow!) * compile_check - check template modifications (slow!)
* @param int|array $options * @param int|array $options
*/ */
public function setOptions($options) { public function setOptions($options)
if(is_array($options)) { {
if (is_array($options)) {
$options = self::_makeMask($options, self::$_option_list); $options = self::_makeMask($options, self::$_option_list);
} }
$this->_storage = array(); $this->_storage = array();
$this->_options = $options; $this->_options = $options;
} }
/**
* Get options as bits
* @return int
*/
public function getOptions() {
return $this->_options;
}
/** /**
* @param bool|string $scm * @param bool|string $scm
* @return Fenom\ProviderInterface * @return Fenom\ProviderInterface
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function getProvider($scm = false) { public function getProvider($scm = false)
if($scm) { {
if(isset($this->_providers[$scm])) { if ($scm) {
if (isset($this->_providers[$scm])) {
return $this->_providers[$scm]; return $this->_providers[$scm];
} else { } else {
throw new InvalidArgumentException("Provider for '$scm' not found"); throw new InvalidArgumentException("Provider for '$scm' not found");
@ -551,7 +605,8 @@ class Fenom {
* *
* @return Fenom\Template * @return Fenom\Template
*/ */
public function getRawTemplate() { public function getRawTemplate()
{
return new \Fenom\Template($this, $this->_options); return new \Fenom\Template($this, $this->_options);
} }
@ -559,20 +614,22 @@ class Fenom {
* Execute template and write result into stdout * Execute template and write result into stdout
* *
* @param string $template name of template * @param string $template name of template
* @param array $vars array of data for template * @param array $vars array of data for template
* @return Fenom\Render * @return Fenom\Render
*/ */
public function display($template, array $vars = array()) { public function display($template, array $vars = array())
{
return $this->getTemplate($template)->display($vars); return $this->getTemplate($template)->display($vars);
} }
/** /**
* *
* @param string $template name of template * @param string $template name of template
* @param array $vars array of data for template * @param array $vars array of data for template
* @return mixed * @return mixed
*/ */
public function fetch($template, array $vars = array()) { public function fetch($template, array $vars = array())
{
return $this->getTemplate($template)->fetch($vars); return $this->getTemplate($template)->fetch($vars);
} }
@ -580,13 +637,14 @@ class Fenom {
* *
* *
* @param string $template name of template * @param string $template name of template
* @param array $vars * @param array $vars
* @param $callback * @param $callback
* @param float $chunk * @param float $chunk
* @return \Fenom\Render * @return \Fenom\Render
* @example $fenom->pipe("products.yml.tpl", $iterators, [new SplFileObject("/tmp/products.yml"), "fwrite"], 512*1024) * @example $fenom->pipe("products.yml.tpl", $iterators, [new SplFileObject("/tmp/products.yml"), "fwrite"], 512*1024)
*/ */
public function pipe($template, array $vars, $callback, $chunk = 1e6) { public function pipe($template, array $vars, $callback, $chunk = 1e6)
{
ob_start($callback, $chunk, true); ob_start($callback, $chunk, true);
$this->getTemplate($template)->display($vars); $this->getTemplate($template)->display($vars);
ob_end_flush(); ob_end_flush();
@ -597,23 +655,24 @@ class Fenom {
* Get template by name * Get template by name
* *
* @param string $template template name with schema * @param string $template template name with schema
* @param int $options additional options and flags * @param int $options additional options and flags
* @return Fenom\Template * @return Fenom\Template
*/ */
public function getTemplate($template, $options = 0) { public function getTemplate($template, $options = 0)
$key = dechex($this->_options | $options)."@".$template; {
if(isset($this->_storage[ $key ])) { $key = dechex($this->_options | $options) . "@" . $template;
/** @var Fenom\Template $tpl */ if (isset($this->_storage[$key])) {
$tpl = $this->_storage[ $key ]; /** @var Fenom\Template $tpl */
if(($this->_options & self::AUTO_RELOAD) && !$tpl->isValid()) { $tpl = $this->_storage[$key];
return $this->_storage[ $key ] = $this->compile($template, true, $options); if (($this->_options & self::AUTO_RELOAD) && !$tpl->isValid()) {
return $this->_storage[$key] = $this->compile($template, true, $options);
} else { } else {
return $tpl; return $tpl;
} }
} elseif($this->_options & self::FORCE_COMPILE) { } elseif ($this->_options & self::FORCE_COMPILE) {
return $this->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options); return $this->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options);
} else { } else {
return $this->_storage[ $key ] = $this->_load($template, $options); return $this->_storage[$key] = $this->_load($template, $options);
} }
} }
@ -622,79 +681,55 @@ class Fenom {
* *
* @param Fenom\Render $template * @param Fenom\Render $template
*/ */
public function addTemplate(Fenom\Render $template) { public function addTemplate(Fenom\Render $template)
$this->_storage[dechex($template->getOptions()).'@'. $template->getName() ] = $template; {
} $this->_storage[dechex($template->getOptions()) . '@' . $template->getName()] = $template;
/**
* Load template from cache or create cache if it doesn't exists.
*
* @param string $tpl
* @param int $opts
* @return Fenom\Render
*/
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);
} else {
$fenom = $this;
return include($this->_compile_dir."/".$file_name);
}
}
/**
* Generate unique name of compiled template
*
* @param string $tpl
* @param int $options
* @return string
*/
private function _getCacheName($tpl, $options) {
$hash = $tpl.":".$options;
return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
} }
/** /**
* Compile and save template * Compile and save template
* *
* @param string $tpl * @param string $tpl
* @param bool $store store template on disk * @param bool $store store template on disk
* @param int $options * @param int $options
* @throws RuntimeException * @throws RuntimeException
* @return \Fenom\Template * @return \Fenom\Template
*/ */
public function compile($tpl, $store = true, $options = 0) { public function compile($tpl, $store = true, $options = 0)
$options = $this->_options | $options; {
$options = $this->_options | $options;
$template = Template::factory($this, $options)->load($tpl); $template = Template::factory($this, $options)->load($tpl);
if($store) { if ($store) {
$cache = $this->_getCacheName($tpl, $options); $cache = $this->_getCacheName($tpl, $options);
$tpl_tmp = tempnam($this->_compile_dir, $cache); $tpl_tmp = tempnam($this->_compile_dir, $cache);
$tpl_fp = fopen($tpl_tmp, "w"); $tpl_fp = fopen($tpl_tmp, "w");
if(!$tpl_fp) { if (!$tpl_fp) {
throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory ".$this->_compile_dir." is writable?"); throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory " . $this->_compile_dir . " is writable?");
} }
fwrite($tpl_fp, $template->getTemplateCode()); fwrite($tpl_fp, $template->getTemplateCode());
fclose($tpl_fp); fclose($tpl_fp);
$file_name = $this->_compile_dir."/".$cache; $file_name = $this->_compile_dir . "/" . $cache;
if(!rename($tpl_tmp, $file_name)) { if (!rename($tpl_tmp, $file_name)) {
throw new \RuntimeException("Can't to move $tpl_tmp to $tpl"); throw new \RuntimeException("Can't to move $tpl_tmp to $tpl");
} }
} }
return $template; return $template;
} }
/** /**
* Flush internal memory template cache * Flush internal memory template cache
*/ */
public function flush() { public function flush()
{
$this->_storage = array(); $this->_storage = array();
} }
/** /**
* Remove all compiled templates * Remove all compiled templates
*/ */
public function clearAllCompiles() { public function clearAllCompiles()
{
\Fenom\Provider::clean($this->_compile_dir); \Fenom\Provider::clean($this->_compile_dir);
} }
@ -705,32 +740,41 @@ class Fenom {
* @param string $name * @param string $name
* @return Fenom\Template * @return Fenom\Template
*/ */
public function compileCode($code, $name = 'Runtime compile') { public function compileCode($code, $name = 'Runtime compile')
{
return Template::factory($this, $this->_options)->source($name, $code); return Template::factory($this, $this->_options)->source($name, $code);
} }
/**
* Load template from cache or create cache if it doesn't exists.
*
* @param string $tpl
* @param int $opts
* @return Fenom\Render
*/
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);
} else {
$fenom = $this;
return include($this->_compile_dir . "/" . $file_name);
}
}
/** /**
* Create bit-mask from associative array use fully associative array possible keys with bit values * Generate unique name of compiled template
* @static *
* @param array $values custom assoc array, ["a" => true, "b" => false] * @param string $tpl
* @param array $options possible values, ["a" => 0b001, "b" => 0b010, "c" => 0b100] * @param int $options
* @param int $mask the initial value of the mask * @return string
* @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 function _getCacheName($tpl, $options)
foreach($values as $value) { {
if(isset($options[$value])) { $hash = $tpl . ":" . $options;
if($options[$value]) {
$mask |= $options[$value]; return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
} else {
$mask &= ~$options[$value];
}
} else {
throw new \RuntimeException("Undefined parameter $value");
}
}
return $mask;
} }
} }