mirror of
https://github.com/Mibew/handlebars.php.git
synced 2024-11-15 08:44:12 +03:00
initial commit
This commit is contained in:
commit
2af461c7f6
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
vendor
|
13
composer.json
Normal file
13
composer.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "xamin/handlebars.php",
|
||||
"description": "Handlebars processor for php",
|
||||
"authors": [
|
||||
{
|
||||
"name": "fzerorubigd",
|
||||
"email": "fzerorubigd@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
|
||||
}
|
||||
}
|
60
src/Handlebars/Cache.php
Normal file
60
src/Handlebars/Cache.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Cache interface
|
||||
* Base cache interface
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
interface Handlebars_Cache
|
||||
{
|
||||
/**
|
||||
* Get cache for $name if exist.
|
||||
*
|
||||
* @param string $name Cache id
|
||||
*
|
||||
* @return data on hit, boolean false on cache not found
|
||||
*/
|
||||
public function get($name);
|
||||
|
||||
/**
|
||||
* Set a cache
|
||||
*
|
||||
* @param string $name cache id
|
||||
* @param mixed $value data to store
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($name, $value);
|
||||
|
||||
/**
|
||||
* Remove cache
|
||||
*
|
||||
* @param string $name Cache id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove($name);
|
||||
}
|
73
src/Handlebars/Cache/Dummy.php
Normal file
73
src/Handlebars/Cache/Dummy.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A dummy array cache
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
class Handlebars_Cache_Dummy implements Handlebars_Cache
|
||||
{
|
||||
private $_cache = array();
|
||||
|
||||
/**
|
||||
* Get cache for $name if exist.
|
||||
*
|
||||
* @param string $name Cache id
|
||||
*
|
||||
* @return data on hit, boolean false on cache not found
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->_cache)) {
|
||||
return $this->_cache[$name];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cache
|
||||
*
|
||||
* @param string $name cache id
|
||||
* @param mixed $value data to store
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($name, $value)
|
||||
{
|
||||
$this->_cache[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove cache
|
||||
*
|
||||
* @param string $name Cache id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove($name)
|
||||
{
|
||||
unset($this->_cache[$name]);
|
||||
}
|
||||
}
|
163
src/Handlebars/Context.php
Normal file
163
src/Handlebars/Context.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handlebars context
|
||||
* Context for a template
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
class Handlebars_Context
|
||||
{
|
||||
/**
|
||||
* @var array stack for context only top stack is available
|
||||
*/
|
||||
protected $stack = array();
|
||||
|
||||
/**
|
||||
* Mustache rendering Context constructor.
|
||||
*
|
||||
* @param mixed $context Default rendering context (default: null)
|
||||
*/
|
||||
public function __construct($context = null)
|
||||
{
|
||||
if ($context !== null) {
|
||||
$this->stack = array($context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a new Context frame onto the stack.
|
||||
*
|
||||
* @param mixed $value Object or array to use for context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function push($value)
|
||||
{
|
||||
array_push($this->stack, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the last Context frame from the stack.
|
||||
*
|
||||
* @return mixed Last Context frame (object or array)
|
||||
*/
|
||||
public function pop()
|
||||
{
|
||||
return array_pop($this->stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last Context frame.
|
||||
*
|
||||
* @return mixed Last Context frame (object or array)
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
return end($this->stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current context to one of current context members
|
||||
*
|
||||
* @param string $variableName name of variable or a callable on current context
|
||||
*
|
||||
* @return mixed actual value
|
||||
*/
|
||||
public function with($variableName)
|
||||
{
|
||||
$value = $this->get($variableName);
|
||||
$this->push($value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a avariable from current context
|
||||
* Supported types :
|
||||
* variable , ../variable , variable.variable , .
|
||||
*
|
||||
* @param string $variableName variavle name to get from current context
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($variableName)
|
||||
{
|
||||
//Need to clean up
|
||||
$variableName = trim($variableName);
|
||||
$level = 0;
|
||||
while (substr($variableName, 0, 3) == '../') {
|
||||
$variableName = trim(substr($variableName, 3));
|
||||
$level++;
|
||||
}
|
||||
if (count($this->stack) < $level) {
|
||||
return '';
|
||||
}
|
||||
end($this->stack);
|
||||
while ($level) {
|
||||
prev($this->stack);
|
||||
$level--;
|
||||
}
|
||||
$current = current($this->stack);
|
||||
if (!$variableName) {
|
||||
return '';
|
||||
} elseif ($variableName == '.') {
|
||||
return $current;
|
||||
} else {
|
||||
$chunks = explode('.', $variableName);
|
||||
foreach ($chunks as $chunk) {
|
||||
if ($current == '') {
|
||||
return $current;
|
||||
}
|
||||
$current = $this->_findVariableInContext($current, $chunk);
|
||||
}
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $variable->$inside is available
|
||||
*
|
||||
* @param mixed $variable variable to check
|
||||
* @param string $inside property/method to check
|
||||
*
|
||||
* @return boolean true if exist
|
||||
*/
|
||||
private function _findVariableInContext($variable, $inside)
|
||||
{
|
||||
$value = '';
|
||||
if (is_array($variable)) {
|
||||
if (isset($variable[$inside])) {
|
||||
$value = $variable[$inside];
|
||||
}
|
||||
} elseif (is_object($variable)) {
|
||||
if (isset($variable->$inside)) {
|
||||
$value = $variable->$inside;
|
||||
} elseif (is_callable(array($variable, $inside))) {
|
||||
$value = call_user_func(array($variable, $inside));
|
||||
}
|
||||
} elseif ($inside === '.') {
|
||||
$value = $variable;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
389
src/Handlebars/Engine.php
Normal file
389
src/Handlebars/Engine.php
Normal file
@ -0,0 +1,389 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Handlebars parser (infact its a mustache parser)
|
||||
* This class is responsible for turning raw template source into a set of Mustache tokens.
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
class Handlebars_Engine
|
||||
{
|
||||
const VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* @var Handlebars_Tokenizer
|
||||
*/
|
||||
private $_tokenizer;
|
||||
|
||||
/**
|
||||
* @var Handlebars_Parser
|
||||
*/
|
||||
private $_parser;
|
||||
/**
|
||||
* @var Handlebars_Helpers
|
||||
*/
|
||||
private $_helpers;
|
||||
|
||||
/**
|
||||
* @var Handlebars_Loader
|
||||
*/
|
||||
private $_loader;
|
||||
|
||||
/**
|
||||
* @var Handlebars_Loader
|
||||
*/
|
||||
private $_partialLoader;
|
||||
|
||||
/**
|
||||
* @var Handlebars_Cache
|
||||
*/
|
||||
private $_cache;
|
||||
/**
|
||||
* @var callable escape function to use
|
||||
*/
|
||||
private $_escape = 'htmlspecialchars';
|
||||
|
||||
/**
|
||||
* @var array parametes to pass to escape function, script prepend string to this array
|
||||
*/
|
||||
private $_escapeArgs = array (
|
||||
ENT_COMPAT,
|
||||
'UTF-8'
|
||||
);
|
||||
|
||||
/**
|
||||
* Handlebars engine constructor
|
||||
* $options array can contain :
|
||||
* helpers => Handlebars_Helpers object
|
||||
* escape => a callable function to escape values
|
||||
* escapeArgs => array to pass as extra parameter to escape function
|
||||
* loader => Handlebars_Loader object
|
||||
* partial_loader => Handlebars_Loader object
|
||||
* cache => Handlebars_Cache object
|
||||
*
|
||||
* @param array $options array of options to set
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
if (isset($options['helpers'])) {
|
||||
$this->setHelpers($options['helpers']);
|
||||
}
|
||||
|
||||
if (isset($options['loader'])) {
|
||||
$this->setLoader($options['loader']);
|
||||
}
|
||||
|
||||
if (isset($options['partial_loader'])) {
|
||||
$this->setPartialLoader($options['partial_loader']);
|
||||
}
|
||||
|
||||
if (isset($options['cache'])) {
|
||||
$this->setCache($options['cache']);
|
||||
}
|
||||
|
||||
if (isset($options['escape'])) {
|
||||
if (!is_callable($options['escape'])) {
|
||||
throw new InvalidArgumentException('Handlebars Constructor "escape" option must be callable');
|
||||
}
|
||||
|
||||
$this->_escape = $options['escape'];
|
||||
}
|
||||
|
||||
if (isset($options['escapeArgs'])) {
|
||||
if (!is_array($options['escapeArgs'])) {
|
||||
$options['escapeArgs'] = array($options['escapeArgs']);
|
||||
}
|
||||
$this->_escapeArgs = $options['escapeArgs'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shortcut 'render' invocation.
|
||||
*
|
||||
* Equivalent to calling `$handlebars->loadTemplate($template)->render($data);`
|
||||
*
|
||||
* @param string $template template name
|
||||
* @param mixed $data data to use as context
|
||||
*
|
||||
* @return string Rendered template
|
||||
* @see Handlebars_Engine::loadTemplate
|
||||
* @see Handlebars_Template::render
|
||||
*/
|
||||
public function render($template, $data)
|
||||
{
|
||||
return $this->loadTemplate($template)->render($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set helpers for current enfine
|
||||
*
|
||||
* @param Handlebars_Helpers $helpers handlebars helper
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHelpers(Handlebars_Helpers $helpers)
|
||||
{
|
||||
$this->_helpers = $helpers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get helpers, or create new one if ther is no helper
|
||||
*
|
||||
* @return Handlebars_Helpers
|
||||
*/
|
||||
public function getHelpers()
|
||||
{
|
||||
if (!isset($this->_helpers)) {
|
||||
$this->_helpers = new Handlebars_Helpers();
|
||||
}
|
||||
return $this->_helpers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current loader
|
||||
*
|
||||
* @param Handlebars_Loader $loader handlebars loader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLoader(Handlebars_Loader $loader)
|
||||
{
|
||||
$this->_loader = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current loader
|
||||
*
|
||||
* @return Handlebars_Loader
|
||||
*/
|
||||
public function getLoader()
|
||||
{
|
||||
if (!isset($this->_loader)) {
|
||||
$this->_loader = new Handlebars_Loader_StringLoader();
|
||||
}
|
||||
return $this->_loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current partial loader
|
||||
*
|
||||
* @param Handlebars_Loader $loader handlebars loader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPartialLoader(Handlebars_Loader $loader)
|
||||
{
|
||||
$this->_partialLoader = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current partial loader
|
||||
*
|
||||
* @return Handlebars_Loader
|
||||
*/
|
||||
public function getPartialLoader()
|
||||
{
|
||||
if (!isset($this->_partialLoader)) {
|
||||
$this->_partialLoader = new Handlebars_Loader_StringLoader();
|
||||
}
|
||||
return $this->_partialLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache for current engine
|
||||
*
|
||||
* @param Handlebars_cache $cache handlebars cache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCache(Handlebars_Cache $cache)
|
||||
{
|
||||
$this->_cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache
|
||||
*
|
||||
* @return Handlebars_Cache
|
||||
*/
|
||||
public function getCache()
|
||||
{
|
||||
if (!isset($this->_cache)) {
|
||||
$this->_cache = new Handlebars_Cache_Dummy();
|
||||
}
|
||||
return $this->_cache;
|
||||
}
|
||||
/**
|
||||
* Get current escape function
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function getEscape()
|
||||
{
|
||||
return $this->_escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current escpae function
|
||||
*
|
||||
* @param callable $escape function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setEscape($escape)
|
||||
{
|
||||
if (!is_callable($escape)) {
|
||||
throw new InvalidArgumentException('Escape function must be a callable');
|
||||
}
|
||||
$this->_escape = $escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current escape function
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function getEscapeArgs()
|
||||
{
|
||||
return $this->_escapeArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current escpae function
|
||||
*
|
||||
* @param array $escapeArgs arguments to pass as extra arg to function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setEscapeArgs($escapeArgs)
|
||||
{
|
||||
if (!is_array($escapeArgs)) {
|
||||
$escapeArgs = array($escapeArgs);
|
||||
}
|
||||
$this->_escapeArgs = $escapeArgs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Handlebars Tokenizer instance.
|
||||
*
|
||||
* @param Handlebars_Tokenizer $tokenizer tokenizer
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTokenizer(Handlebars_Tokenizer $tokenizer)
|
||||
{
|
||||
$this->_tokenizer = $tokenizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Handlebars Tokenizer instance.
|
||||
*
|
||||
* If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
|
||||
*
|
||||
* @return Handlebars_Tokenizer
|
||||
*/
|
||||
public function getTokenizer()
|
||||
{
|
||||
if (!isset($this->_tokenizer)) {
|
||||
$this->_tokenizer = new Handlebars_Tokenizer();
|
||||
}
|
||||
|
||||
return $this->_tokenizer;
|
||||
}
|
||||
/**
|
||||
* Set the Handlebars Parser instance.
|
||||
*
|
||||
* @param Handlebars_Parser $parser parser object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setParser(Handlebars_Parser $parser)
|
||||
{
|
||||
$this->_parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Handlebars Parser instance.
|
||||
*
|
||||
* If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
|
||||
*
|
||||
* @return Handlebars_Parser
|
||||
*/
|
||||
public function getParser()
|
||||
{
|
||||
if (!isset($this->_parser)) {
|
||||
$this->_parser = new Handlebars_Parser();
|
||||
}
|
||||
|
||||
return $this->_parser;
|
||||
}
|
||||
/**
|
||||
* Load a template by name with current template loader
|
||||
*
|
||||
* @param string $name template name
|
||||
*
|
||||
* @return Handlebars_Template
|
||||
*/
|
||||
public function loadTemplate($name)
|
||||
{
|
||||
$source = $this->getLoader()->load($name);
|
||||
$tree = $this->_tokenize($source);
|
||||
return new Handlebars_Template($this, $tree, $source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a partial by name with current partial loader
|
||||
*
|
||||
* @param string $name partial name
|
||||
*
|
||||
* @return Handlebars_Template
|
||||
*/
|
||||
public function loadPartial($name)
|
||||
{
|
||||
$source = $this->getPartialLoader()->load($name);
|
||||
$tree = $this->_tokenize($source);
|
||||
return new Handlebars_Template($this, $tree, $source);
|
||||
}
|
||||
|
||||
/**
|
||||
* try to tokenize source, or get them from cache if available
|
||||
*
|
||||
* @param string $source handlebars source code
|
||||
*
|
||||
* @return array handlebars parsed data into array
|
||||
*/
|
||||
private function _tokenize($source)
|
||||
{
|
||||
$hash = md5(sprintf('version: %s, data : %s', self::VERSION, $source));
|
||||
$tree = $this->getCache()->get($hash);
|
||||
if ($tree === false) {
|
||||
$tokens = $this->getTokenizer()->scan($source);
|
||||
$tree = $this->getParser()->parse($tokens);
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
}
|
249
src/Handlebars/Helpers.php
Normal file
249
src/Handlebars/Helpers.php
Normal file
@ -0,0 +1,249 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handlebars helpers
|
||||
*
|
||||
* a collection of helper function. normally a function like
|
||||
* function ($sender, $name, $arguments) $arguments is unscaped arguments and is a string, not array
|
||||
* TODO: Add support for an interface with an execute method
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
class Handlebars_Helpers
|
||||
{
|
||||
/**
|
||||
* @var array array of helpers
|
||||
*/
|
||||
protected $helpers = array();
|
||||
|
||||
/**
|
||||
* Create new helper container class
|
||||
*
|
||||
* @param array $helpers array of name=>$value helpers
|
||||
* @param array $defaults add defaults helper (if, unless, each,with)
|
||||
*
|
||||
* @throw InvalidArgumentException when $helpers is not an array (or traversable) or helper is not a caallable
|
||||
*/
|
||||
public function __construct($helpers = null, $defaults = true)
|
||||
{
|
||||
if ($defaults) {
|
||||
$this->addDefaultHelpers();
|
||||
}
|
||||
if ($helpers != null) {
|
||||
if (!is_array($helpers) && !$helpers instanceof Traversable) {
|
||||
throw new InvalidArgumentException('HelperCollection constructor expects an array of helpers');
|
||||
}
|
||||
foreach ($helpers as $name => $helper) {
|
||||
$this->add($name, $helpers);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add default helpers (if unless each with)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addDefaultHelpers()
|
||||
{
|
||||
$this->add(
|
||||
'if',
|
||||
function ($template, $context, $args, $source)
|
||||
{
|
||||
$tmp = $context->get($args);
|
||||
$buffer = '';
|
||||
if ($tmp) {
|
||||
$buffer = $template->render($context);
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
);
|
||||
|
||||
$this->add(
|
||||
'each',
|
||||
function($template, $context, $args, $source)
|
||||
{
|
||||
$tmp = $context->get($args);
|
||||
$buffer = '';
|
||||
if (is_array($tmp) || $tmp instanceof Traversable) {
|
||||
foreach ($tmp as $var) {
|
||||
$context->push($var);
|
||||
$buffer .= $template->render($context);
|
||||
$context->pop();
|
||||
}
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
);
|
||||
|
||||
$this->add(
|
||||
'unless',
|
||||
function ($template, $context, $args, $source)
|
||||
{
|
||||
$tmp = $context->get($args);
|
||||
$buffer = '';
|
||||
if (!$tmp) {
|
||||
$buffer = $template->render($context);
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
);
|
||||
|
||||
$this->add(
|
||||
'with',
|
||||
function ($template, $context, $args, $source)
|
||||
{
|
||||
$tmp = $context->get($args);
|
||||
$context->push($tmp);
|
||||
$buffer = $template->render($context);
|
||||
$context->pop();
|
||||
return $buffer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new helper to helpers
|
||||
*
|
||||
* @param string $name helper name
|
||||
* @param callable $helper a function as a helper
|
||||
*
|
||||
* @return void
|
||||
* @throw InvalidArgumentException if $helper is not a callable
|
||||
*/
|
||||
public function add($name ,$helper)
|
||||
{
|
||||
if (!is_callable($helper)) {
|
||||
throw new InvalidArgumentException("$name Helper is not a callable.");
|
||||
}
|
||||
$this->helpers[$name] = $helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $name helper is available
|
||||
*
|
||||
* @param string $name helper name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return array_key_exists($name, $this->helpers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a helper. __magic__ method :)
|
||||
*
|
||||
* @param string $name helper name
|
||||
*
|
||||
* @return callable helper function
|
||||
* @throw InvalidArgumentException if $name is not available
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (!$this->has($name)) {
|
||||
throw new InvalidArgumentException('Unknow helper :' . $name);
|
||||
}
|
||||
return $this->helpers[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $name helper is available __magic__ method :)
|
||||
*
|
||||
* @param string $name helper name
|
||||
*
|
||||
* @return boolean
|
||||
* @see Handlebras_Helpers::has
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new helper to helpers __magic__ method :)
|
||||
*
|
||||
* @param string $name helper name
|
||||
* @param callable $helper a function as a helper
|
||||
*
|
||||
* @return void
|
||||
* @throw InvalidArgumentException if $helper is not a callable
|
||||
*/
|
||||
public function __set($name ,$helper)
|
||||
{
|
||||
$this->add($name, $helpers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unset a helper
|
||||
*
|
||||
* @param string $name helpername to remove
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
unset($this->helpers[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given helper is present in the collection.
|
||||
*
|
||||
* @param string $name helper name
|
||||
*
|
||||
* @return void
|
||||
* @throws InvalidArgumentException if the requested helper is not present.
|
||||
*/
|
||||
public function remove($name)
|
||||
{
|
||||
if (!$this->has($name)) {
|
||||
throw new InvalidArgumentException('Unknown helper: ' . $name);
|
||||
}
|
||||
|
||||
unset($this->helpers[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the helper collection.
|
||||
*
|
||||
* Removes all helpers from this collection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->helpers = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the helper collection is empty.
|
||||
*
|
||||
* @return boolean True if the collection is empty
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->helpers);
|
||||
}
|
||||
|
||||
}
|
40
src/Handlebars/Loader.php
Normal file
40
src/Handlebars/Loader.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Handlebars loader interface
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
interface Handlebars_Loader
|
||||
{
|
||||
|
||||
/**
|
||||
* Load a Template by name.
|
||||
*
|
||||
* @param string $name template name to load
|
||||
*
|
||||
* @return string Mustache Template source
|
||||
*/
|
||||
public function load($name);
|
||||
}
|
118
src/Handlebars/Loader/FilesystemLoader.php
Normal file
118
src/Handlebars/Loader/FilesystemLoader.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handlebars Template filesystem Loader implementation.
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir *
|
||||
* @implements Loader
|
||||
*/
|
||||
class Handlebars_Loader_FilesystemLoader implements Handlebars_Loader
|
||||
{
|
||||
private $_baseDir;
|
||||
private $_extension = '.handlebars';
|
||||
private $_templates = array();
|
||||
|
||||
/**
|
||||
* Handlebars filesystem Loader constructor.
|
||||
*
|
||||
* Passing an $options array allows overriding certain Loader options during instantiation:
|
||||
*
|
||||
* $options = array(
|
||||
* // The filename extension used for Mustache templates. Defaults to '.mustache'
|
||||
* 'extension' => '.ms',
|
||||
* );
|
||||
*
|
||||
* @param string $baseDir Base directory containing Mustache template files.
|
||||
* @param array $options Array of Loader options (default: array())
|
||||
*
|
||||
* @throws RuntimeException if $baseDir does not exist.
|
||||
*/
|
||||
public function __construct($baseDir, array $options = array())
|
||||
{
|
||||
$this->_baseDir = rtrim(realpath($baseDir), '/');
|
||||
|
||||
if (!is_dir($this->_baseDir)) {
|
||||
throw new RuntimeException('FilesystemLoader baseDir must be a directory: '.$baseDir);
|
||||
}
|
||||
|
||||
if (isset($options['extension'])) {
|
||||
$this->_extension = '.' . ltrim($options['extension'], '.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a Template by name.
|
||||
*
|
||||
* $loader = new FilesystemLoader(dirname(__FILE__).'/views');
|
||||
* $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache";
|
||||
*
|
||||
* @param string $name template name
|
||||
*
|
||||
* @return string Handkebars Template source
|
||||
*/
|
||||
public function load($name)
|
||||
{
|
||||
if (!isset($this->_templates[$name])) {
|
||||
$this->_templates[$name] = $this->loadFile($name);
|
||||
}
|
||||
|
||||
return $this->_templates[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for loading a Mustache file by name.
|
||||
*
|
||||
* @param string $name template name
|
||||
*
|
||||
* @return string Mustache Template source
|
||||
* @throws InvalidArgumentException if a template file is not found.
|
||||
*/
|
||||
protected function loadFile($name)
|
||||
{
|
||||
$fileName = $this->getFileName($name);
|
||||
|
||||
if (!file_exists($fileName)) {
|
||||
throw new InvalidArgumentException('Template '.$name.' not found.');
|
||||
}
|
||||
|
||||
return file_get_contents($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getting a Mustache template file name.
|
||||
*
|
||||
* @param string $name template name
|
||||
*
|
||||
* @return string Template file name
|
||||
*/
|
||||
protected function getFileName($name)
|
||||
{
|
||||
$fileName = $this->_baseDir . '/' . $name;
|
||||
if (substr($fileName, 0 - strlen($this->_extension)) !== $this->_extension) {
|
||||
$fileName .= $this->_extension;
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
}
|
43
src/Handlebars/Loader/StringLoader.php
Normal file
43
src/Handlebars/Loader/StringLoader.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handlebars Template string Loader implementation.
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir *
|
||||
* @implements Loader
|
||||
*/
|
||||
class Handlebars_Loader_StringLoader implements Handlebars_Loader
|
||||
{
|
||||
|
||||
/**
|
||||
* Load a Template by source.
|
||||
*
|
||||
* @param string $name Handlebars Template source
|
||||
*
|
||||
* @return string Handlebars Template source
|
||||
*/
|
||||
public function load($name)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
}
|
98
src/Handlebars/Parser.php
Normal file
98
src/Handlebars/Parser.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
* re-write to use with handlebars
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handlebars parser (infact its a mustache parser)
|
||||
* This class is responsible for turning raw template source into a set of Mustache tokens.
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
class Handlebars_Parser
|
||||
{
|
||||
/**
|
||||
* Process array of tokens and convert them into parse tree
|
||||
*
|
||||
* @param array $tokens Set of
|
||||
*
|
||||
* @return array Token parse tree
|
||||
*/
|
||||
public function parse(array $tokens = array())
|
||||
{
|
||||
return $this->_buildTree(new ArrayIterator($tokens));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for recursively building a parse tree.
|
||||
*
|
||||
* @param ArrayIterator $tokens Stream of tokens
|
||||
*
|
||||
* @return array Token parse tree
|
||||
*
|
||||
* @throws LogicException when nesting errors or mismatched section tags are encountered.
|
||||
*/
|
||||
private function _buildTree(ArrayIterator $tokens)
|
||||
{
|
||||
$stack = array();
|
||||
|
||||
do {
|
||||
$token = $tokens->current();
|
||||
$tokens->next();
|
||||
|
||||
if ($token === null) {
|
||||
continue;
|
||||
} else {
|
||||
switch ($token[Handlebars_Tokenizer::TYPE]) {
|
||||
case Handlebars_Tokenizer::T_END_SECTION:
|
||||
$newNodes = array ();
|
||||
$continue = true;
|
||||
do {
|
||||
$result = array_pop($stack);
|
||||
if ($result === null) {
|
||||
throw new LogicException('Unexpected closing tag: /'. $token[Handlebars_Tokenizer::NAME]);
|
||||
}
|
||||
|
||||
if (!array_key_exists(Handlebars_Tokenizer::NODES, $result)
|
||||
&& isset($result[Handlebars_Tokenizer::NAME])
|
||||
&& $result[Handlebars_Tokenizer::NAME] == $token[Handlebars_Tokenizer::NAME]
|
||||
) {
|
||||
$result[Handlebars_Tokenizer::NODES] = $newNodes;
|
||||
$result[Handlebars_Tokenizer::END] = $token[Handlebars_Tokenizer::INDEX];
|
||||
array_push($stack, $result);
|
||||
break 2;
|
||||
} else {
|
||||
array_unshift($newNodes, $result);
|
||||
}
|
||||
} while (true);
|
||||
break;
|
||||
default:
|
||||
array_push($stack, $token);
|
||||
}
|
||||
}
|
||||
|
||||
} while ($tokens->valid());
|
||||
|
||||
return $stack;
|
||||
|
||||
}
|
||||
}
|
207
src/Handlebars/Template.php
Normal file
207
src/Handlebars/Template.php
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Handlebars-php
|
||||
* Base on mustache-php https://github.com/bobthecow/mustache.php
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Handlebars base template
|
||||
* contain some utility method to get context and helpers
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 (c) ParsPooyesh Co
|
||||
* @license GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
class Handlebars_Template
|
||||
{
|
||||
/**
|
||||
* @var Handlebars_Engine
|
||||
*/
|
||||
protected $handlebars;
|
||||
|
||||
|
||||
protected $tree = array();
|
||||
|
||||
protected $source = '';
|
||||
|
||||
/**
|
||||
* @var array Run stack
|
||||
*/
|
||||
private $_stack = array();
|
||||
/**
|
||||
* Handlebars template constructor
|
||||
*
|
||||
* @param Handlebars_Engine $engine handlebar engine
|
||||
* @param array $tree Parsed tree
|
||||
* @param string $source Handlebars source
|
||||
*/
|
||||
public function __construct(Handlebars_Engine $engine, $tree, $source)
|
||||
{
|
||||
$this->handlebars = $engine;
|
||||
$this->tree = $tree;
|
||||
$this->source = $source;
|
||||
array_push($this->_stack, array (0, $this->getTree()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current tree
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTree()
|
||||
{
|
||||
return $this->tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current source
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current engine associated with this object
|
||||
*
|
||||
* @return Handlebars_Engine
|
||||
*/
|
||||
public function getEngine()
|
||||
{
|
||||
return $this->handlebars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render top tree
|
||||
*
|
||||
* @param mixed $context current context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render($context)
|
||||
{
|
||||
if (!$context instanceof Handlebars_Context) {
|
||||
$context = new Handlebars_Context($context);
|
||||
}
|
||||
$topTree = end($this->_stack); //This method never pop a value from stack
|
||||
list($index ,$tree) = $topTree;
|
||||
|
||||
$buffer = '';
|
||||
while (array_key_exists($index, $tree)) {
|
||||
$current = $tree[$index];
|
||||
$index++;
|
||||
switch ($current[Handlebars_Tokenizer::TYPE]) {
|
||||
case Handlebars_Tokenizer::T_SECTION :
|
||||
array_push($this->_stack, array(0, $current[Handlebars_Tokenizer::NODES]));
|
||||
$buffer .= $this->_section($context, $current);
|
||||
array_pop($this->_stack);
|
||||
break;
|
||||
case Handlebars_Tokenizer::T_INVERTED : //TODO: This has no effect, remove the whole ^ thing!
|
||||
case Handlebars_Tokenizer::T_COMMENT :
|
||||
$buffer .= '';
|
||||
break;
|
||||
case Handlebars_Tokenizer::T_PARTIAL:
|
||||
case Handlebars_Tokenizer::T_PARTIAL_2:
|
||||
$buffer .= $this->_partial($context, $current);
|
||||
break;
|
||||
case Handlebars_Tokenizer::T_UNESCAPED:
|
||||
case Handlebars_Tokenizer::T_UNESCAPED_2:
|
||||
$buffer .= $this->_variables($context, $current, false);
|
||||
break;
|
||||
case Handlebars_Tokenizer::T_ESCAPED:
|
||||
$buffer .= $this->_variables($context, $current, true);
|
||||
break;
|
||||
case Handlebars_Tokenizer::T_TEXT:
|
||||
$buffer .= $current[Handlebars_Tokenizer::VALUE];
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('Invalid node type : ' . json_encode($current));
|
||||
}
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process section nodes
|
||||
*
|
||||
* @param Handlebars_Context $context current context
|
||||
* @param array $current section node data
|
||||
*
|
||||
* @return string the result
|
||||
*/
|
||||
private function _section(Handlebars_Context $context, $current)
|
||||
{
|
||||
$helpers = $this->handlebars->getHelpers();
|
||||
$sectionName = $current[Handlebars_Tokenizer::NAME];
|
||||
if ($helpers->has($sectionName)) {
|
||||
$source = substr(
|
||||
$this->getSource(),
|
||||
$current[Handlebars_Tokenizer::INDEX],
|
||||
$current[Handlebars_Tokenizer::END] - $current[Handlebars_Tokenizer::INDEX]
|
||||
);
|
||||
$params = array(
|
||||
$this, //First argument is this template
|
||||
$context, //Secound is current context
|
||||
$current[Handlebars_Tokenizer::ARGS], //Arguments
|
||||
$source
|
||||
);
|
||||
return call_user_func_array($helpers->$sectionName, $params);
|
||||
} else {
|
||||
throw new RuntimeException($sectionName . ' is not registered as a helper');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process partial section
|
||||
*
|
||||
* @param Handlebars_Context $context current context
|
||||
* @param array $current section node data
|
||||
*
|
||||
* @return string the result
|
||||
*/
|
||||
private function _partial($context, $current)
|
||||
{
|
||||
$partial = $this->handlebars->loadPartial($current[Handlebars_Tokenizer::NAME]);
|
||||
return $partial->render($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process partial section
|
||||
*
|
||||
* @param Handlebars_Context $context current context
|
||||
* @param array $current section node data
|
||||
* @param boolean $escaped escape result or not
|
||||
*
|
||||
* @return string the result
|
||||
*/
|
||||
private function _variables($context, $current, $escaped)
|
||||
{
|
||||
$value = $context->get($current[Handlebars_Tokenizer::NAME]);
|
||||
if ($escaped) {
|
||||
$args = $this->handlebars->getEscapeArgs();
|
||||
array_unshift($args, $value);
|
||||
$value = call_user_func_array($this->handlebars->getEscape(), array_values($args));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
}
|
322
src/Handlebars/Tokenizer.php
Normal file
322
src/Handlebars/Tokenizer.php
Normal file
@ -0,0 +1,322 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of Mustache.php.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
* Changes to match xamin-std and handlebars made by xamin team
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author Justin Hileman <dontknow@example.org>
|
||||
* @author fzerorubigd <fzerorubigd@gmail.com>
|
||||
* @copyright 2012 Justin Hileman
|
||||
* @license MIT <http://opensource.org/licenses/mit-license.php>
|
||||
* @version GIT: $Id$
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Handlebars parser (infact its a mustache parser)
|
||||
* This class is responsible for turning raw template source into a set of Mustache tokens.
|
||||
*
|
||||
* @category Xamin
|
||||
* @package Handlebars
|
||||
* @author Justin Hileman <dontknow@example.org>
|
||||
* @copyright 2012 Justin Hileman
|
||||
* @license MIT <http://opensource.org/licenses/mit-license.php>
|
||||
* @version Release: @package_version@
|
||||
* @link http://xamin.ir
|
||||
*/
|
||||
class Handlebars_Tokenizer
|
||||
{
|
||||
|
||||
// Finite state machine states
|
||||
const IN_TEXT = 0;
|
||||
const IN_TAG_TYPE = 1;
|
||||
const IN_TAG = 2;
|
||||
|
||||
// Token types
|
||||
const T_SECTION = '#';
|
||||
const T_INVERTED = '^'; //Must remove this
|
||||
const T_END_SECTION = '/';
|
||||
const T_COMMENT = '!';
|
||||
const T_PARTIAL = '>'; //Maybe remove this partials and replace them with helpers
|
||||
const T_PARTIAL_2 = '<';
|
||||
const T_DELIM_CHANGE = '=';
|
||||
const T_ESCAPED = '_v';
|
||||
const T_UNESCAPED = '{';
|
||||
const T_UNESCAPED_2 = '&';
|
||||
const T_TEXT = '_t';
|
||||
|
||||
// Valid token types
|
||||
private static $_tagTypes = array(
|
||||
self::T_SECTION => true,
|
||||
self::T_INVERTED => true,
|
||||
self::T_END_SECTION => true,
|
||||
self::T_COMMENT => true,
|
||||
self::T_PARTIAL => true,
|
||||
self::T_PARTIAL_2 => true,
|
||||
self::T_DELIM_CHANGE => true,
|
||||
self::T_ESCAPED => true,
|
||||
self::T_UNESCAPED => true,
|
||||
self::T_UNESCAPED_2 => true,
|
||||
);
|
||||
|
||||
// Interpolated tags
|
||||
private static $_interpolatedTags = array(
|
||||
self::T_ESCAPED => true,
|
||||
self::T_UNESCAPED => true,
|
||||
self::T_UNESCAPED_2 => true,
|
||||
);
|
||||
|
||||
// Token properties
|
||||
const TYPE = 'type';
|
||||
const NAME = 'name';
|
||||
const OTAG = 'otag';
|
||||
const CTAG = 'ctag';
|
||||
const INDEX = 'index';
|
||||
const END = 'end';
|
||||
const INDENT = 'indent';
|
||||
const NODES = 'nodes';
|
||||
const VALUE = 'value';
|
||||
const ARGS = 'args';
|
||||
|
||||
protected $state;
|
||||
protected $tagType;
|
||||
protected $tag;
|
||||
protected $buffer;
|
||||
protected $tokens;
|
||||
protected $seenTag;
|
||||
protected $lineStart;
|
||||
protected $otag;
|
||||
protected $ctag;
|
||||
|
||||
/**
|
||||
* Scan and tokenize template source.
|
||||
*
|
||||
* @param string $text Mustache template source to tokenize
|
||||
* @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null)
|
||||
*
|
||||
* @return array Set of Mustache tokens
|
||||
*/
|
||||
public function scan($text, $delimiters = null)
|
||||
{
|
||||
$this->reset();
|
||||
|
||||
if ($delimiters = trim($delimiters)) {
|
||||
list($otag, $ctag) = explode(' ', $delimiters);
|
||||
$this->otag = $otag;
|
||||
$this->ctag = $ctag;
|
||||
}
|
||||
|
||||
$len = strlen($text);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
switch ($this->state) {
|
||||
case self::IN_TEXT:
|
||||
if ($this->tagChange($this->otag, $text, $i)) {
|
||||
$i--;
|
||||
$this->flushBuffer();
|
||||
$this->state = self::IN_TAG_TYPE;
|
||||
} else {
|
||||
if ($text[$i] == "\n") {
|
||||
$this->filterLine();
|
||||
} else {
|
||||
$this->buffer .= $text[$i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case self::IN_TAG_TYPE:
|
||||
|
||||
$i += strlen($this->otag) - 1;
|
||||
if (isset(self::$_tagTypes[$text[$i + 1]])) {
|
||||
$tag = $text[$i + 1];
|
||||
$this->tagType = $tag;
|
||||
} else {
|
||||
$tag = null;
|
||||
$this->tagType = self::T_ESCAPED;
|
||||
}
|
||||
|
||||
if ($this->tagType === self::T_DELIM_CHANGE) {
|
||||
$i = $this->changeDelimiters($text, $i);
|
||||
$this->state = self::IN_TEXT;
|
||||
} else {
|
||||
if ($tag !== null) {
|
||||
$i++;
|
||||
}
|
||||
$this->state = self::IN_TAG;
|
||||
}
|
||||
$this->seenTag = $i;
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($this->tagChange($this->ctag, $text, $i)) {
|
||||
if ($this->tagType == self::T_SECTION || $this->tagType == self::T_INVERTED) {
|
||||
$newBuffer = explode(' ', trim($this->buffer), 2);
|
||||
$args = '';
|
||||
if (count($newBuffer) == 2) {
|
||||
$args = $newBuffer[1];
|
||||
}
|
||||
$this->buffer = $newBuffer[0];
|
||||
}
|
||||
$t = array(
|
||||
self::TYPE => $this->tagType,
|
||||
self::NAME => trim($this->buffer),
|
||||
self::OTAG => $this->otag,
|
||||
self::CTAG => $this->ctag,
|
||||
self::INDEX => ($this->tagType == self::T_END_SECTION) ? $this->seenTag - strlen($this->otag) : $i + strlen($this->ctag),
|
||||
);
|
||||
if (isset($args)) {
|
||||
$t[self::ARGS] = $args;
|
||||
}
|
||||
$this->tokens[] = $t;
|
||||
unset($t);
|
||||
unset($args);
|
||||
$this->buffer = '';
|
||||
$i += strlen($this->ctag) - 1;
|
||||
$this->state = self::IN_TEXT;
|
||||
if ($this->tagType == self::T_UNESCAPED) {
|
||||
if ($this->ctag == '}}') {
|
||||
$i++;
|
||||
} else {
|
||||
// Clean up `{{{ tripleStache }}}` style tokens.
|
||||
$lastName = $this->tokens[count($this->tokens) - 1][self::NAME];
|
||||
if (substr($lastName, -1) === '}') {
|
||||
$this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->buffer .= $text[$i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->filterLine(true);
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to reset tokenizer internal state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function reset()
|
||||
{
|
||||
$this->state = self::IN_TEXT;
|
||||
$this->tagType = null;
|
||||
$this->tag = null;
|
||||
$this->buffer = '';
|
||||
$this->tokens = array();
|
||||
$this->seenTag = false;
|
||||
$this->lineStart = 0;
|
||||
$this->otag = '{{';
|
||||
$this->ctag = '}}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the current buffer to a token.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function flushBuffer()
|
||||
{
|
||||
if (!empty($this->buffer)) {
|
||||
$this->tokens[] = array(self::TYPE => self::T_TEXT, self::VALUE => $this->buffer);
|
||||
$this->buffer = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the current line is entirely made up of whitespace.
|
||||
*
|
||||
* @return boolean True if the current line is all whitespace
|
||||
*/
|
||||
protected function lineIsWhitespace()
|
||||
{
|
||||
$tokensCount = count($this->tokens);
|
||||
for ($j = $this->lineStart; $j < $tokensCount; $j++) {
|
||||
$token = $this->tokens[$j];
|
||||
if (isset(self::$_tagTypes[$token[self::TYPE]])) {
|
||||
if (isset(self::$_interpolatedTags[$token[self::TYPE]])) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($token[self::TYPE] == self::T_TEXT) {
|
||||
if (preg_match('/\S/', $token[self::VALUE])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out whitespace-only lines and store indent levels for partials.
|
||||
*
|
||||
* @param bool $noNewLine Suppress the newline? (default: false)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function filterLine($noNewLine = false)
|
||||
{
|
||||
$this->flushBuffer();
|
||||
if ($this->seenTag && $this->lineIsWhitespace()) {
|
||||
$tokensCount = count($this->tokens);
|
||||
for ($j = $this->lineStart; $j < $tokensCount; $j++) {
|
||||
if ($this->tokens[$j][self::TYPE] == self::T_TEXT) {
|
||||
if (isset($this->tokens[$j + 1]) && $this->tokens[$j + 1][self::TYPE] == self::T_PARTIAL) {
|
||||
$this->tokens[$j + 1][self::INDENT] = $this->tokens[$j][self::VALUE];
|
||||
}
|
||||
|
||||
$this->tokens[$j] = null;
|
||||
}
|
||||
}
|
||||
} elseif (!$noNewLine) {
|
||||
$this->tokens[] = array(self::TYPE => self::T_TEXT, self::VALUE => "\n");
|
||||
}
|
||||
|
||||
$this->seenTag = false;
|
||||
$this->lineStart = count($this->tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current Mustache delimiters. Set new `otag` and `ctag` values.
|
||||
*
|
||||
* @param string $text Mustache template source
|
||||
* @param int $index Current tokenizer index
|
||||
*
|
||||
* @return int New index value
|
||||
*/
|
||||
protected function changeDelimiters($text, $index)
|
||||
{
|
||||
$startIndex = strpos($text, '=', $index) + 1;
|
||||
$close = '='.$this->ctag;
|
||||
$closeIndex = strpos($text, $close, $index);
|
||||
|
||||
list($otag, $ctag) = explode(' ', trim(substr($text, $startIndex, $closeIndex - $startIndex)));
|
||||
$this->otag = $otag;
|
||||
$this->ctag = $ctag;
|
||||
|
||||
return $closeIndex + strlen($close) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether it's time to change tags.
|
||||
*
|
||||
* @param string $tag Current tag name
|
||||
* @param string $text Mustache template source
|
||||
* @param int $index Current tokenizer index
|
||||
*
|
||||
* @return boolean True if this is a closing section tag
|
||||
*/
|
||||
protected function tagChange($tag, $text, $index)
|
||||
{
|
||||
return substr($text, $index, strlen($tag)) === $tag;
|
||||
}
|
||||
}
|
61
src/Handlebars/simpletest.php
Normal file
61
src/Handlebars/simpletest.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
require "Template.php";
|
||||
require "Engine.php";
|
||||
require "Context.php";
|
||||
require "Tokenizer.php";
|
||||
require "Parser.php";
|
||||
|
||||
require "Cache.php";
|
||||
require "Cache/Dummy.php";
|
||||
require "Loader.php";
|
||||
require "Loader/StringLoader.php";
|
||||
require "Helpers.php";
|
||||
|
||||
|
||||
$temp = <<<END_HERE
|
||||
<div id="message"> {{!Place holder for message, leave it be in any case}}
|
||||
{{#if error}}
|
||||
<ul>
|
||||
{{#each errors}}
|
||||
<li>{{.}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#with t}}
|
||||
{{{form}}}
|
||||
{{/with}}
|
||||
{{>Test}} {{! since there is no Test partial and loader is string loder, just Test is printed out}}
|
||||
{{{slots.search}}}
|
||||
|
||||
{{slots.tags}}
|
||||
|
||||
Test
|
||||
|
||||
{{#each t.errors}}
|
||||
<li>{{.}}</li>
|
||||
{{/each}}
|
||||
END_HERE;
|
||||
|
||||
|
||||
|
||||
$contextArray =
|
||||
[
|
||||
'error' => true,
|
||||
'errors' => ['err1', 'err2', 'err3'],
|
||||
'slots' => [ 'search' => '<b>search</b>' ,'tags' => '<b>tags</b>'],
|
||||
't' =>
|
||||
[
|
||||
'errors' => ['t.err1', 't.err2'],
|
||||
'form' => '<form></form>'
|
||||
]
|
||||
];
|
||||
$engine = new Handlebars_Engine();
|
||||
|
||||
$helper = new Handlebars_Helpers();
|
||||
|
||||
|
||||
$engine->setHelpers($helper);
|
||||
|
||||
echo $engine->render($temp, $contextArray);
|
Loading…
Reference in New Issue
Block a user