zordius / lightncandy

An extremely fast PHP implementation of handlebars ( http://handlebarsjs.com/ ) and mustache ( http://mustache.github.io/ ),
https://zordius.github.io/HandlebarsCookbook/
MIT License
610 stars 76 forks source link

fix: Pass input data of current scope to {{else}} of {{#each}} #369

Open eliashaeussler opened 7 months ago

eliashaeussler commented 7 months ago

Problem

Given the following Handlebars template:

{{#each paragraphs}}
    <p>{{this}}</p>
{{else}}
    <p class="empty">{{foo}}</p>
{{/each}}

Given the following context:

{
    "foo": "baz"
}

When rendering the template with this context, {{else}} is being rendered because paragraphs is missing in the context. With native handlebars.js, this generates the following result:

<p class="empty">baz</p>

👉 For reference, have a look at the playground on handlebarsjs.com.

However, when rendering the same template and context with lightncandy, the result is:

<p class="empty"></p>

👉 For reference, create and run the following PHP script:

require_once __DIR__ . '/vendor/autoload.php';

$template = <<<HBS
{{#each paragraphs}}
    <p>{{this}}</p>
{{else}}
    <p class="empty">{{foo}}</p>
{{/each}}
HBS;

$compileResult = \LightnCandy\LightnCandy::compile($template, [
    'flags' => \LightnCandy\LightnCandy::FLAG_HANDLEBARS,
]);
$fn = \LightnCandy\LightnCandy::prepare($compileResult);

echo $fn([
    'foo' => 'baz',
]);

And check the output:

$ php test.php
<p class="empty"></p>

Solution

This PR fixes the issue by passing input data of the current scope to the else callback in Runtime::sec(). In addition, a test case is added which covers the above scenario.