Added registerHelper

I did it in such a way where I’m not messing with the other methods or
classes to get what I wanted.

I realize that there maybe no need for the child context, however when
trying a permutation of

```
'fn' => function($data = null) use($context, $template) {
    $context->push($context->last());

    if(is_array($data)) {
   		$context->push($data);
   	}

    $template->setStopToken('else');
    $buffer = $template->render($context);
    $template->setStopToken(false);
    $template->discard($context);

    if(is_array($data)) {
   		$context->pop();
   	}

    $context->pop();

    return $buffer;
}
```
It didn’t parse the `../../../test` correctly in the test. I figured
that the ChildContext is a nice pattern overall and doesn’t interfere
with the rest of the package anyways…
This commit is contained in:
Christian Blanquera 2015-09-21 17:50:00 +08:00
parent 57d00e6e82
commit 33c76738b4
2 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1,85 @@
<?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>
* @author Behrooz Shabani <everplays@gmail.com>
* @author Chris Gray <chris.w.gray@gmail.com>
* @author Ulrik Lystbaek <ulrik@bettertaste.dk>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @author Christian Blanquera <cblanquera@openovate.com>
* @copyright 2010-2012 (c) Justin Hileman
* @copyright 2012 (c) ParsPooyesh Co
* @copyright 2013 (c) Behrooz Shabani
* @copyright 2013 (c) f0ruD A
* @license MIT <http://opensource.org/licenses/MIT>
* @version GIT: $Id$
* @link http://xamin.ir
*/
namespace Handlebars;
/**
* Handlebars context
* Context for a template
*
* @category Xamin
* @package Handlebars
* @author fzerorubigd <fzerorubigd@gmail.com>
* @author Behrooz Shabani <everplays@gmail.com>
* @copyright 2010-2012 (c) Justin Hileman
* @copyright 2012 (c) ParsPooyesh Co
* @license MIT <http://opensource.org/licenses/MIT>
* @version Release: @package_version@
* @link http://xamin.ir
*/
class ChildContext extends Context
{
protected $parentContext = null;
/**
* Sets a parent context in which
* we will case for the ../ in get()
*
* @param Context
* @return void
*/
public function setParent(Context $parent) {
$this->parentContext = $parent;
}
/**
* Get a available from current context
* Supported types :
* variable , ../variable , variable.variable , variable.[variable] , .
*
* @param string $variableName variable name to get from current context
* @param boolean $strict strict search? if not found then throw exception
*
* @throws \InvalidArgumentException in strict mode and variable not found
* @throws \RuntimeException if supplied argument is a malformed quoted string
* @throws \InvalidArgumentException if variable name is invalid
* @return mixed
*/
public function get($variableName, $strict = false)
{
//if the variable name starts with a ../
//and we have a parent
if(strpos($variableName, '../') === 0 && $this->parentContext instanceof Context) {
//just remove the first ../
$variableName = substr($variableName, 3);
//and let the parent context handle the rest
return $this->parentContext->get($variableName, $strict);
}
//otherwise, it's business as usual
return parent::get($variableName, $strict);
}
}

View File

@ -235,6 +235,111 @@ class Handlebars
{
return $this->getHelpers()->has($name);
}
/**
* Add a new helper.
*
* @param string $name helper name
* @param mixed $helper helper callable
*
* @return void
*/
public function registerHelper($name, $helper)
{
$this->addHelper($name, function($template, $context, $arg) use ($helper)
{
$args = $template->parseArguments($arg);
$named = $template->parseNamedArguments($arg);
foreach($args as $i => $arg) {
//if it's literally string
if($arg instanceof StringWrapper) {
//we have no problems here
$args[$i] = (string) $arg;
continue;
}
//not sure what to do if it's not a string or StringWrapper
if(!is_string($arg)) {
continue;
}
//it's a variable and we need to figure out the value of it
$args[$i] = $context->get($arg);
}
//push the options
$args[] = array(
//special fields
'data' => array(
'index' => $context->get('@index'),
'key' => $context->get('@key'),
'first' => $context->get('@first'),
'last' => $context->get('@last')),
// Named arguments
'hash' => $named,
// A renderer for block helper
'fn' => function($inContext = null) use($context, $template)
{
$defined = !!$inContext;
if(!$defined) {
$inContext = $context;
$inContext->push($inContext->last());
} else if (!$inContext instanceof Context) {
$inContext = new ChildContext($inContext);
$inContext->setParent($context);
}
$template->setStopToken('else');
$buffer = $template->render($inContext);
$template->setStopToken(false);
//what if it's a loop ?
$template->rewind();
//What's the point of this again?
//I mean in this context (literally)
//$template->discard($inContext);
if($defined) {
$inContext->pop();
}
return $buffer;
},
// A render for the else block
'inverse' => function($inContext = null) use($context, $template)
{
$defined = !!$inContext;
if(!$defined) {
$inContext = $context;
$inContext->push($inContext->last());
} else if (!$inContext instanceof Context) {
$inContext = new ChildContext($inContext);
$inContext->setParent($context);
}
$template->setStopToken('else');
$template->discard($inContext);
$template->setStopToken(false);
$buffer = $template->render($inContext);
if($defined) {
$inContext->pop();
}
return $buffer;
},
// The current context.
'context' => $context,
// The current template
'template' => $template);
return call_user_func_array($helper, $args);
});
}
/**
* Remove a helper by name.