fenom/src/Fenom.php

863 lines
24 KiB
PHP
Raw Normal View History

2013-01-25 18:36:16 +04:00
<?php
2013-04-28 11:33:36 +04:00
/*
2013-06-28 11:53:53 +04:00
* This file is part of Fenom.
2013-04-28 11:33:36 +04:00
*
* (c) 2013 Ivan Shalganov
*
2013-04-28 18:08:57 +04:00
* For the full copyright and license information, please view the license.md
2013-04-28 11:33:36 +04:00
* file that was distributed with this source code.
*/
2013-08-01 01:05:19 +04:00
use Fenom\ProviderInterface;
use Fenom\Template;
2013-01-25 18:36:16 +04:00
2013-01-25 19:18:09 +04:00
/**
2013-06-28 11:53:53 +04:00
* Fenom Template Engine
2013-07-04 01:28:10 +04:00
*
* @author Ivan Shalganov <a.cobest@gmail.com>
2013-01-25 19:18:09 +04:00
*/
2013-07-29 14:58:14 +04:00
class Fenom
{
2013-07-24 19:37:07 +04:00
const VERSION = '1.2';
2013-01-25 18:36:16 +04:00
2013-07-20 21:25:32 +04:00
/* Actions */
2013-07-29 14:58:14 +04:00
const INLINE_COMPILER = 1;
const BLOCK_COMPILER = 2;
const INLINE_FUNCTION = 3;
const BLOCK_FUNCTION = 4;
const MODIFIER = 5;
2013-01-25 18:36:16 +04:00
2013-07-03 12:10:50 +04:00
/* Options */
2013-07-29 14:58:14 +04:00
const DENY_METHODS = 0x10;
const DENY_NATIVE_FUNCS = 0x20;
const FORCE_INCLUDE = 0x40;
const AUTO_RELOAD = 0x80;
const FORCE_COMPILE = 0x100;
const AUTO_ESCAPE = 0x200;
const DISABLE_CACHE = 0x400;
const FORCE_VERIFY = 0x800; // reserved
const AUTO_TRIM = 0x1000; // reserved
const DENY_STATICS = 0x2000; // reserved
2013-01-25 18:36:16 +04:00
2013-07-24 19:37:07 +04:00
/* @deprecated */
2013-07-29 14:58:14 +04:00
const DENY_INLINE_FUNCS = 0x20;
2013-07-24 19:37:07 +04:00
2013-07-03 12:10:50 +04:00
/* Default parsers */
2013-06-28 11:53:53 +04:00
const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose';
2013-07-29 14:58:14 +04:00
const DEFAULT_FUNC_PARSER = 'Fenom\Compiler::stdFuncParser';
const DEFAULT_FUNC_OPEN = 'Fenom\Compiler::stdFuncOpen';
const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose';
const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser';
2013-01-25 18:36:16 +04:00
2013-07-29 14:58:14 +04:00
const MAX_MACRO_RECURSIVE = 32;
2013-07-29 14:53:21 +04:00
2013-01-25 18:36:16 +04:00
/**
2013-05-19 02:04:52 +04:00
* @var int[] of possible options, as associative array
* @see setOptions
2013-01-25 18:36:16 +04:00
*/
2013-07-23 11:32:31 +04:00
private static $_options_list = array(
2013-07-29 14:58:14 +04:00
"disable_methods" => self::DENY_METHODS,
2013-07-24 19:37:07 +04:00
"disable_native_funcs" => self::DENY_NATIVE_FUNCS,
2013-07-29 14:58:14 +04:00
"disable_cache" => self::DISABLE_CACHE,
"force_compile" => self::FORCE_COMPILE,
"auto_reload" => self::AUTO_RELOAD,
"force_include" => self::FORCE_INCLUDE,
"auto_escape" => self::AUTO_ESCAPE,
"force_verify" => self::FORCE_VERIFY,
"auto_trim" => self::AUTO_TRIM,
"disable_statics" => self::DENY_STATICS,
2013-01-25 18:36:16 +04:00
);
2013-08-01 01:05:19 +04:00
/**
* @var callable[]
*/
public $pre_filters = array();
/**
* @var callable[]
*/
public $filters = array();
/**
* @var callable[]
*/
public $post_filters = array();
2013-01-28 16:34:34 +04:00
/**
2013-06-28 11:53:53 +04:00
* @var Fenom\Render[] Templates storage
2013-01-28 16:34:34 +04:00
*/
protected $_storage = array();
2013-08-01 01:05:19 +04:00
2013-01-25 18:36:16 +04:00
/**
* @var string compile directory
*/
protected $_compile_dir = "/tmp";
/**
* @var int masked options
*/
protected $_options = 0;
/**
* @var ProviderInterface
*/
private $_provider;
/**
2013-06-28 11:53:53 +04:00
* @var Fenom\ProviderInterface[]
*/
protected $_providers = array();
2013-01-25 18:36:16 +04:00
/**
2013-05-19 02:04:52 +04:00
* @var string[] list of modifiers [modifier_name => callable]
2013-01-25 18:36:16 +04:00
*/
protected $_modifiers = array(
2013-08-02 21:50:04 +04:00
"upper" => 'strtoupper',
"up" => 'strtoupper',
"lower" => 'strtolower',
"low" => 'strtolower',
2013-06-28 11:53:53 +04:00
"date_format" => 'Fenom\Modifier::dateFormat',
2013-08-02 21:50:04 +04:00
"date" => 'Fenom\Modifier::date',
"truncate" => 'Fenom\Modifier::truncate',
"escape" => 'Fenom\Modifier::escape',
"e" => 'Fenom\Modifier::escape', // alias of escape
"unescape" => 'Fenom\Modifier::unescape',
"strip" => 'Fenom\Modifier::strip',
"length" => 'Fenom\Modifier::length',
"iterable" => 'Fenom\Modifier::isIterable'
2013-01-25 18:36:16 +04:00
);
/**
* @var array of allowed PHP functions
2013-01-25 18:36:16 +04:00
*/
protected $_allowed_funcs = array(
2013-08-02 21:50:04 +04:00
"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,
"ip2long" => 1, "long2ip" => 1, "strip_tags" => 1, "nl2br" => 1, "explode" => 1, "implode" => 1
2013-01-25 18:36:16 +04:00
);
/**
2013-05-19 02:04:52 +04:00
* @var array[] of compilers and functions
2013-01-25 18:36:16 +04:00
*/
protected $_actions = array(
'foreach' => array( // {foreach ...} {break} {continue} {foreachelse} {/foreach}
2013-01-25 18:36:16 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::foreachOpen',
'close' => 'Fenom\Compiler::foreachClose',
2013-01-25 18:36:16 +04:00
'tags' => array(
2013-06-28 11:53:53 +04:00
'foreachelse' => 'Fenom\Compiler::foreachElse',
'break' => 'Fenom\Compiler::tagBreak',
'continue' => 'Fenom\Compiler::tagContinue',
2013-01-25 18:36:16 +04:00
),
'float_tags' => array('break' => 1, 'continue' => 1)
),
2013-07-29 14:58:14 +04:00
'if' => array( // {if ...} {elseif ...} {else} {/if}
2013-01-25 18:36:16 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::ifOpen',
'close' => 'Fenom\Compiler::stdClose',
2013-01-25 18:36:16 +04:00
'tags' => array(
2013-06-28 11:53:53 +04:00
'elseif' => 'Fenom\Compiler::tagElseIf',
'else' => 'Fenom\Compiler::tagElse',
2013-01-25 18:36:16 +04:00
)
),
2013-07-29 14:58:14 +04:00
'switch' => array( // {switch ...} {case ...} {break} {default} {/switch}
2013-01-25 18:36:16 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::switchOpen',
'close' => 'Fenom\Compiler::stdClose',
2013-01-25 18:36:16 +04:00
'tags' => array(
2013-06-28 11:53:53 +04:00
'case' => 'Fenom\Compiler::tagCase',
'default' => 'Fenom\Compiler::tagDefault',
'break' => 'Fenom\Compiler::tagBreak',
2013-01-25 18:36:16 +04:00
),
'float_tags' => array('break' => 1)
),
2013-07-29 14:58:14 +04:00
'for' => array( // {for ...} {break} {continue} {/for}
2013-01-25 18:36:16 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::forOpen',
'close' => 'Fenom\Compiler::forClose',
2013-01-25 18:36:16 +04:00
'tags' => array(
2013-06-28 11:53:53 +04:00
'forelse' => 'Fenom\Compiler::forElse',
'break' => 'Fenom\Compiler::tagBreak',
'continue' => 'Fenom\Compiler::tagContinue',
2013-01-25 18:36:16 +04:00
),
'float_tags' => array('break' => 1, 'continue' => 1)
),
2013-07-29 14:58:14 +04:00
'while' => array( // {while ...} {break} {continue} {/while}
2013-01-25 18:36:16 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::whileOpen',
'close' => 'Fenom\Compiler::stdClose',
2013-01-25 18:36:16 +04:00
'tags' => array(
2013-06-28 11:53:53 +04:00
'break' => 'Fenom\Compiler::tagBreak',
'continue' => 'Fenom\Compiler::tagContinue',
2013-01-25 18:36:16 +04:00
),
'float_tags' => array('break' => 1, 'continue' => 1)
),
'include' => array( // {include ...}
2013-01-25 18:36:16 +04:00
'type' => self::INLINE_COMPILER,
2013-06-28 11:53:53 +04:00
'parser' => 'Fenom\Compiler::tagInclude'
2013-01-25 18:36:16 +04:00
),
2013-07-29 14:58:14 +04:00
'var' => array( // {var ...}
2013-03-17 14:37:23 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::varOpen',
'close' => 'Fenom\Compiler::varClose'
2013-01-25 18:36:16 +04:00
),
2013-07-29 14:58:14 +04:00
'block' => array( // {block ...} {parent} {/block}
2013-01-25 18:36:16 +04:00
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::tagBlockOpen',
'close' => 'Fenom\Compiler::tagBlockClose',
2013-02-20 18:09:24 +04:00
'tags' => array(
2013-06-28 11:53:53 +04:00
'parent' => 'Fenom\Compiler::tagParent'
2013-02-20 18:09:24 +04:00
),
'float_tags' => array('parent' => 1)
2013-01-25 18:36:16 +04:00
),
'extends' => array( // {extends ...}
2013-01-25 18:36:16 +04:00
'type' => self::INLINE_COMPILER,
2013-06-28 11:53:53 +04:00
'parser' => 'Fenom\Compiler::tagExtends'
2013-01-25 18:36:16 +04:00
),
2013-02-20 18:09:24 +04:00
'use' => array( // {use}
'type' => self::INLINE_COMPILER,
2013-06-28 11:53:53 +04:00
'parser' => 'Fenom\Compiler::tagUse'
2013-02-20 18:09:24 +04:00
),
'filter' => array( // {filter} ... {/filter}
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::filterOpen',
'close' => 'Fenom\Compiler::filterClose'
2013-02-22 00:05:20 +04:00
),
'macro' => array(
'type' => self::BLOCK_COMPILER,
2013-06-28 11:53:53 +04:00
'open' => 'Fenom\Compiler::macroOpen',
'close' => 'Fenom\Compiler::macroClose'
2013-02-22 00:05:20 +04:00
),
'import' => array(
'type' => self::INLINE_COMPILER,
2013-06-28 11:53:53 +04:00
'parser' => 'Fenom\Compiler::tagImport'
),
2013-07-03 12:10:50 +04:00
'cycle' => array(
'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagCycle'
),
2013-07-29 14:58:14 +04:00
'raw' => array(
'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagRaw'
),
'autoescape' => array(
'type' => self::BLOCK_COMPILER,
'open' => 'Fenom\Compiler::autoescapeOpen',
'close' => 'Fenom\Compiler::autoescapeClose'
2013-07-24 16:16:20 +04:00
),
'unset' => array(
'type' => self::INLINE_COMPILER,
'parser' => 'Fenom\Compiler::tagUnset'
2013-07-03 12:10:50 +04:00
)
2013-01-25 18:36:16 +04:00
);
2013-02-13 20:51:27 +04:00
/**
* Just factory
*
2013-06-28 11:53:53 +04:00
* @param string|Fenom\ProviderInterface $source path to templates or custom provider
2013-02-13 20:51:27 +04:00
* @param string $compile_dir path to compiled files
* @param int $options
* @throws InvalidArgumentException
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-02-13 20:51:27 +04:00
*/
2013-07-29 14:58:14 +04:00
public static function factory($source, $compile_dir = '/tmp', $options = 0)
{
if (is_string($source)) {
2013-06-28 11:53:53 +04:00
$provider = new Fenom\Provider($source);
2013-07-29 14:58:14 +04:00
} elseif ($source instanceof ProviderInterface) {
2013-02-13 20:51:27 +04:00
$provider = $source;
} else {
throw new InvalidArgumentException("Source must be a valid path or provider object");
}
2013-06-28 11:53:53 +04:00
$fenom = new static($provider);
/* @var Fenom $fenom */
2013-06-28 11:53:53 +04:00
$fenom->setCompileDir($compile_dir);
2013-07-29 14:58:14 +04:00
if ($options) {
2013-06-28 11:53:53 +04:00
$fenom->setOptions($options);
2013-01-25 18:36:16 +04:00
}
2013-06-28 11:53:53 +04:00
return $fenom;
2013-01-25 18:36:16 +04:00
}
/**
2013-06-28 11:53:53 +04:00
* @param Fenom\ProviderInterface $provider
*/
2013-07-29 14:58:14 +04:00
public function __construct(Fenom\ProviderInterface $provider)
{
$this->_provider = $provider;
}
/**
* Set compile directory
2013-05-19 02:04:52 +04:00
*
* @param string $dir directory to store compiled templates in
2013-06-28 11:53:53 +04:00
* @return Fenom
*/
2013-07-29 14:58:14 +04:00
public function setCompileDir($dir)
{
2013-01-25 18:36:16 +04:00
$this->_compile_dir = $dir;
return $this;
}
/**
*
* @param callable $cb
2013-08-01 01:05:19 +04:00
* @return self
*/
2013-08-01 01:05:19 +04:00
public function addPreFilter($cb)
2013-07-29 14:58:14 +04:00
{
2013-08-01 01:05:19 +04:00
$this->pre_filters[] = $cb;
return $this;
}
2013-08-02 21:50:04 +04:00
public function getPreFilters()
{
2013-08-01 01:05:19 +04:00
return $this->pre_filters;
}
/**
*
* @param callable $cb
2013-08-01 01:05:19 +04:00
* @return self
*/
2013-08-01 01:05:19 +04:00
public function addPostFilter($cb)
2013-07-29 14:58:14 +04:00
{
2013-08-01 01:05:19 +04:00
$this->post_filters[] = $cb;
return $this;
}
2013-08-02 21:50:04 +04:00
public function getPostFilters()
{
2013-08-01 01:05:19 +04:00
return $this->post_filters;
2013-01-25 18:36:16 +04:00
}
/**
* @param callable $cb
2013-08-01 01:05:19 +04:00
* @return self
*/
2013-08-01 01:05:19 +04:00
public function addFilter($cb)
2013-07-29 14:58:14 +04:00
{
2013-08-01 01:05:19 +04:00
$this->filters[] = $cb;
return $this;
}
2013-08-02 21:50:04 +04:00
public function getFilters()
{
2013-08-01 01:05:19 +04:00
return $this->filters;
}
2013-01-25 18:36:16 +04:00
/**
* Add modifier
*
2013-05-19 02:04:52 +04:00
* @param string $modifier the modifier name
* @param string $callback the modifier callback
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addModifier($modifier, $callback)
{
2013-01-25 18:36:16 +04:00
$this->_modifiers[$modifier] = $callback;
return $this;
}
/**
2013-02-20 18:09:24 +04:00
* Add inline tag compiler
*
* @param string $compiler
2013-02-20 18:09:24 +04:00
* @param callable $parser
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addCompiler($compiler, $parser)
{
2013-01-25 18:36:16 +04:00
$this->_actions[$compiler] = array(
'type' => self::INLINE_COMPILER,
'parser' => $parser
);
return $this;
}
/**
* @param string $compiler
* @param string|object $storage
* @return $this
*/
2013-07-29 14:58:14 +04:00
public function addCompilerSmart($compiler, $storage)
{
if (method_exists($storage, "tag" . $compiler)) {
$this->_actions[$compiler] = array(
'type' => self::INLINE_COMPILER,
2013-07-29 14:58:14 +04:00
'parser' => array($storage, "tag" . $compiler)
);
}
2013-03-04 12:40:32 +04:00
return $this;
}
2013-01-25 18:36:16 +04:00
/**
2013-02-20 18:09:24 +04:00
* Add block compiler
*
* @param string $compiler
2013-02-20 18:09:24 +04:00
* @param callable $open_parser
* @param callable|string $close_parser
2013-01-25 18:36:16 +04:00
* @param array $tags
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addBlockCompiler($compiler, $open_parser, $close_parser = self::DEFAULT_CLOSE_COMPILER, array $tags = array())
{
2013-01-25 18:36:16 +04:00
$this->_actions[$compiler] = array(
'type' => self::BLOCK_COMPILER,
'open' => $open_parser,
2013-07-29 14:58:14 +04:00
'close' => $close_parser ? : self::DEFAULT_CLOSE_COMPILER,
2013-01-25 18:36:16 +04:00
'tags' => $tags,
);
return $this;
}
2013-03-04 12:40:32 +04:00
/**
* @param $compiler
* @param $storage
2013-03-04 12:40:32 +04:00
* @param array $tags
* @param array $floats
* @throws LogicException
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-03-04 12:40:32 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array())
{
$c = array(
'type' => self::BLOCK_COMPILER,
"tags" => array(),
"float_tags" => array()
);
2013-07-29 14:58:14 +04:00
if (method_exists($storage, $compiler . "Open")) {
$c["open"] = $compiler . "Open";
} else {
throw new \LogicException("Open compiler {$compiler}Open not found");
}
2013-07-29 14:58:14 +04:00
if (method_exists($storage, $compiler . "Close")) {
$c["close"] = $compiler . "Close";
} else {
throw new \LogicException("Close compiler {$compiler}Close not found");
}
2013-07-29 14:58:14 +04:00
foreach ($tags as $tag) {
if (method_exists($storage, "tag" . $tag)) {
$c["tags"][$tag] = "tag" . $tag;
if ($floats && in_array($tag, $floats)) {
$c['float_tags'][$tag] = 1;
}
} else {
throw new \LogicException("Tag compiler $tag (tag{$compiler}) not found");
}
}
$this->_actions[$compiler] = $c;
2013-03-04 12:40:32 +04:00
return $this;
}
2013-01-25 18:36:16 +04:00
/**
* @param string $function
* @param callable $callback
2013-02-26 23:56:06 +04:00
* @param callable|string $parser
2013-06-28 11:53:53 +04:00
* @return Fenom
*/
2013-07-29 14:58:14 +04:00
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER)
{
$this->_actions[$function] = array(
'type' => self::INLINE_FUNCTION,
'parser' => $parser,
'function' => $callback,
);
return $this;
}
/**
* @param string $function
* @param callable $callback
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addFunctionSmart($function, $callback)
{
2013-01-25 18:36:16 +04:00
$this->_actions[$function] = array(
'type' => self::INLINE_FUNCTION,
'parser' => self::SMART_FUNC_PARSER,
2013-01-25 18:36:16 +04:00
'function' => $callback,
);
return $this;
}
/**
2013-02-20 18:09:24 +04:00
* @param string $function
* @param callable $callback
* @param callable|string $parser_open
* @param callable|string $parser_close
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE)
{
2013-01-25 18:36:16 +04:00
$this->_actions[$function] = array(
2013-07-29 14:58:14 +04:00
'type' => self::BLOCK_FUNCTION,
'open' => $parser_open,
'close' => $parser_close,
'function' => $callback,
2013-01-25 18:36:16 +04:00
);
return $this;
}
/**
* @param array $funcs
2013-06-28 11:53:53 +04:00
* @return Fenom
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addAllowedFunctions(array $funcs)
{
2013-01-25 18:36:16 +04:00
$this->_allowed_funcs = $this->_allowed_funcs + array_flip($funcs);
return $this;
}
/**
2013-02-20 18:09:24 +04:00
* Return modifier function
*
2013-07-24 19:37:07 +04:00
* @param string $modifier
* @param Fenom\Template $template
2013-01-25 18:36:16 +04:00
* @return mixed
*/
2013-07-29 14:58:14 +04:00
public function getModifier($modifier, Template $template = null)
{
if (isset($this->_modifiers[$modifier])) {
2013-01-25 18:36:16 +04:00
return $this->_modifiers[$modifier];
2013-07-29 14:58:14 +04:00
} elseif ($this->isAllowedFunction($modifier)) {
2013-01-25 18:36:16 +04:00
return $modifier;
} else {
2013-07-24 19:37:07 +04:00
return $this->_loadModifier($modifier, $template);
2013-01-25 18:36:16 +04:00
}
}
/**
2013-07-24 19:37:07 +04:00
* @param string $modifier
* @param Fenom\Template $template
* @return bool
*/
2013-07-29 14:58:14 +04:00
protected function _loadModifier($modifier, $template)
{
2013-07-24 19:37:07 +04:00
return false;
}
/**
2013-01-25 18:36:16 +04:00
* @param string $function
2013-07-24 19:37:07 +04:00
* @param Fenom\Template $template
* @return bool|string
* @deprecated
*/
2013-07-29 14:58:14 +04:00
public function getFunction($function, Template $template = null)
{
2013-07-24 19:37:07 +04:00
return $this->getTag($function, $template);
}
/**
* Returns tag info
*
* @param string $tag
* @param Fenom\Template $template
2013-01-25 18:36:16 +04:00
* @return string|bool
*/
2013-07-29 14:58:14 +04:00
public function getTag($tag, Template $template = null)
{
if (isset($this->_actions[$tag])) {
2013-07-24 19:37:07 +04:00
return $this->_actions[$tag];
2013-01-25 18:36:16 +04:00
} else {
2013-07-24 19:37:07 +04:00
return $this->_loadTag($tag, $template);
2013-01-25 18:36:16 +04:00
}
}
2013-07-24 19:37:07 +04:00
/**
* @param $tag
* @param Fenom\Template $template
* @return bool
*/
2013-07-29 14:58:14 +04:00
protected function _loadTag($tag, $template)
{
2013-07-24 19:37:07 +04:00
return false;
}
2013-02-20 18:09:24 +04:00
/**
* @param string $function
* @return bool
*/
2013-07-29 14:58:14 +04:00
public function isAllowedFunction($function)
{
if ($this->_options & self::DENY_NATIVE_FUNCS) {
2013-01-25 18:36:16 +04:00
return isset($this->_allowed_funcs[$function]);
} else {
return is_callable($function);
}
}
2013-02-26 23:56:06 +04:00
/**
* @param string $tag
* @return array
*/
2013-07-29 14:58:14 +04:00
public function getTagOwners($tag)
{
2013-01-25 18:36:16 +04:00
$tags = array();
2013-07-29 14:58:14 +04:00
foreach ($this->_actions as $owner => $params) {
if (isset($params["tags"][$tag])) {
2013-01-25 18:36:16 +04:00
$tags[] = $owner;
}
}
return $tags;
}
2013-02-20 18:09:24 +04:00
/**
* Add source template provider by scheme
*
* @param string $scm scheme name
2013-06-28 11:53:53 +04:00
* @param Fenom\ProviderInterface $provider provider object
2013-02-20 18:09:24 +04:00
*/
2013-07-29 14:58:14 +04:00
public function addProvider($scm, \Fenom\ProviderInterface $provider)
{
$this->_providers[$scm] = $provider;
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
/**
2013-07-25 02:05:44 +04:00
* Set options
2013-01-25 18:36:16 +04:00
* @param int|array $options
*/
2013-07-29 14:58:14 +04:00
public function setOptions($options)
{
if (is_array($options)) {
2013-07-23 11:32:31 +04:00
$options = self::_makeMask($options, self::$_options_list, $this->_options);
2013-01-25 18:36:16 +04:00
}
2013-02-20 18:09:24 +04:00
$this->_storage = array();
2013-01-25 18:36:16 +04:00
$this->_options = $options;
}
/**
* Get options as bits
* @return int
*/
2013-07-29 14:58:14 +04:00
public function getOptions()
{
2013-01-25 18:36:16 +04:00
return $this->_options;
}
/**
* @param bool|string $scm
2013-06-28 11:53:53 +04:00
* @return Fenom\ProviderInterface
* @throws InvalidArgumentException
*/
2013-07-29 14:58:14 +04:00
public function getProvider($scm = false)
{
if ($scm) {
if (isset($this->_providers[$scm])) {
return $this->_providers[$scm];
} else {
throw new InvalidArgumentException("Provider for '$scm' not found");
}
} else {
return $this->_provider;
}
}
2013-02-20 18:09:24 +04:00
/**
* Return empty template
*
2013-06-28 11:53:53 +04:00
* @return Fenom\Template
2013-02-20 18:09:24 +04:00
*/
2013-07-29 14:58:14 +04:00
public function getRawTemplate()
{
2013-07-25 02:05:44 +04:00
return new Template($this, $this->_options);
2013-02-13 20:51:27 +04:00
}
2013-01-25 18:36:16 +04:00
/**
* Execute template and write result into stdout
*
2013-02-20 18:09:24 +04:00
* @param string $template name of template
* @param array $vars array of data for template
2013-06-28 11:53:53 +04:00
* @return Fenom\Render
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function display($template, array $vars = array())
{
2013-01-25 18:36:16 +04:00
return $this->getTemplate($template)->display($vars);
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
/**
*
2013-02-20 18:09:24 +04:00
* @param string $template name of template
* @param array $vars array of data for template
2013-01-25 18:36:16 +04:00
* @return mixed
*/
2013-07-29 14:58:14 +04:00
public function fetch($template, array $vars = array())
{
2013-01-28 16:34:34 +04:00
return $this->getTemplate($template)->fetch($vars);
}
2013-01-25 18:36:16 +04:00
2013-07-03 12:10:50 +04:00
/**
*
*
* @param string $template name of template
* @param array $vars
2013-07-20 21:25:32 +04:00
* @param callable $callback
2013-07-03 12:10:50 +04:00
* @param float $chunk
2013-07-20 21:25:32 +04:00
* @return array
2013-07-03 12:10:50 +04:00
*/
2013-07-29 14:58:14 +04:00
public function pipe($template, $callback, array $vars = array(), $chunk = 1e6)
{
2013-07-03 12:10:50 +04:00
ob_start($callback, $chunk, true);
2013-07-20 21:25:32 +04:00
$data = $this->getTemplate($template)->display($vars);
2013-07-03 12:10:50 +04:00
ob_end_flush();
2013-07-20 21:25:32 +04:00
return $data;
2013-07-03 12:10:50 +04:00
}
2013-01-25 18:36:16 +04:00
/**
2013-05-19 02:04:52 +04:00
* Get template by name
2013-01-25 18:36:16 +04:00
*
2013-05-19 02:04:52 +04:00
* @param string $template template name with schema
* @param int $options additional options and flags
2013-06-28 11:53:53 +04:00
* @return Fenom\Template
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function getTemplate($template, $options = 0)
{
2013-07-22 18:03:43 +04:00
$options |= $this->_options;
2013-07-29 14:58:14 +04:00
$key = dechex($options) . "@" . $template;
if (isset($this->_storage[$key])) {
/** @var Fenom\Template $tpl */
$tpl = $this->_storage[$key];
if (($this->_options & self::AUTO_RELOAD) && !$tpl->isValid()) {
return $this->_storage[$key] = $this->compile($template, true, $options);
2013-01-25 18:36:16 +04:00
} else {
return $tpl;
2013-01-25 18:36:16 +04:00
}
2013-07-29 14:58:14 +04:00
} elseif ($this->_options & self::FORCE_COMPILE) {
return $this->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options);
2013-01-25 18:36:16 +04:00
} else {
2013-07-29 14:58:14 +04:00
return $this->_storage[$key] = $this->_load($template, $options);
2013-01-25 18:36:16 +04:00
}
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
/**
2013-07-20 21:25:32 +04:00
* Check if template exists
* @param string $template
* @return bool
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function templateExists($template)
{
if ($provider = strstr($template, ":", true)) {
if (isset($this->_providers[$provider])) {
2013-07-20 21:25:32 +04:00
return $this->_providers[$provider]->templateExists(substr($template, strlen($provider) + 1));
}
} else {
return $this->_provider->templateExists($template);
}
return false;
2013-01-25 18:36:16 +04:00
}
/**
2013-05-19 02:04:52 +04:00
* Load template from cache or create cache if it doesn't exists.
2013-01-25 18:36:16 +04:00
*
* @param string $tpl
* @param int $opts
2013-06-28 11:53:53 +04:00
* @return Fenom\Render
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
protected function _load($tpl, $opts)
{
$file_name = $this->_getCacheName($tpl, $opts);
2013-07-29 14:58:14 +04:00
if (!is_file($this->_compile_dir . "/" . $file_name)) {
2013-05-19 02:04:52 +04:00
return $this->compile($tpl, true, $opts);
2013-01-28 16:34:34 +04:00
} else {
2013-06-28 11:53:53 +04:00
$fenom = $this;
2013-07-29 14:58:14 +04:00
return include($this->_compile_dir . "/" . $file_name);
2013-01-28 16:34:34 +04:00
}
}
2013-01-25 18:36:16 +04:00
/**
* Generate unique name of compiled template
*
* @param string $tpl
* @param int $options
2013-01-25 18:36:16 +04:00
* @return string
*/
2013-07-29 14:58:14 +04:00
private function _getCacheName($tpl, $options)
{
$hash = $tpl . ":" . $options;
return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
2013-01-25 18:36:16 +04:00
}
/**
* Compile and save template
*
* @param string $tpl
* @param bool $store store template on disk
2013-04-04 10:56:44 +04:00
* @param int $options
* @throws RuntimeException
2013-06-28 11:53:53 +04:00
* @return \Fenom\Template
*/
2013-07-29 14:58:14 +04:00
public function compile($tpl, $store = true, $options = 0)
{
2013-04-04 10:56:44 +04:00
$options = $this->_options | $options;
2013-07-25 02:05:44 +04:00
$template = $this->getRawTemplate()->load($tpl);
2013-07-29 14:58:14 +04:00
if ($store) {
2013-04-04 10:56:44 +04:00
$cache = $this->_getCacheName($tpl, $options);
$tpl_tmp = tempnam($this->_compile_dir, $cache);
$tpl_fp = fopen($tpl_tmp, "w");
2013-07-29 14:58:14 +04:00
if (!$tpl_fp) {
throw new \RuntimeException("Can't to open temporary file $tpl_tmp. Directory " . $this->_compile_dir . " is writable?");
}
fwrite($tpl_fp, $template->getTemplateCode());
fclose($tpl_fp);
2013-07-29 14:58:14 +04:00
$file_name = $this->_compile_dir . "/" . $cache;
if (!rename($tpl_tmp, $file_name)) {
throw new \RuntimeException("Can't to move $tpl_tmp to $tpl");
}
}
2013-01-25 18:36:16 +04:00
return $template;
}
/**
2013-05-19 02:04:52 +04:00
* Flush internal memory template cache
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function flush()
{
2013-07-03 12:10:50 +04:00
$this->_storage = array();
2013-05-19 02:04:52 +04:00
}
2013-01-25 18:36:16 +04:00
/**
2013-05-19 02:04:52 +04:00
* Remove all compiled templates
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function clearAllCompiles()
{
2013-06-28 11:53:53 +04:00
\Fenom\Provider::clean($this->_compile_dir);
2013-01-25 18:36:16 +04:00
}
/**
* Compile code to template
*
* @param string $code
* @param string $name
2013-06-28 11:53:53 +04:00
* @return Fenom\Template
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function compileCode($code, $name = 'Runtime compile')
{
2013-07-25 02:05:44 +04:00
return $this->getRawTemplate()->source($name, $code);
2013-01-25 18:36:16 +04:00
}
2013-02-21 22:51:24 +04:00
/**
* 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
*/
2013-07-29 14:58:14 +04:00
private static function _makeMask(array $values, array $options, $mask = 0)
{
2013-07-23 11:32:31 +04:00
foreach ($values as $key => $value) {
if (isset($options[$key])) {
if ($options[$key]) {
$mask |= $options[$key];
2013-02-21 22:51:24 +04:00
} else {
$mask &= ~$options[$key];
2013-02-21 22:51:24 +04:00
}
} else {
throw new \RuntimeException("Undefined parameter $value");
}
}
return $mask;
}
2013-01-25 18:36:16 +04:00
}