Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 29 additions & 28 deletions src/main/php/com/handlebarsjs/BlockHelpers.class.php
Original file line number Diff line number Diff line change
@@ -1,48 +1,49 @@
<?php namespace com\handlebarsjs;

use lang\XPClass;

/**
* Block helpers factory
*/
/** Block helpers factory */
class BlockHelpers {
private static $byName;
private $impl;

static function __static() {
self::$byName= [
'if' => new XPClass(IfBlockHelper::class),
'unless' => new XPClass(UnlessBlockHelper::class),
'with' => new XPClass(WithBlockHelper::class),
'each' => new XPClass(EachBlockHelper::class),
'>' => new XPClass(PartialBlockHelper::class)
];
/** @param [:string] $impl */
public function __construct($impl) {
$this->impl= $impl;
}

/**
* Gets a block helper class by a given name
* Register a named implementation
*
* @param string $name
* @return ?lang.XPClass
* @param ?string $impl
* @return self
*/
public static function named($name) {
return self::$byName[$name] ?? null;
public function register($name, $impl) {
if (null === $impl) {
unset($this->impl[$name]);
} else {
$this->impl[$name]= $impl;
}
return $this;
}

/**
* Creates a new with block helper
*
* @param string $name
* @param string[] $options
* @param com.github.mustache.NodeList $fn
* @param com.github.mustache.NodeList $inverse
* @param string $start
* @param string $end
* - Creates instances of named block implementations
* - Registers `*inline` partials in top-level nodes
* - Uses default block implementation otherwise
*
* @param var[] $options
* @param com.github.ParseState $state
* @return com.github.mustache.Node
*/
public static function newInstance($name, $options, $fn, $inverse, $start, $end) {
if (isset(self::$byName[$name])) {
return self::$byName[$name]->newInstance($options, $fn, $inverse, $start, $end);
public function newInstance($options, $state) {
$name= array_shift($options);
if ($impl= $this->impl[$name] ?? null) {
return $state->target->add(new $impl($options, null, null, $state->start, $state->end));
} else if ('*inline' === $name) {
return new BlockNode('inline', $options, $state->parents[0]->declare($options[0] ?? null));
} else {
return new BlockNode($name, $options, $fn, $inverse, $start, $end);
return $state->target->add(new BlockNode($name, $options, null, null, $state->start, $state->end));
}
}
}
20 changes: 10 additions & 10 deletions src/main/php/com/handlebarsjs/BlockNode.class.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace com\handlebarsjs;

use com\github\mustache\Node;
use com\github\mustache\{Node, NodeList};
use util\Objects;

/**
Expand All @@ -20,17 +20,17 @@ class BlockNode extends Node {
* Creates a new section node
*
* @param string $name
* @param string[] $options
* @param com.handlebarsjs.Nodes $fn
* @param com.handlebarsjs.Nodes $inverse
* @param var[] $options
* @param com.github.mustache.NodeList $fn
* @param com.github.mustache.NodeList $inverse
* @param string $start
* @param string $end
*/
public function __construct($name, $options= [], Nodes $fn= null, Nodes $inverse= null, $start= '{{', $end= '}}') {
public function __construct($name, $options= [], NodeList $fn= null, NodeList $inverse= null, $start= '{{', $end= '}}') {
$this->name= $name;
$this->options= $options;
$this->fn= $fn ?: new Nodes();
$this->inverse= $inverse ?: new Nodes();
$this->fn= $fn ?? new NodeList();
$this->inverse= $inverse ?? new NodeList();
$this->start= $start;
$this->end= $end;
}
Expand All @@ -47,7 +47,7 @@ public function name() {
/**
* Returns fn
*
* @return com.handlebarsjs.Nodes
* @return com.github.mustache.NodeList
*/
public function fn() {
return $this->fn;
Expand All @@ -56,7 +56,7 @@ public function fn() {
/**
* Returns inverse
*
* @return com.handlebarsjs.Nodes
* @return com.github.mustache.NodeList
*/
public function inverse() {
return $this->inverse;
Expand All @@ -65,7 +65,7 @@ public function inverse() {
/**
* Returns options passed to this section
*
* @return string[]
* @return var[]
*/
public function options() {
return $this->options;
Expand Down
47 changes: 0 additions & 47 deletions src/main/php/com/handlebarsjs/Decoration.class.php

This file was deleted.

3 changes: 0 additions & 3 deletions src/main/php/com/handlebarsjs/HandlebarsEngine.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ static function __static() {
self::$builtin= [
'lookup' => function($node, $context, $options) {
return $options[0][$options[1]] ?? null;
},
'*inline' => function($node, $context, $options) {
$context->engine->templates->register($options[0]($node, $context, []), $node);
}
];
}
Expand Down
32 changes: 14 additions & 18 deletions src/main/php/com/handlebarsjs/HandlebarsParser.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
/**
* Parses handlebars templates
*
* @test xp://com.handlebarsjs.unittest.ParsingTest
* @test xp://com.handlebarsjs.unittest.SubexpressionsTest
* @test com.handlebarsjs.unittest.ParsingTest
* @test com.handlebarsjs.unittest.SubexpressionsTest
* @see https://github.com/wycats/handlebars.js/blob/master/spec/parser.js
*/
class HandlebarsParser extends AbstractMustacheParser {
public $blocks;

/**
* Tokenize name and options from a given tag, e.g.:
Expand Down Expand Up @@ -103,29 +104,24 @@ public function options($tag) {

/**
* Initialize this parser.
*
* @return void
*/
protected function initialize() {
$this->blocks= new BlockHelpers([
'if' => IfBlockHelper::class,
'unless' => UnlessBlockHelper::class,
'with' => WithBlockHelper::class,
'each' => EachBlockHelper::class,
'>' => PartialBlockHelper::class,
]);

// Sections
$this->withHandler('#', true, function($tag, $state, $parse) {
$parsed= $parse->options(trim(substr($tag, 1)));
$name= array_shift($parsed);
$state->parents[]= $state->target;

if ('*' === $name[0]) {
$block= $state->target->decorate(new Decoration($name, $parsed));
} else {
$block= $state->target->add(BlockHelpers::newInstance(
$name,
$parsed,
null,
null,
$state->start,
$state->end
));
}
$state->parents[]= $block;
$block= $this->blocks->newInstance($parse->options(trim(substr($tag, 1))), $state);
$state->target= $block->fn();
$state->parents[]= $block;
});
$this->withHandler('/', true, function($tag, $state) {
$name= trim(substr($tag, 1));
Expand Down
63 changes: 39 additions & 24 deletions src/main/php/com/handlebarsjs/Nodes.class.php
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
<?php namespace com\handlebarsjs;

use com\github\mustache\NodeList;
use lang\IllegalArgumentException;

class Nodes extends NodeList {
private $decorations= [];
private $partials= [];

/**
* Add a decoration
* Declares a partial and returns the nodes associated with the name.
*
* @param com.handlebarsjs.Decoration $decoration
* @return com.handlebarsjs.Decoration
*/
public function decorate($decoration) {
$this->decorations[]= $decoration;
return $decoration;
}

/**
* Evaluates decorators
*
* @param com.github.mustache.Context $context the rendering context
* @return void
* @param string|com.handlebarsjs.Quoted $partial
* @param ?com.github.mustache.NodeList $nodes
* @return com.github.mustache.NodeList
* @throws lang.IllegalArgumentException
*/
public function enter($context) {
foreach ($this->decorations as $decoration) {
$decoration->enter($context);
public function declare($partial, $nodes= null) {
if ($partial instanceof Quoted) {
$name= $partial->chars;
} else if (is_string($partial)) {
$name= $partial;
} else {
throw new IllegalArgumentException('Partial names must be strings or Quoted instances, have '.typeof($partial));
}

return $this->partials[$name]= $nodes ?? new NodeList();
}

/** @return [:com.github.mustache.NodeList] */
public function partials() { return $this->partials; }

/**
* Returns block without decorators
* Returns a partial with a given name, or NULL if this partial does
* not exist.
*
* @return com.github.mustache.NodeList
* @param string $partial
* @return ?com.github.mustache.NodeList
*/
public function block() { return new NodeList($this->nodes); }
public function partial($partial) {
return $this->partials[$partial] ?? null;
}

/**
* Evaluates this node
Expand All @@ -42,9 +47,19 @@ public function block() { return new NodeList($this->nodes); }
* @param io.streams.OutputStream $out
*/
public function write($context, $out) {
foreach ($this->decorations as $decoration) {
$decoration->enter($context);
$templates= $context->engine->templates();
$previous= [];
foreach ($this->partials as $name => $partial) {
$previous[$name]= $templates->register($name, $partial);
}

// Restore partials to previous state after processing this template
try {
parent::write($context, $out);
} finally {
foreach ($previous as $name => $partial) {
$templates->register($name, $partial);
}
}
parent::write($context, $out);
}
}
4 changes: 1 addition & 3 deletions src/main/php/com/handlebarsjs/PartialBlockHelper.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ public function write($context, $out) {

$source= $templates->source($this->name);
if ($source->exists()) {
$this->fn->enter($context);

$template= $context->engine->load($this->name, $this->start, $this->end, '');
$previous= $templates->register('@partial-block', $this->fn->block());
$previous= $templates->register('@partial-block', $this->fn);
try {
$context->engine->write($template, $context, $out);
} finally {
Expand Down
2 changes: 1 addition & 1 deletion src/main/php/com/handlebarsjs/Quoted.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use lang\Value;

class Quoted implements Value {
protected $chars;
public $chars;

/**
* Creates a new Quoted instance
Expand Down
Loading