Added helper interface and converted all built in helpers

This commit is contained in:
Jeff Turcotte 2014-03-26 21:54:19 -04:00
parent 4e0ed3c7ae
commit c166af1ed7
8 changed files with 432 additions and 152 deletions

42
src/Handlebars/Helper.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of Handlebars-php
*
* PHP version 5.3
*
* @category Xamin
* @package Handlebars
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars;
/**
* Handlebars helper interface
*
* @category Xamin
* @package Handlebars
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
interface Helper
{
/**
* Execute the helper
*
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed
*/
public function execute(Template $template, Context $context, $args, $source);
}

View File

@ -0,0 +1,55 @@
<?php
/**
* This file is part of Handlebars-php
*
* PHP version 5.3
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars\Helper;
use Handlebars\Context;
use Handlebars\Helper;
use Handlebars\Template;
/**
* The bindAttr Helper
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
class BindAttrHelper implements Helper
{
/**
* Execute the helper
*
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed
*/
public function execute(Template $template, Context $context, $args, $source)
{
return $args;
}
}

View File

@ -0,0 +1,90 @@
<?php
/**
* This file is part of Handlebars-php
*
* PHP version 5.3
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars\Helper;
use Handlebars\Context;
use Handlebars\Helper;
use Handlebars\Template;
/**
* The Each Helper
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
class EachHelper implements Helper
{
/**
* Execute the helper
*
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed
*/
public function execute(Template $template, Context $context, $args, $source)
{
$tmp = $context->get($args);
$buffer = '';
if (!$tmp) {
$template->setStopToken('else');
$template->discard();
$template->setStopToken(false);
$buffer = $template->render($context);
} elseif (is_array($tmp) || $tmp instanceof \Traversable) {
$isList = is_array($tmp) && (array_keys($tmp) == range(0, count($tmp) - 1));
$index = 0;
$lastIndex = $isList ? (count($tmp) - 1) : false;
foreach ($tmp as $key => $var) {
$specialVariables = array(
'@index' => $index,
'@first' => ($index === 0),
'@last' => ($index === $lastIndex),
);
if (!$isList) {
$specialVariables['@key'] = $key;
}
$context->pushSpecialVariables($specialVariables);
$context->push($var);
$template->setStopToken('else');
$template->rewind();
$buffer .= $template->render($context);
$context->pop();
$context->popSpecialVariables();
$index++;
}
$template->setStopToken(false);
}
return $buffer;
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* This file is part of Handlebars-php
*
* PHP version 5.3
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars\Helper;
use Handlebars\Context;
use Handlebars\Helper;
use Handlebars\Template;
/**
* Handlebars halper interface
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
class IfHelper implements Helper
{
/**
* Execute the helper
*
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed
*/
public function execute(Template $template, Context $context, $args, $source)
{
if (is_numeric($args)) {
$tmp = $args;
} else {
$tmp = $context->get($args);
}
if ($tmp) {
$template->setStopToken('else');
$buffer = $template->render($context);
$template->setStopToken(false);
$template->discard($context);
} else {
$template->setStopToken('else');
$template->discard($context);
$template->setStopToken(false);
$buffer = $template->render($context);
}
return $buffer;
}
}

View File

@ -0,0 +1,68 @@
<?php
/**
* This file is part of Handlebars-php
*
* PHP version 5.3
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars\Helper;
use Handlebars\Context;
use Handlebars\Helper;
use Handlebars\Template;
/**
* The Unless Helper
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
class UnlessHelper implements Helper
{
/**
* Execute the helper
*
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed
*/
public function execute(Template $template, Context $context, $args, $source)
{
$tmp = $context->get($args);
if (!$tmp) {
$template->setStopToken('else');
$buffer = $template->render($context);
$template->setStopToken(false);
} else {
$template->setStopToken('else');
$template->discard();
$template->setStopToken(false);
$buffer = $template->render($context);
}
return $buffer;
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* This file is part of Handlebars-php
*
* PHP version 5.3
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars\Helper;
use Handlebars\Context;
use Handlebars\Helper;
use Handlebars\Template;
/**
* The With Helper
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2014 Authors
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
class WithHelper implements Helper
{
/**
* Execute the helper
*
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed
*/
public function execute(Template $template, Context $context, $args, $source)
{
$context->with($args);
$buffer = $template->render($context);
$context->pop();
return $buffer;
}
}

View File

@ -10,6 +10,7 @@
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2012 (c) ParsPooyesh Co
* @copyright 2013 (c) Behrooz Shabani
* @license MIT <http://opensource.org/licenses/MIT>
@ -26,8 +27,6 @@ namespace Handlebars;
* 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>
@ -79,160 +78,60 @@ class Helpers
*/
protected function addDefaultHelpers()
{
$this->add(
'if',
function ($template, $context, $args, $source) {
/**
* @var $template \Handlebars\Template
* @var $context \Handlebars\Context
* @var $args array
* @var $source string
*/
if (is_numeric($args)) {
$tmp = $args;
} else {
$tmp = $context->get($args);
}
if ($tmp) {
$template->setStopToken('else');
$buffer = $template->render($context);
$template->setStopToken(false);
$template->discard($context);
} else {
$template->setStopToken('else');
$template->discard($context);
$template->setStopToken(false);
$buffer = $template->render($context);
}
return $buffer;
}
);
$this->add(
'each',
function ($template, $context, $args, $source) {
/**
* @var $template \Handlebars\Template
* @var $context \Handlebars\Context
* @var $args array
* @var $source string
*/
$tmp = $context->get($args);
$buffer = '';
if (!$tmp) {
$template->setStopToken('else');
$template->discard();
$template->setStopToken(false);
$buffer = $template->render($context);
} elseif (is_array($tmp) || $tmp instanceof \Traversable) {
$isList = is_array($tmp) && (array_keys($tmp) == range(0, count($tmp) - 1));
$index = 0;
$lastIndex = $isList ? (count($tmp) - 1) : false;
foreach ($tmp as $key => $var) {
$specialVariables = array(
'@index' => $index,
'@first' => ($index === 0),
'@last' => ($index === $lastIndex),
);
if (!$isList) {
$specialVariables['@key'] = $key;
}
$context->pushSpecialVariables($specialVariables);
$context->push($var);
$template->setStopToken('else');
$template->rewind();
$buffer .= $template->render($context);
$context->pop();
$context->popSpecialVariables();
$index++;
}
$template->setStopToken(false);
}
return $buffer;
}
);
$this->add(
'unless',
function ($template, $context, $args, $source) {
/**
* @var $template \Handlebars\Template
* @var $context \Handlebars\Context
* @var $args array
* @var $source string
*/
$tmp = $context->get($args);
if (!$tmp) {
$template->setStopToken('else');
$buffer = $template->render($context);
$template->setStopToken(false);
} else {
$template->setStopToken('else');
$template->discard();
$template->setStopToken(false);
$buffer = $template->render($context);
}
return $buffer;
}
);
$this->add(
'with',
function ($template, $context, $args, $source) {
/**
* @var $template \Handlebars\Template
* @var $context \Handlebars\Context
* @var $args array
* @var $source string
*/
$context->with($args);
$buffer = $template->render($context);
$context->pop();
return $buffer;
}
);
$this->add('if', new Helper\IfHelper());
$this->add('each', new Helper\EachHelper());
$this->add('unless', new Helper\UnlessHelper());
$this->add('with', new Helper\WithHelper());
//Just for compatibility with ember
$this->add(
'bindAttr',
function ($template, $context, $args, $source) {
/**
* @var $template \Handlebars\Template
* @var $context \Handlebars\Context
* @var $args array
* @var $source string
*/
return $args;
}
);
$this->add('bindAttr', new Helper\BindAttrHelper());
}
/**
* Add a new helper to helpers
*
* @param string $name helper name
* @param callable $helper a function as a helper
* @param string $name helper name
* @param mixed $helper a callable or Helper implementation as a helper
*
* @throws \InvalidArgumentException if $helper is not a callable
* @return void
*/
public function add($name, $helper)
{
if (!is_callable($helper)) {
throw new \InvalidArgumentException("$name Helper is not a callable.");
if (!is_callable($helper) && ! $helper instanceof Helper) {
throw new \InvalidArgumentException(
"$name Helper is not a callable or doesn't implement the Helper interface."
);
}
$this->helpers[$name] = $helper;
}
/**
* Calls a helper, whether it be a Closure or Helper instance
*
* @param string $name The name of the helper
* @param \Handlebars\Template $template The template instance
* @param \Handlebars\Context $context The current context
* @param array $args The arguments passed the the helper
* @param string $source The source
*
* @return mixed The helper return value
*/
public function call($name, Template $template, Context $context, $args, $source)
{
if (!$this->has($name)) {
throw new \InvalidArgumentException('Unknown helper: ' . $name);
}
if ($this->helpers[$name] instanceof Helper) {
return $this->helpers[$name]->execute(
$template, $context, $args, $source
);
}
return $this->helpers[$name]($template, $context, $args, $source);
}
/**
* Check if $name helper is available
*
@ -288,7 +187,6 @@ class Helpers
$this->add($name, $helper);
}
/**
* Unset a helper
*
@ -339,5 +237,4 @@ class Helpers
{
return empty($this->helpers);
}
}

View File

@ -12,6 +12,7 @@
* @author Chris Gray <chris.w.gray@gmail.com>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author majortom731 <majortom731@googlemail.com>
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @copyright 2010-2012 (c) Justin Hileman
* @copyright 2012 (c) ParsPooyesh Co
* @copyright 2013 (c) Behrooz Shabani
@ -268,20 +269,14 @@ class Template
} else {
$source = '';
}
$params = array(
$this, //First argument is this template
$context, //Second is current context
$current[Tokenizer::ARGS], //Arguments
$source
);
// subexpression parsing loop
$subexprs = array(); // will contain all subexpressions inside outermost brackets
$inside_of = array( 'single' => false, 'double' => false );
$lvl = 0;
$cur_start = 0;
for ($i=0; $i < strlen($params[2]); $i++) {
$cur = substr($params[2], $i, 1);
for ($i=0; $i < strlen($current[Tokenizer::ARGS]); $i++) {
$cur = substr($current[Tokenizer::ARGS], $i, 1);
if ($cur == "'" ) {
$inside_of['single'] = ! $inside_of['single'];
}
@ -298,7 +293,7 @@ class Template
if ($cur == ')' && ! $inside_of['single'] && ! $inside_of['double']) {
$lvl--;
if ($lvl == 0) {
$subexprs[] = substr($params[2], $cur_start, $i - $cur_start);
$subexprs[] = substr($current[Tokenizer::ARGS], $cur_start, $i - $cur_start);
}
}
@ -320,11 +315,12 @@ class Template
// resolve the node recursively
$resolved = $this->_handlebarsStyleSection($context, $section_node);
// replace original subexpression with result
$params[2] = str_replace('('.$expr.')', $resolved, $params[2]);
$current[Tokenizer::ARGS] = str_replace('('.$expr.')', $resolved, $current[Tokenizer::ARGS]);
}
}
$return = call_user_func_array($helpers->$sectionName, $params);
$return = $helpers->call($sectionName, $this, $context, $current[Tokenizer::ARGS], $source);
if ($return instanceof String) {
return $this->handlebars->loadString($return)->render($context);
} else {