mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Merge cf6bae47cb3abce5be7615fe1b623ad1822297a6 into a10808d1f35f7a4f7a68ad664e08fd06b51b1f27
This commit is contained in:
commit
c8f22e206e
256
src/Fenom.php
256
src/Fenom.php
@ -7,15 +7,16 @@
|
||||
* 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 {
|
||||
class Fenom
|
||||
{
|
||||
const VERSION = '1.0';
|
||||
|
||||
/* Compiler types */
|
||||
@ -40,7 +41,6 @@ class Fenom {
|
||||
const DEFAULT_FUNC_OPEN = 'Fenom\Compiler::stdFuncOpen';
|
||||
const DEFAULT_FUNC_CLOSE = 'Fenom\Compiler::stdFuncClose';
|
||||
const SMART_FUNC_PARSER = 'Fenom\Compiler::smartFuncParser';
|
||||
|
||||
/**
|
||||
* @var int[] of possible options, as associative array
|
||||
* @see setOptions, addOptions, delOptions
|
||||
@ -53,7 +53,6 @@ class Fenom {
|
||||
"auto_reload" => self::AUTO_RELOAD,
|
||||
"force_include" => self::FORCE_INCLUDE,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Fenom\Render[] Templates storage
|
||||
*/
|
||||
@ -62,25 +61,17 @@ class Fenom {
|
||||
* @var string compile directory
|
||||
*/
|
||||
protected $_compile_dir = "/tmp";
|
||||
|
||||
/**
|
||||
* @var int masked options
|
||||
*/
|
||||
protected $_options = 0;
|
||||
|
||||
protected $_on_pre_cmp = array();
|
||||
protected $_on_cmp = array();
|
||||
protected $_on_post_cmp = array();
|
||||
|
||||
/**
|
||||
* @var ProviderInterface
|
||||
*/
|
||||
private $_provider;
|
||||
/**
|
||||
* @var Fenom\ProviderInterface[]
|
||||
*/
|
||||
protected $_providers = array();
|
||||
|
||||
/**
|
||||
* @var string[] list of modifiers [modifier_name => callable]
|
||||
*/
|
||||
@ -99,7 +90,6 @@ class Fenom {
|
||||
"length" => 'Fenom\Modifier::length',
|
||||
"default" => 'Fenom\Modifier::defaultValue'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array of allowed PHP functions
|
||||
*/
|
||||
@ -108,7 +98,6 @@ class Fenom {
|
||||
"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
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array[] of compilers and functions
|
||||
*/
|
||||
@ -215,6 +204,18 @@ class Fenom {
|
||||
'parser' => 'Fenom\Compiler::tagCycle'
|
||||
)
|
||||
);
|
||||
/**
|
||||
* @var ProviderInterface
|
||||
*/
|
||||
private $_provider;
|
||||
|
||||
/**
|
||||
* @param Fenom\ProviderInterface $provider
|
||||
*/
|
||||
public function __construct(Fenom\ProviderInterface $provider)
|
||||
{
|
||||
$this->_provider = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just factory
|
||||
@ -225,7 +226,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) {
|
||||
@ -239,14 +241,34 @@ class Fenom {
|
||||
if ($options) {
|
||||
$fenom->setOptions($options);
|
||||
}
|
||||
|
||||
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) {
|
||||
$this->_provider = $provider;
|
||||
private static function _makeMask(array $values, array $options, $mask = 0)
|
||||
{
|
||||
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
|
||||
* @return Fenom
|
||||
*/
|
||||
public function setCompileDir($dir) {
|
||||
public function setCompileDir($dir)
|
||||
{
|
||||
$this->_compile_dir = $dir;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -264,7 +288,8 @@ class Fenom {
|
||||
*
|
||||
* @param callable $cb
|
||||
*/
|
||||
public function addPreCompileFilter($cb) {
|
||||
public function addPreCompileFilter($cb)
|
||||
{
|
||||
$this->_on_pre_cmp[] = $cb;
|
||||
}
|
||||
|
||||
@ -272,14 +297,16 @@ class Fenom {
|
||||
*
|
||||
* @param callable $cb
|
||||
*/
|
||||
public function addPostCompileFilter($cb) {
|
||||
public function addPostCompileFilter($cb)
|
||||
{
|
||||
$this->_on_post_cmp[] = $cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $cb
|
||||
*/
|
||||
public function addCompileFilter($cb) {
|
||||
public function addCompileFilter($cb)
|
||||
{
|
||||
$this->_on_cmp[] = $cb;
|
||||
}
|
||||
|
||||
@ -290,8 +317,10 @@ 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;
|
||||
}
|
||||
|
||||
@ -302,11 +331,13 @@ 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
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -315,13 +346,15 @@ 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,
|
||||
'parser' => array($storage, "tag" . $compiler)
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -334,13 +367,15 @@ 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,
|
||||
'close' => $close_parser ? : self::DEFAULT_CLOSE_COMPILER,
|
||||
'tags' => $tags,
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -352,7 +387,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(),
|
||||
@ -379,6 +415,7 @@ class Fenom {
|
||||
}
|
||||
}
|
||||
$this->_actions[$compiler] = $c;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -388,12 +425,14 @@ 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,
|
||||
'function' => $callback,
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -402,12 +441,14 @@ 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,
|
||||
'function' => $callback,
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -418,13 +459,15 @@ 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,
|
||||
'close' => $parser_close,
|
||||
'function' => $callback,
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -432,8 +475,10 @@ 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;
|
||||
}
|
||||
|
||||
@ -444,7 +489,8 @@ class Fenom {
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getModifier($modifier) {
|
||||
public function getModifier($modifier)
|
||||
{
|
||||
if (isset($this->_modifiers[$modifier])) {
|
||||
return $this->_modifiers[$modifier];
|
||||
} elseif ($this->isAllowedFunction($modifier)) {
|
||||
@ -460,7 +506,8 @@ class Fenom {
|
||||
* @param string $function
|
||||
* @return string|bool
|
||||
*/
|
||||
public function getFunction($function) {
|
||||
public function getFunction($function)
|
||||
{
|
||||
if (isset($this->_actions[$function])) {
|
||||
return $this->_actions[$function];
|
||||
} else {
|
||||
@ -472,7 +519,8 @@ class Fenom {
|
||||
* @param string $function
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedFunction($function) {
|
||||
public function isAllowedFunction($function)
|
||||
{
|
||||
if ($this->_options & self::DENY_INLINE_FUNCS) {
|
||||
return isset($this->_allowed_funcs[$function]);
|
||||
} else {
|
||||
@ -484,13 +532,15 @@ 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])) {
|
||||
$tags[] = $owner;
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
@ -500,10 +550,20 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* FORCE_COMPILE, CHECK_MTIME, or associative array with boolean values:
|
||||
@ -513,7 +573,8 @@ class Fenom {
|
||||
* compile_check - check template modifications (slow!)
|
||||
* @param int|array $options
|
||||
*/
|
||||
public function setOptions($options) {
|
||||
public function setOptions($options)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
$options = self::_makeMask($options, self::$_option_list);
|
||||
}
|
||||
@ -521,20 +582,13 @@ class Fenom {
|
||||
$this->_options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options as bits
|
||||
* @return int
|
||||
*/
|
||||
public function getOptions() {
|
||||
return $this->_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|string $scm
|
||||
* @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];
|
||||
@ -551,7 +605,8 @@ class Fenom {
|
||||
*
|
||||
* @return Fenom\Template
|
||||
*/
|
||||
public function getRawTemplate() {
|
||||
public function getRawTemplate()
|
||||
{
|
||||
return new \Fenom\Template($this, $this->_options);
|
||||
}
|
||||
|
||||
@ -562,7 +617,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);
|
||||
}
|
||||
|
||||
@ -572,7 +628,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);
|
||||
}
|
||||
|
||||
@ -586,7 +643,8 @@ class Fenom {
|
||||
* @return \Fenom\Render
|
||||
* @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);
|
||||
$this->getTemplate($template)->display($vars);
|
||||
ob_end_flush();
|
||||
@ -600,7 +658,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)
|
||||
{
|
||||
$key = dechex($this->_options | $options) . "@" . $template;
|
||||
if (isset($this->_storage[$key])) {
|
||||
/** @var Fenom\Template $tpl */
|
||||
@ -622,39 +681,11 @@ class Fenom {
|
||||
*
|
||||
* @param Fenom\Render $template
|
||||
*/
|
||||
public function addTemplate(Fenom\Render $template) {
|
||||
public function addTemplate(Fenom\Render $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
|
||||
*
|
||||
@ -664,7 +695,8 @@ 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);
|
||||
if ($store) {
|
||||
@ -681,20 +713,23 @@ class Fenom {
|
||||
throw new \RuntimeException("Can't to move $tpl_tmp to $tpl");
|
||||
}
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -705,32 +740,41 @@ class Fenom {
|
||||
* @param string $name
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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
|
||||
* Generate unique name of compiled template
|
||||
*
|
||||
* @param string $tpl
|
||||
* @param int $options
|
||||
* @return string
|
||||
*/
|
||||
private static function _makeMask(array $values, array $options, $mask = 0) {
|
||||
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;
|
||||
private function _getCacheName($tpl, $options)
|
||||
{
|
||||
$hash = $tpl . ":" . $options;
|
||||
|
||||
return sprintf("%s.%x.%x.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Fenom;
|
||||
|
||||
use Fenom\Tokenizer;
|
||||
use Fenom\Template;
|
||||
use Fenom\Scope;
|
||||
@ -17,7 +18,8 @@ use Fenom\Scope;
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Compiler {
|
||||
class Compiler
|
||||
{
|
||||
/**
|
||||
* Tag {include ...}
|
||||
*
|
||||
@ -27,13 +29,15 @@ class Compiler {
|
||||
* @throws InvalidUsageException
|
||||
* @return string
|
||||
*/
|
||||
public static function tagInclude(Tokenizer $tokens, Template $tpl) {
|
||||
public static function tagInclude(Tokenizer $tokens, Template $tpl)
|
||||
{
|
||||
$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);';
|
||||
} else {
|
||||
return '$tpl->getStorage()->getTemplate(' . $cname . ')->display(' . self::toArray($p) . '+(array)$tpl);';
|
||||
@ -42,6 +46,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);';
|
||||
} else {
|
||||
return '$tpl->getStorage()->getTemplate(' . $cname . ')->display((array)$tpl);';
|
||||
@ -49,7 +54,6 @@ class Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open tag {if ...}
|
||||
*
|
||||
@ -58,8 +62,10 @@ 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,10 +78,12 @@ 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}');
|
||||
}
|
||||
|
||||
return '} elseif(' . $scope->tpl->parseExp($tokens, true) . ') {';
|
||||
}
|
||||
|
||||
@ -88,8 +96,10 @@ 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 +113,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,8 +183,10 @@ 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 +198,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 +214,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('', '');
|
||||
@ -214,16 +229,22 @@ class Compiler {
|
||||
if (is_numeric($p["step"])) {
|
||||
if ($p["step"] > 0) {
|
||||
$condition = "$var <= {$p['to']}";
|
||||
if($p["last"]) $c = "($var + {$p['step']}) > {$p['to']}";
|
||||
if ($p["last"]) {
|
||||
$c = "($var + {$p['step']}) > {$p['to']}";
|
||||
}
|
||||
} elseif ($p["step"] < 0) {
|
||||
$condition = "$var >= {$p['to']}";
|
||||
if($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}";
|
||||
if ($p["last"]) {
|
||||
$c = "($var + {$p['step']}) < {$p['to']}";
|
||||
}
|
||||
} else {
|
||||
throw new InvalidUsageException("Invalid step value if {for}");
|
||||
}
|
||||
} else {
|
||||
$condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})";
|
||||
if($p["last"]) $c = "({$p['step']} > 0 && ($var + {$p['step']}) <= {$p['to']} || {$p['step']} < 0 && ($var + {$p['step']}) >= {$p['to']})";
|
||||
if ($p["last"]) {
|
||||
$c = "({$p['step']} > 0 && ($var + {$p['step']}) <= {$p['to']} || {$p['step']} < 0 && ($var + {$p['step']}) >= {$p['to']})";
|
||||
}
|
||||
}
|
||||
|
||||
if ($p["first"]) {
|
||||
@ -255,9 +276,11 @@ 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 +290,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 +305,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,9 +318,11 @@ 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
|
||||
return '';
|
||||
}
|
||||
@ -308,13 +335,15 @@ 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"]);
|
||||
$code = $scope["switch"] . "\n" . $code;
|
||||
$scope["switch"] = "";
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
@ -327,7 +356,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,13 +373,15 @@ 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"]);
|
||||
$code = $scope["switch"] . "\n" . $code;
|
||||
$scope["switch"] = "";
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
@ -362,7 +394,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 +410,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()) {
|
||||
@ -396,12 +430,14 @@ class Compiler {
|
||||
$tpl->_compatible = & $tpl->_extends->_compatible;
|
||||
}
|
||||
$tpl->addDepend($tpl->_extends);
|
||||
|
||||
return "";
|
||||
} else { // dynamic extends
|
||||
if (!isset($tpl->_compatible)) {
|
||||
$tpl->_compatible = true;
|
||||
}
|
||||
$tpl->_extends = $tpl_name;
|
||||
|
||||
return '$parent = $tpl->getStorage()->getTemplate(' . $tpl_name . ', \Fenom\Template::EXTENDED);';
|
||||
}
|
||||
}
|
||||
@ -411,7 +447,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;
|
||||
@ -434,12 +471,14 @@ class Compiler {
|
||||
} else {
|
||||
$body = '<?php ob_start(); ?>' . $body . '<?php ob_end_clean(); ?>' . $t->getBody();
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
$body .= $t->getBody();
|
||||
}
|
||||
} else {
|
||||
$body = '<?php ob_start(); ?>' . $body . '<?php ob_end_clean(); $parent->b = &$tpl->b; $parent->display((array)$tpl); unset($tpl->b, $parent->b); ?>';
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -452,7 +491,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");
|
||||
}
|
||||
@ -473,11 +513,13 @@ class Compiler {
|
||||
}
|
||||
$tpl->uses = $blocks + $tpl->uses;
|
||||
$tpl->addDepend($donor);
|
||||
|
||||
return '?>' . $donor->getBody() . '<?php ';
|
||||
} else {
|
||||
// throw new InvalidUsageException('template name must be given explicitly yet');
|
||||
// under construction
|
||||
$tpl->_compatible = true;
|
||||
|
||||
return '$donor = $tpl->getStorage()->getTemplate(' . $cname . ', \Fenom\Template::EXTENDED);' . PHP_EOL .
|
||||
'$donor->fetch((array)$tpl);' . PHP_EOL .
|
||||
'$tpl->b += (array)$donor->b';
|
||||
@ -491,7 +533,8 @@ 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;
|
||||
@ -506,7 +549,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
|
||||
@ -558,11 +602,13 @@ class Compiler {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
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 childs");
|
||||
}
|
||||
@ -574,7 +620,8 @@ class Compiler {
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
public static function stdClose() {
|
||||
public static function stdClose()
|
||||
{
|
||||
return '}';
|
||||
}
|
||||
|
||||
@ -587,7 +634,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 "echo $function(" . self::toArray($tpl->parseParams($tokens)) . ', $tpl);';
|
||||
}
|
||||
|
||||
@ -600,7 +648,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);
|
||||
@ -618,6 +667,7 @@ class Compiler {
|
||||
$args[] = $param->getDefaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
return "echo $function(" . implode(", ", $args) . ');';
|
||||
}
|
||||
|
||||
@ -629,8 +679,10 @@ 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 +694,8 @@ class Compiler {
|
||||
* @param Scope $scope
|
||||
* @return string
|
||||
*/
|
||||
public static function stdFuncClose($tokens, Scope $scope) {
|
||||
public static function stdFuncClose($tokens, Scope $scope)
|
||||
{
|
||||
return "echo " . $scope["function"] . '(' . $scope["params"] . ', ob_get_clean(), $tpl);';
|
||||
}
|
||||
|
||||
@ -651,7 +704,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 +719,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;
|
||||
@ -682,6 +737,7 @@ class Compiler {
|
||||
} else {
|
||||
$scope["value"] = "ob_get_clean()";
|
||||
}
|
||||
|
||||
return 'ob_start();';
|
||||
}
|
||||
}
|
||||
@ -691,18 +747,20 @@ 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"] . ';';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Tokenizer $tokens
|
||||
* @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 +769,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 +782,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 {
|
||||
@ -738,6 +798,7 @@ class Compiler {
|
||||
}
|
||||
} else {
|
||||
$var = $tpl->tmpVar();
|
||||
|
||||
return 'echo ' . __CLASS__ . '::cycle(' . $exp . ", isset($var) ? ++$var : ($var = 0) )";
|
||||
}
|
||||
}
|
||||
@ -748,7 +809,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 +823,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();
|
||||
@ -814,6 +877,7 @@ class Compiler {
|
||||
}
|
||||
$tpl->addDepend($donor);
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
@ -825,7 +889,8 @@ 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();
|
||||
@ -857,7 +922,8 @@ class Compiler {
|
||||
* @param Tokenizer $tokens
|
||||
* @param Scope $scope
|
||||
*/
|
||||
public static function macroClose(Tokenizer $tokens, Scope $scope) {
|
||||
public static function macroClose(Tokenizer $tokens, Scope $scope)
|
||||
{
|
||||
$scope->tpl->macros[$scope["name"]] = array(
|
||||
"body" => $content = $scope->getContent(),
|
||||
"args" => $scope["args"],
|
||||
|
@ -13,8 +13,8 @@ namespace Fenom;
|
||||
* Collection of modifiers
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Modifier {
|
||||
|
||||
class Modifier
|
||||
{
|
||||
/**
|
||||
* Date format
|
||||
*
|
||||
@ -22,11 +22,15 @@ 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();
|
||||
if (!$date) {
|
||||
$date = time();
|
||||
}
|
||||
}
|
||||
|
||||
return strftime($format, $date);
|
||||
}
|
||||
|
||||
@ -35,11 +39,13 @@ 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();
|
||||
}
|
||||
|
||||
return date($format, $date);
|
||||
}
|
||||
|
||||
@ -50,7 +56,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 +75,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 +97,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) {
|
||||
@ -109,6 +118,7 @@ class Modifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
@ -119,7 +129,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 +144,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)) {
|
||||
@ -146,15 +158,16 @@ class Modifier {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $value
|
||||
* @param $list
|
||||
* @return bool
|
||||
*/
|
||||
public static function in($value, $list) {
|
||||
public static function in($value, $list)
|
||||
{
|
||||
if (is_array($list)) {
|
||||
return in_array($value, $list);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -10,19 +10,35 @@
|
||||
namespace Fenom;
|
||||
|
||||
use Fenom\ProviderInterface;
|
||||
|
||||
/**
|
||||
* Base template provider
|
||||
* @author Ivan Shalganov
|
||||
*/
|
||||
class Provider implements ProviderInterface {
|
||||
class Provider implements ProviderInterface
|
||||
{
|
||||
private $_path;
|
||||
|
||||
/**
|
||||
* @param string $template_dir directory of templates
|
||||
* @throws \LogicException if directory doesn't exists
|
||||
*/
|
||||
public function __construct($template_dir)
|
||||
{
|
||||
if ($_dir = realpath($template_dir)) {
|
||||
$this->_path = $_dir;
|
||||
} else {
|
||||
throw new \LogicException("Template directory {$template_dir} doesn't exists");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean directory from files
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public static function clean($path) {
|
||||
public static function clean($path)
|
||||
{
|
||||
if (is_file($path)) {
|
||||
unlink($path);
|
||||
} elseif (is_dir($path)) {
|
||||
@ -51,66 +67,47 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template_dir directory of templates
|
||||
* @throws \LogicException if directory doesn't exists
|
||||
*/
|
||||
public function __construct($template_dir) {
|
||||
if($_dir = realpath($template_dir)) {
|
||||
$this->_path = $_dir;
|
||||
} else {
|
||||
throw new \LogicException("Template directory {$template_dir} doesn't exists");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $tpl
|
||||
* @param int $time
|
||||
* @return string
|
||||
*/
|
||||
public function getSource($tpl, &$time) {
|
||||
public function getSource($tpl, &$time)
|
||||
{
|
||||
$tpl = $this->_getTemplatePath($tpl);
|
||||
clearstatcache(null, $tpl);
|
||||
$time = filemtime($tpl);
|
||||
|
||||
return file_get_contents($tpl);
|
||||
}
|
||||
|
||||
public function getLastModified($tpl) {
|
||||
public function getLastModified($tpl)
|
||||
{
|
||||
clearstatcache(null, $tpl = $this->_getTemplatePath($tpl));
|
||||
|
||||
return filemtime($tpl);
|
||||
}
|
||||
|
||||
public function getList() {
|
||||
public function getList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template path
|
||||
* @param $tpl
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function _getTemplatePath($tpl) {
|
||||
if(($path = realpath($this->_path."/".$tpl)) && strpos($path, $this->_path) === 0) {
|
||||
return $path;
|
||||
} else {
|
||||
throw new \RuntimeException("Template $tpl not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tpl
|
||||
* @return bool
|
||||
*/
|
||||
public function templateExists($tpl) {
|
||||
public function templateExists($tpl)
|
||||
{
|
||||
return file_exists($this->_path . "/" . $tpl);
|
||||
}
|
||||
|
||||
@ -118,11 +115,13 @@ class Provider implements ProviderInterface {
|
||||
* @param array $tpls
|
||||
* @return array
|
||||
*/
|
||||
public function getLastModifiedBatch($tpls) {
|
||||
public function getLastModifiedBatch($tpls)
|
||||
{
|
||||
$tpls = array_flip($tpls);
|
||||
foreach ($tpls as $tpl => &$time) {
|
||||
$time = $this->getLastModified($tpl);
|
||||
}
|
||||
|
||||
return $tpls;
|
||||
}
|
||||
|
||||
@ -132,7 +131,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) {
|
||||
@ -140,6 +140,22 @@ class Provider implements ProviderInterface {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template path
|
||||
* @param $tpl
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function _getTemplatePath($tpl)
|
||||
{
|
||||
if (($path = realpath($this->_path . "/" . $tpl)) && strpos($path, $this->_path) === 0) {
|
||||
return $path;
|
||||
} else {
|
||||
throw new \RuntimeException("Template $tpl not found");
|
||||
}
|
||||
}
|
||||
}
|
@ -14,12 +14,14 @@ namespace Fenom;
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
interface ProviderInterface {
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $tpl
|
||||
* @return bool
|
||||
*/
|
||||
public function templateExists($tpl);
|
||||
|
||||
/**
|
||||
* @param string $tpl
|
||||
* @param int $time
|
||||
|
@ -8,13 +8,15 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Fenom;
|
||||
|
||||
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" => "",
|
||||
@ -50,17 +52,14 @@ class Render extends \ArrayObject {
|
||||
* @var float
|
||||
*/
|
||||
protected $_time = 0.0;
|
||||
|
||||
/**
|
||||
* @var array depends list
|
||||
*/
|
||||
protected $_depends = array();
|
||||
|
||||
/**
|
||||
* @var int tempalte options (see Fenom options)
|
||||
*/
|
||||
protected $_options = 0;
|
||||
|
||||
/**
|
||||
* Template provider
|
||||
* @var ProviderInterface
|
||||
@ -72,7 +71,8 @@ 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, $props = array())
|
||||
{
|
||||
$this->_fenom = $fenom;
|
||||
$props += self::$_props;
|
||||
$this->_name = $props["name"];
|
||||
@ -87,34 +87,41 @@ class Render extends \ArrayObject {
|
||||
* Get template storage
|
||||
* @return Fenom
|
||||
*/
|
||||
public function getStorage() {
|
||||
public function getStorage()
|
||||
{
|
||||
return $this->_fenom;
|
||||
}
|
||||
|
||||
public function getDepends() {
|
||||
public function getDepends()
|
||||
{
|
||||
return $this->_depends;
|
||||
}
|
||||
|
||||
public function getScm() {
|
||||
public function getScm()
|
||||
{
|
||||
return $this->_scm;
|
||||
}
|
||||
|
||||
public function getProvider() {
|
||||
public function getProvider()
|
||||
{
|
||||
return $this->_provider;
|
||||
}
|
||||
|
||||
public function getBaseName() {
|
||||
public function getBaseName()
|
||||
{
|
||||
return $this->_base_name;
|
||||
}
|
||||
|
||||
public function getOptions() {
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
public function __toString()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
@ -122,20 +129,22 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate template
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid() {
|
||||
public function isValid()
|
||||
{
|
||||
$provider = $this->_fenom->getProvider(strstr($this->_name, ":"), true);
|
||||
if ($provider->getLastModified($this->_name) >= $this->_time) {
|
||||
return false;
|
||||
@ -145,6 +154,7 @@ class Render extends \ArrayObject {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -153,9 +163,11 @@ class Render extends \ArrayObject {
|
||||
* @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;
|
||||
}
|
||||
|
||||
@ -165,10 +177,12 @@ class Render extends \ArrayObject {
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function fetch(array $values) {
|
||||
public function fetch(array $values)
|
||||
{
|
||||
ob_start();
|
||||
try {
|
||||
$this->display($values);
|
||||
|
||||
return ob_get_clean();
|
||||
} catch (\Exception $e) {
|
||||
ob_end_clean();
|
||||
@ -182,7 +196,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,8 +14,8 @@ namespace Fenom;
|
||||
*
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Scope extends \ArrayObject {
|
||||
|
||||
class Scope extends \ArrayObject
|
||||
{
|
||||
public $line = 0;
|
||||
public $name;
|
||||
public $level = 0;
|
||||
@ -39,7 +39,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;
|
||||
@ -53,7 +54,8 @@ class Scope extends \ArrayObject {
|
||||
*
|
||||
* @param string $function
|
||||
*/
|
||||
public function setFuncName($function) {
|
||||
public function setFuncName($function)
|
||||
{
|
||||
$this["function"] = $function;
|
||||
$this->is_compiler = false;
|
||||
}
|
||||
@ -64,7 +66,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);
|
||||
}
|
||||
|
||||
@ -75,7 +78,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]);
|
||||
@ -83,6 +87,7 @@ class Scope extends \ArrayObject {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -93,7 +98,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);
|
||||
}
|
||||
|
||||
@ -103,7 +109,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);
|
||||
}
|
||||
|
||||
@ -113,7 +120,8 @@ class Scope extends \ArrayObject {
|
||||
* @throws \LogicException
|
||||
* @return string
|
||||
*/
|
||||
public function getContent() {
|
||||
public function getContent()
|
||||
{
|
||||
return substr($this->_body, $this->_offset);
|
||||
}
|
||||
|
||||
@ -123,9 +131,11 @@ 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;
|
||||
}
|
||||
|
||||
@ -134,7 +144,8 @@ class Scope extends \ArrayObject {
|
||||
*
|
||||
* @param $new_content
|
||||
*/
|
||||
public function replaceContent($new_content) {
|
||||
public function replaceContent($new_content)
|
||||
{
|
||||
$this->cutContent();
|
||||
$this->_body .= $new_content;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Fenom;
|
||||
|
||||
use Fenom;
|
||||
|
||||
/**
|
||||
@ -16,8 +17,8 @@ use Fenom;
|
||||
* @package Fenom
|
||||
* @author Ivan Shalganov <a.cobest@gmail.com>
|
||||
*/
|
||||
class Template extends Render {
|
||||
|
||||
class Template extends Render
|
||||
{
|
||||
/**
|
||||
* Disable array parser.
|
||||
*/
|
||||
@ -26,14 +27,12 @@ class Template extends Render {
|
||||
* Disable modifier parser.
|
||||
*/
|
||||
const DENY_MODS = 2;
|
||||
|
||||
/**
|
||||
* Template was extended
|
||||
*/
|
||||
const DYNAMIC_EXTEND = 0x1000;
|
||||
const EXTENDED = 0x2000;
|
||||
const DYNAMIC_BLOCK = 0x4000;
|
||||
|
||||
/**
|
||||
* @var int shared counter
|
||||
*/
|
||||
@ -43,31 +42,24 @@ class Template extends Render {
|
||||
* @var string
|
||||
*/
|
||||
public $_body;
|
||||
|
||||
/**
|
||||
* @var array of macros
|
||||
*/
|
||||
public $macros = array();
|
||||
|
||||
/**
|
||||
* @var array of blocks
|
||||
*/
|
||||
public $blocks = array();
|
||||
|
||||
public $uses = array();
|
||||
|
||||
public $parents = array();
|
||||
|
||||
public $_extends;
|
||||
public $_extended = false;
|
||||
public $_compatible;
|
||||
|
||||
/**
|
||||
* Call stack
|
||||
* @var Scope[]
|
||||
*/
|
||||
private $_stack = array();
|
||||
|
||||
/**
|
||||
* Template source
|
||||
* @var string
|
||||
@ -82,9 +74,19 @@ class Template extends Render {
|
||||
* @var bool
|
||||
*/
|
||||
private $_ignore = false;
|
||||
|
||||
private $_filter = array();
|
||||
|
||||
/**
|
||||
* @param Fenom $fenom Template storage
|
||||
* @param int $options
|
||||
* @return \Fenom\Template
|
||||
*/
|
||||
public function __construct(Fenom $fenom, $options)
|
||||
{
|
||||
$this->_fenom = $fenom;
|
||||
$this->_options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just factory
|
||||
*
|
||||
@ -92,25 +94,17 @@ class Template extends Render {
|
||||
* @param $options
|
||||
* @return Template
|
||||
*/
|
||||
public static function factory(Fenom $fenom, $options) {
|
||||
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) {
|
||||
$this->_fenom = $fenom;
|
||||
$this->_options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag stack size
|
||||
* @return int
|
||||
*/
|
||||
public function getStackSize() {
|
||||
public function getStackSize()
|
||||
{
|
||||
return count($this->_stack);
|
||||
}
|
||||
|
||||
@ -120,7 +114,8 @@ class Template extends Render {
|
||||
* @param bool $compile
|
||||
* @return $this
|
||||
*/
|
||||
public function load($name, $compile = true) {
|
||||
public function load($name, $compile = true)
|
||||
{
|
||||
$this->_name = $name;
|
||||
if ($provider = strstr($name, ":", true)) {
|
||||
$this->_scm = $provider;
|
||||
@ -133,6 +128,7 @@ class Template extends Render {
|
||||
if ($compile) {
|
||||
$this->compile();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -143,12 +139,14 @@ 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) {
|
||||
$this->compile();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -157,11 +155,16 @@ class Template extends Render {
|
||||
*
|
||||
* @throws CompileException
|
||||
*/
|
||||
public function compile() {
|
||||
public function compile()
|
||||
{
|
||||
$end = $pos = 0;
|
||||
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
|
||||
$pos = $start + 1;
|
||||
continue 2;
|
||||
case "*": // if comments
|
||||
@ -238,78 +241,16 @@ class Template extends Render {
|
||||
* Generate temporary internal template variable
|
||||
* @return string
|
||||
*/
|
||||
public function tmpVar() {
|
||||
public function tmpVar()
|
||||
{
|
||||
return '$t' . ($this->i++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append plain text to template body
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
private function _appendText($text) {
|
||||
$this->_line += substr_count($text, "\n");
|
||||
if($this->_filter) {
|
||||
if(strpos($text, "<?") === false) {
|
||||
$this->_body .= $text;
|
||||
} else {
|
||||
$fragments = explode("<?", $text);
|
||||
foreach($fragments as &$fragment) {
|
||||
if($fragment) {
|
||||
foreach($this->_filter as $filter) {
|
||||
$fragment = call_user_func($filter, $fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->_body .= implode('<?php echo "<?"; ?>', $fragments);
|
||||
}
|
||||
} else {
|
||||
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>'.PHP_EOL, $text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append PHP_EOL after each '?>'
|
||||
* @param int $code
|
||||
* @return string
|
||||
*/
|
||||
private function _escapeCode($code) {
|
||||
$c = "";
|
||||
foreach(token_get_all($code) as $token) {
|
||||
if(is_string($token)) {
|
||||
$c .= $token;
|
||||
} elseif($token[0] == T_CLOSE_TAG) {
|
||||
$c .= $token[1].PHP_EOL;
|
||||
} else {
|
||||
$c .= $token[1];
|
||||
}
|
||||
}
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append PHP code to template body
|
||||
*
|
||||
* @param string $code
|
||||
* @param $source
|
||||
*/
|
||||
private function _appendCode($code, $source) {
|
||||
|
||||
if(!$code) {
|
||||
return;
|
||||
} else {
|
||||
$this->_line += substr_count($source, "\n");
|
||||
if(strpos($code, '?>') !== false) {
|
||||
$code = $this->_escapeCode($code); // paste PHP_EOL
|
||||
}
|
||||
$this->_body .= "<?php\n/* {$this->_name}:{$this->_line}: {$source} */\n $code ?>".PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable[] $cb
|
||||
*/
|
||||
public function addPostCompile($cb) {
|
||||
public function addPostCompile($cb)
|
||||
{
|
||||
$this->_post[] = $cb;
|
||||
}
|
||||
|
||||
@ -318,7 +259,8 @@ class Template extends Render {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBody() {
|
||||
public function getBody()
|
||||
{
|
||||
return $this->_body;
|
||||
}
|
||||
|
||||
@ -327,25 +269,20 @@ class Template extends Render {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateCode() {
|
||||
public function getTemplateCode()
|
||||
{
|
||||
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(
|
||||
"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";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return closure code
|
||||
* @return string
|
||||
*/
|
||||
private function _getClosureSource() {
|
||||
return "function (\$tpl) {\n?>{$this->_body}<?php\n}";
|
||||
), true
|
||||
) . ");\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -355,7 +292,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() . ";");
|
||||
@ -363,6 +301,7 @@ class Template extends Render {
|
||||
throw new CompileException("Fatal error while creating the template");
|
||||
}
|
||||
}
|
||||
|
||||
return parent::display($values);
|
||||
|
||||
}
|
||||
@ -371,7 +310,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();
|
||||
}
|
||||
|
||||
@ -381,141 +321,18 @@ class Template extends Render {
|
||||
* @throws CompileException
|
||||
* @return string
|
||||
*/
|
||||
public function fetch(array $values) {
|
||||
public function fetch(array $values)
|
||||
{
|
||||
if (!$this->_code) {
|
||||
eval("\$this->_code = " . $this->_getClosureSource() . ";");
|
||||
if (!$this->_code) {
|
||||
throw new CompileException("Fatal error while creating the template");
|
||||
}
|
||||
}
|
||||
|
||||
return parent::fetch($values);
|
||||
}
|
||||
|
||||
private function _print($data) {
|
||||
if($this->_options & Fenom::AUTO_ESCAPE) {
|
||||
return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8')";
|
||||
} else {
|
||||
return "echo $data";
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Internal tags router
|
||||
* @param Tokenizer $tokens
|
||||
*
|
||||
* @throws SecurityException
|
||||
* @throws CompileException
|
||||
* @return string executable PHP code
|
||||
*/
|
||||
private function _tag(Tokenizer $tokens) {
|
||||
try {
|
||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
if($tokens->current() === "ignore") {
|
||||
$this->_ignore = true;
|
||||
$tokens->next();
|
||||
return '';
|
||||
} else {
|
||||
return $this->_parseAct($tokens);
|
||||
}
|
||||
} elseif ($tokens->is('/')) {
|
||||
return $this->_end($tokens);
|
||||
} elseif ($tokens->is('#')) {
|
||||
return $this->_print($this->parseConst($tokens)).';';
|
||||
} else {
|
||||
return $code = $this->_print($this->parseExp($tokens)).";";
|
||||
}
|
||||
} catch (InvalidUsageException $e) {
|
||||
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
} catch (\LogicException $e) {
|
||||
throw new SecurityException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
} catch (\Exception $e) {
|
||||
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close tag handler
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @return mixed
|
||||
* @throws TokenizeException
|
||||
*/
|
||||
private function _end(Tokenizer $tokens) {
|
||||
//return "end";
|
||||
$name = $tokens->getNext(Tokenizer::MACRO_STRING);
|
||||
$tokens->next();
|
||||
if(!$this->_stack) {
|
||||
throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened");
|
||||
}
|
||||
/** @var Scope $scope */
|
||||
$scope = array_pop($this->_stack);
|
||||
if($scope->name !== $name) {
|
||||
throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})");
|
||||
}
|
||||
return $scope->close($tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse action {action ...} or {action(...) ...}
|
||||
*
|
||||
* @static
|
||||
* @param Tokenizer $tokens
|
||||
* @throws \LogicException
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
*/
|
||||
private function _parseAct(Tokenizer $tokens) {
|
||||
if($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$action = $tokens->getAndNext();
|
||||
} else {
|
||||
return $this->_print($this->parseExp($tokens)).';'; // may be math and/or boolean expression
|
||||
}
|
||||
|
||||
if($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON)) { // just invoke function or static method
|
||||
$tokens->back();
|
||||
return $this->_print($this->parseExp($tokens)).";";
|
||||
} elseif($tokens->is('.')) {
|
||||
$name = $tokens->skip()->get(Tokenizer::MACRO_STRING);
|
||||
if($action !== "macro") {
|
||||
$name = $action.".".$name;
|
||||
}
|
||||
return $this->parseMacro($tokens, $name);
|
||||
}
|
||||
|
||||
if($act = $this->_fenom->getFunction($action)) { // call some function
|
||||
switch($act["type"]) {
|
||||
case Fenom::BLOCK_COMPILER:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, 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);
|
||||
case Fenom::INLINE_FUNCTION:
|
||||
return call_user_func($act["parser"], $act["function"], $tokens, $this);
|
||||
case Fenom::BLOCK_FUNCTION:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
||||
$scope->setFuncName($act["function"]);
|
||||
array_push($this->_stack, $scope);
|
||||
return $scope->open($tokens);
|
||||
default:
|
||||
throw new \LogicException("Unknown function type");
|
||||
}
|
||||
}
|
||||
|
||||
for($j = $i = count($this->_stack)-1; $i>=0; $i--) { // call function's internal tag
|
||||
if($this->_stack[$i]->hasTag($action, $j - $i)) {
|
||||
return $this->_stack[$i]->tag($action, $tokens);
|
||||
}
|
||||
}
|
||||
if($tags = $this->_fenom->getTagOwners($action)) { // unknown template tag
|
||||
throw new TokenizeException("Unexpected tag '$action' (this tag can be used with '".implode("', '", $tags)."')");
|
||||
} else {
|
||||
throw new TokenizeException("Unexpected tag $action");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse expressions. The mix of math operations, boolean operations, scalars, arrays and variables.
|
||||
*
|
||||
@ -527,7 +344,8 @@ class Template extends Render {
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
*/
|
||||
public function parseExp(Tokenizer $tokens, $required = false) {
|
||||
public function parseExp(Tokenizer $tokens, $required = false)
|
||||
{
|
||||
$_exp = "";
|
||||
$brackets = 0;
|
||||
$term = false;
|
||||
@ -629,6 +447,7 @@ class Template extends Render {
|
||||
if ($required && $_exp === "") {
|
||||
throw new UnexpectedTokenException($tokens);
|
||||
}
|
||||
|
||||
return $_exp;
|
||||
}
|
||||
|
||||
@ -639,7 +458,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();
|
||||
@ -682,6 +502,7 @@ class Template extends Render {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $_var;
|
||||
}
|
||||
|
||||
@ -698,12 +519,14 @@ class Template extends Render {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return string
|
||||
*/
|
||||
public function parseVariable(Tokenizer $tokens, $deny = 0, &$pure_var = true) {
|
||||
public function parseVariable(Tokenizer $tokens, $deny = 0, &$pure_var = true)
|
||||
{
|
||||
$_var = $this->parseVar($tokens, $deny);
|
||||
$pure_var = true;
|
||||
while ($t = $tokens->key()) {
|
||||
if ($t === "|" && !($deny & self::DENY_MODS)) {
|
||||
$pure_var = false;
|
||||
|
||||
return $this->parseModifier($tokens, $_var);
|
||||
} elseif ($t === T_OBJECT_OPERATOR) {
|
||||
$prop = $tokens->getNext(T_STRING);
|
||||
@ -720,11 +543,13 @@ class Template extends Render {
|
||||
}
|
||||
} elseif ($t === "?" || $t === "!") {
|
||||
$pure_var = false;
|
||||
|
||||
return $this->parseTernary($tokens, $_var, $t);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $_var;
|
||||
}
|
||||
|
||||
@ -737,7 +562,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(":")) {
|
||||
@ -775,7 +601,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) {
|
||||
@ -795,6 +622,7 @@ class Template extends Render {
|
||||
return $this->parseModifier($tokens, $_scalar);
|
||||
}
|
||||
}
|
||||
|
||||
return $_scalar;
|
||||
}
|
||||
|
||||
@ -805,7 +633,8 @@ class Template extends Render {
|
||||
* @throws UnexpectedTokenException
|
||||
* @return string
|
||||
*/
|
||||
public function parseSubstr(Tokenizer $tokens) {
|
||||
public function parseSubstr(Tokenizer $tokens)
|
||||
{
|
||||
if ($tokens->is('"', "`")) {
|
||||
$stop = $tokens->current();
|
||||
$_str = '"';
|
||||
@ -824,6 +653,7 @@ class Template extends Render {
|
||||
$tokens->next();
|
||||
if ($tokens->is($stop)) {
|
||||
$tokens->skip();
|
||||
|
||||
return $_str;
|
||||
} else {
|
||||
$_str .= '."';
|
||||
@ -838,6 +668,7 @@ class Template extends Render {
|
||||
$_str .= '(' . $this->parseExp($tokens) . ')';
|
||||
if ($tokens->is($stop)) {
|
||||
$tokens->next();
|
||||
|
||||
return $_str;
|
||||
} else {
|
||||
$_str .= '."';
|
||||
@ -846,6 +677,7 @@ class Template extends Render {
|
||||
$tokens->next();
|
||||
} elseif ($t === $stop) {
|
||||
$tokens->next();
|
||||
|
||||
return $_str . '"';
|
||||
} else {
|
||||
|
||||
@ -872,7 +704,8 @@ 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));
|
||||
|
||||
@ -900,7 +733,6 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_string($mods)) { // dynamic modifier
|
||||
$mods = 'call_user_func($tpl->getStorage()->getModifier("' . $modifier_name . '"), ';
|
||||
} else {
|
||||
@ -912,6 +744,7 @@ class Template extends Render {
|
||||
$value = $mods . $value . ')';
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
@ -923,7 +756,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;
|
||||
@ -951,6 +785,7 @@ class Template extends Render {
|
||||
$val = true;
|
||||
} elseif ($tokens->is(']') && !$key) {
|
||||
$tokens->next();
|
||||
|
||||
return $_arr . ')';
|
||||
} else {
|
||||
break;
|
||||
@ -968,7 +803,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();
|
||||
@ -995,7 +831,8 @@ class Template extends Render {
|
||||
* @return string
|
||||
* @throws InvalidUsageException
|
||||
*/
|
||||
public function parseMacro(Tokenizer $tokens, $name) {
|
||||
public function parseMacro(Tokenizer $tokens, $name)
|
||||
{
|
||||
if (isset($this->macros[$name])) {
|
||||
$macro = $this->macros[$name];
|
||||
$p = $this->parseParams($tokens);
|
||||
@ -1010,6 +847,7 @@ class Template extends Render {
|
||||
}
|
||||
}
|
||||
$args = $args ? '$tpl = ' . Compiler::toArray($args) . ';' : '';
|
||||
|
||||
return '$_tpl = $tpl; ' . $args . ' ?>' . $macro["body"] . '<?php $tpl = $_tpl; unset($_tpl);';
|
||||
} else {
|
||||
throw new InvalidUsageException("Undefined macro '$name'");
|
||||
@ -1025,7 +863,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;
|
||||
@ -1044,6 +883,7 @@ class Template extends Render {
|
||||
$colon = true;
|
||||
} elseif (!$colon && $tokens->is(')')) {
|
||||
$tokens->next();
|
||||
|
||||
return $_args . ')';
|
||||
} else {
|
||||
break;
|
||||
@ -1060,17 +900,20 @@ 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);
|
||||
} else {
|
||||
$str = $tokens->getAndNext();
|
||||
$static = stripslashes(substr($str, 1, -1));
|
||||
|
||||
return $str;
|
||||
}
|
||||
} elseif ($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$static = $tokens->getAndNext();
|
||||
|
||||
return '"' . addslashes($static) . '"';
|
||||
} else {
|
||||
return $this->parseExp($tokens, true);
|
||||
@ -1087,7 +930,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)) {
|
||||
@ -1113,9 +957,233 @@ class Template extends Render {
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append plain text to template body
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
private function _appendText($text)
|
||||
{
|
||||
$this->_line += substr_count($text, "\n");
|
||||
if ($this->_filter) {
|
||||
if (strpos($text, "<?") === false) {
|
||||
$this->_body .= $text;
|
||||
} else {
|
||||
$fragments = explode("<?", $text);
|
||||
foreach ($fragments as &$fragment) {
|
||||
if ($fragment) {
|
||||
foreach ($this->_filter as $filter) {
|
||||
$fragment = call_user_func($filter, $fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->_body .= implode('<?php echo "<?"; ?>', $fragments);
|
||||
}
|
||||
} else {
|
||||
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>' . PHP_EOL, $text);
|
||||
}
|
||||
}
|
||||
|
||||
class CompileException extends \ErrorException {}
|
||||
class SecurityException extends CompileException {}
|
||||
class InvalidUsageException extends \LogicException {}
|
||||
class TokenizeException extends \RuntimeException {}
|
||||
/**
|
||||
* Append PHP_EOL after each '?>'
|
||||
* @param int $code
|
||||
* @return string
|
||||
*/
|
||||
private function _escapeCode($code)
|
||||
{
|
||||
$c = "";
|
||||
foreach (token_get_all($code) as $token) {
|
||||
if (is_string($token)) {
|
||||
$c .= $token;
|
||||
} elseif ($token[0] == T_CLOSE_TAG) {
|
||||
$c .= $token[1] . PHP_EOL;
|
||||
} else {
|
||||
$c .= $token[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append PHP code to template body
|
||||
*
|
||||
* @param string $code
|
||||
* @param $source
|
||||
*/
|
||||
private function _appendCode($code, $source)
|
||||
{
|
||||
|
||||
if (!$code) {
|
||||
return;
|
||||
} else {
|
||||
$this->_line += substr_count($source, "\n");
|
||||
if (strpos($code, '?>') !== false) {
|
||||
$code = $this->_escapeCode($code); // paste PHP_EOL
|
||||
}
|
||||
$this->_body .= "<?php\n/* {$this->_name}:{$this->_line}: {$source} */\n $code ?>" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return closure code
|
||||
* @return string
|
||||
*/
|
||||
private function _getClosureSource()
|
||||
{
|
||||
return "function (\$tpl) {\n?>{$this->_body}<?php\n}";
|
||||
}
|
||||
|
||||
private function _print($data)
|
||||
{
|
||||
if ($this->_options & Fenom::AUTO_ESCAPE) {
|
||||
return "echo htmlspecialchars($data, ENT_COMPAT, 'UTF-8')";
|
||||
} else {
|
||||
return "echo $data";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal tags router
|
||||
* @param Tokenizer $tokens
|
||||
*
|
||||
* @throws SecurityException
|
||||
* @throws CompileException
|
||||
* @return string executable PHP code
|
||||
*/
|
||||
private function _tag(Tokenizer $tokens)
|
||||
{
|
||||
try {
|
||||
if ($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
if ($tokens->current() === "ignore") {
|
||||
$this->_ignore = true;
|
||||
$tokens->next();
|
||||
|
||||
return '';
|
||||
} else {
|
||||
return $this->_parseAct($tokens);
|
||||
}
|
||||
} elseif ($tokens->is('/')) {
|
||||
return $this->_end($tokens);
|
||||
} elseif ($tokens->is('#')) {
|
||||
return $this->_print($this->parseConst($tokens)) . ';';
|
||||
} else {
|
||||
return $code = $this->_print($this->parseExp($tokens)) . ";";
|
||||
}
|
||||
} catch (InvalidUsageException $e) {
|
||||
throw new CompileException($e->getMessage() . " in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
} catch (\LogicException $e) {
|
||||
throw new SecurityException($e->getMessage() . " in {$this} line {$this->_line}, near '{" . $tokens->getSnippetAsString(0, 0) . "' <- there", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
} catch (\Exception $e) {
|
||||
throw new CompileException($e->getMessage() . " in {$this} line {$this->_line}, near '{" . $tokens->getSnippetAsString(0, 0) . "' <- there", 0, E_ERROR, $this->_name, $this->_line, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close tag handler
|
||||
*
|
||||
* @param Tokenizer $tokens
|
||||
* @return mixed
|
||||
* @throws TokenizeException
|
||||
*/
|
||||
private function _end(Tokenizer $tokens)
|
||||
{
|
||||
//return "end";
|
||||
$name = $tokens->getNext(Tokenizer::MACRO_STRING);
|
||||
$tokens->next();
|
||||
if (!$this->_stack) {
|
||||
throw new TokenizeException("Unexpected closing of the tag '$name', the tag hasn't been opened");
|
||||
}
|
||||
/** @var Scope $scope */
|
||||
$scope = array_pop($this->_stack);
|
||||
if ($scope->name !== $name) {
|
||||
throw new TokenizeException("Unexpected closing of the tag '$name' (expecting closing of the tag {$scope->name}, opened on line {$scope->line})");
|
||||
}
|
||||
|
||||
return $scope->close($tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse action {action ...} or {action(...) ...}
|
||||
*
|
||||
* @static
|
||||
* @param Tokenizer $tokens
|
||||
* @throws \LogicException
|
||||
* @throws TokenizeException
|
||||
* @return string
|
||||
*/
|
||||
private function _parseAct(Tokenizer $tokens)
|
||||
{
|
||||
if ($tokens->is(Tokenizer::MACRO_STRING)) {
|
||||
$action = $tokens->getAndNext();
|
||||
} else {
|
||||
return $this->_print($this->parseExp($tokens)) . ';'; // may be math and/or boolean expression
|
||||
}
|
||||
|
||||
if ($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON)) { // just invoke function or static method
|
||||
$tokens->back();
|
||||
|
||||
return $this->_print($this->parseExp($tokens)) . ";";
|
||||
} elseif ($tokens->is('.')) {
|
||||
$name = $tokens->skip()->get(Tokenizer::MACRO_STRING);
|
||||
if ($action !== "macro") {
|
||||
$name = $action . "." . $name;
|
||||
}
|
||||
|
||||
return $this->parseMacro($tokens, $name);
|
||||
}
|
||||
|
||||
if ($act = $this->_fenom->getFunction($action)) { // call some function
|
||||
switch ($act["type"]) {
|
||||
case Fenom::BLOCK_COMPILER:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, 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);
|
||||
case Fenom::INLINE_FUNCTION:
|
||||
return call_user_func($act["parser"], $act["function"], $tokens, $this);
|
||||
case Fenom::BLOCK_FUNCTION:
|
||||
$scope = new Scope($action, $this, $this->_line, $act, count($this->_stack), $this->_body);
|
||||
$scope->setFuncName($act["function"]);
|
||||
array_push($this->_stack, $scope);
|
||||
|
||||
return $scope->open($tokens);
|
||||
default:
|
||||
throw new \LogicException("Unknown function type");
|
||||
}
|
||||
}
|
||||
|
||||
for ($j = $i = count($this->_stack) - 1; $i >= 0; $i--) { // call function's internal tag
|
||||
if ($this->_stack[$i]->hasTag($action, $j - $i)) {
|
||||
return $this->_stack[$i]->tag($action, $tokens);
|
||||
}
|
||||
}
|
||||
if ($tags = $this->_fenom->getTagOwners($action)) { // unknown template tag
|
||||
throw new TokenizeException("Unexpected tag '$action' (this tag can be used with '" . implode("', '", $tags) . "')");
|
||||
} else {
|
||||
throw new TokenizeException("Unexpected tag $action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CompileException extends \ErrorException
|
||||
{
|
||||
}
|
||||
|
||||
class SecurityException extends CompileException
|
||||
{
|
||||
}
|
||||
|
||||
class InvalidUsageException extends \LogicException
|
||||
{
|
||||
}
|
||||
|
||||
class TokenizeException extends \RuntimeException
|
||||
{
|
||||
}
|
@ -35,12 +35,12 @@ 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;
|
||||
const LINE = 3;
|
||||
|
||||
/**
|
||||
* Some text value: foo, bar, new, class ...
|
||||
*/
|
||||
@ -77,13 +77,6 @@ class Tokenizer {
|
||||
* Condition operation
|
||||
*/
|
||||
const MACRO_COND = 1008;
|
||||
|
||||
public $tokens;
|
||||
public $p = 0;
|
||||
public $quotes = 0;
|
||||
private $_max = 0;
|
||||
private $_last_no = 0;
|
||||
|
||||
/**
|
||||
* @see http://docs.php.net/manual/en/tokens.php
|
||||
* @var array groups of tokens
|
||||
@ -135,7 +128,6 @@ class Tokenizer {
|
||||
\T_LNUMBER => 1, \T_DNUMBER => 1, \T_CONSTANT_ENCAPSED_STRING => 1
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Special tokens
|
||||
* @var array
|
||||
@ -143,11 +135,17 @@ class Tokenizer {
|
||||
private static $spec = array(
|
||||
'true' => 1, 'false' => 1, 'null' => 1, 'TRUE' => 1, 'FALSE' => 1, 'NULL' => 1
|
||||
);
|
||||
public $tokens;
|
||||
public $p = 0;
|
||||
public $quotes = 0;
|
||||
private $_max = 0;
|
||||
private $_last_no = 0;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@ -185,12 +183,32 @@ class Tokenizer {
|
||||
$this->_last_no = $this->tokens[$this->_max][3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token name
|
||||
* @static
|
||||
* @param int|string $token
|
||||
* @return string
|
||||
*/
|
||||
public static function getName($token)
|
||||
{
|
||||
if (is_string($token)) {
|
||||
return $token;
|
||||
} elseif (is_integer($token)) {
|
||||
return token_name($token);
|
||||
} elseif (is_array($token)) {
|
||||
return token_name($token[0]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is incomplete mean some string not closed
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function isIncomplete() {
|
||||
public function isIncomplete()
|
||||
{
|
||||
return ($this->quotes % 2) || ($this->tokens[$this->_max][0] === T_ENCAPSED_AND_WHITESPACE);
|
||||
}
|
||||
|
||||
@ -200,7 +218,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];
|
||||
}
|
||||
|
||||
@ -210,45 +229,25 @@ 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;
|
||||
}
|
||||
$this->p++;
|
||||
unset($this->prev, $this->curr, $this->next);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check token type. If token type is one of expected types return true. Otherwise return false
|
||||
*
|
||||
* @param array $expects
|
||||
* @param string|int $token
|
||||
* @return bool
|
||||
*/
|
||||
private function _valid($expects, $token) {
|
||||
foreach($expects as $expect) {
|
||||
if(is_string($expect) || $expect < 1000) {
|
||||
if($expect === $token) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
||||
if(isset(self::$_macros[ $expect ][ $token ])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the next token is a valid one, move the position of cursor one step forward. Otherwise throws an exception.
|
||||
* @param array $tokens
|
||||
* @throws UnexpectedTokenException
|
||||
* @return mixed
|
||||
*/
|
||||
public function _next($tokens) {
|
||||
public function _next($tokens)
|
||||
{
|
||||
$this->next();
|
||||
if (!$this->curr) {
|
||||
throw new UnexpectedTokenException($this, $tokens);
|
||||
@ -267,19 +266,21 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return substring. This method doesn't move pointer.
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
public function getSubstr($offset, $limit = 0) {
|
||||
public function getSubstr($offset, $limit = 0)
|
||||
{
|
||||
$str = '';
|
||||
if (!$limit) {
|
||||
$limit = $this->_max;
|
||||
@ -289,6 +290,7 @@ class Tokenizer {
|
||||
for ($i = $offset; $i <= $limit; $i++) {
|
||||
$str .= $this->tokens[$i][1] . $this->tokens[$i][2];
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
@ -297,10 +299,12 @@ class Tokenizer {
|
||||
* @return mixed
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function getAndNext() {
|
||||
public function getAndNext()
|
||||
{
|
||||
if ($this->curr) {
|
||||
$cur = $this->curr[1];
|
||||
$this->next();
|
||||
|
||||
return $cur;
|
||||
} else {
|
||||
throw new UnexpectedTokenException($this, func_get_args());
|
||||
@ -312,7 +316,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]);
|
||||
}
|
||||
|
||||
@ -321,7 +326,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]);
|
||||
}
|
||||
|
||||
@ -330,7 +336,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]);
|
||||
}
|
||||
|
||||
@ -341,7 +348,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 {
|
||||
@ -353,12 +361,14 @@ class Tokenizer {
|
||||
* Step back
|
||||
* @return Tokenizer
|
||||
*/
|
||||
public function back() {
|
||||
public function back()
|
||||
{
|
||||
if ($this->p === 0) {
|
||||
return $this;
|
||||
}
|
||||
$this->p--;
|
||||
unset($this->prev, $this->curr, $this->next);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -368,7 +378,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;
|
||||
@ -381,7 +392,8 @@ class Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
public function count() {
|
||||
public function count()
|
||||
{
|
||||
return $this->_max;
|
||||
}
|
||||
|
||||
@ -389,7 +401,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;
|
||||
}
|
||||
|
||||
@ -398,44 +411,30 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token name
|
||||
* @static
|
||||
* @param int|string $token
|
||||
* @return string
|
||||
*/
|
||||
public static function getName($token) {
|
||||
if(is_string($token)) {
|
||||
return $token;
|
||||
} elseif(is_integer($token)) {
|
||||
return token_name($token);
|
||||
} elseif(is_array($token)) {
|
||||
return token_name($token[0]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip specific token or throw an exception
|
||||
*
|
||||
* @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();
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
throw new UnexpectedTokenException($this, func_get_args());
|
||||
}
|
||||
} else {
|
||||
$this->next();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@ -446,10 +445,12 @@ 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();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -460,7 +461,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 {
|
||||
@ -474,7 +476,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) {
|
||||
@ -516,11 +519,13 @@ 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];
|
||||
}
|
||||
|
||||
return trim(str_replace("\n", '↵', $str));
|
||||
}
|
||||
|
||||
@ -528,7 +533,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()]);
|
||||
}
|
||||
|
||||
@ -536,14 +542,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;
|
||||
}
|
||||
|
||||
@ -551,16 +559,44 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check token type. If token type is one of expected types return true. Otherwise return false
|
||||
*
|
||||
* @param array $expects
|
||||
* @param string|int $token
|
||||
* @return bool
|
||||
*/
|
||||
private function _valid($expects, $token)
|
||||
{
|
||||
foreach ($expects as $expect) {
|
||||
if (is_string($expect) || $expect < 1000) {
|
||||
if ($expect === $token) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (isset(self::$_macros[$expect][$token])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unexpected token
|
||||
*/
|
||||
class UnexpectedTokenException extends \RuntimeException {
|
||||
public function __construct(Tokenizer $tokens, $expect = null, $where = null) {
|
||||
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 {
|
||||
@ -574,4 +610,6 @@ class UnexpectedTokenException extends \RuntimeException {
|
||||
$this->message = "Unexpected token '" . $tokens->current() . "' in " . ($where ? : "expression") . "$expect";
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
;
|
||||
|
Loading…
x
Reference in New Issue
Block a user