bobthecow / mustache.php

A Mustache implementation in PHP.
http://mustache.github.io/
MIT License
3.23k stars 418 forks source link

Binding $this to context in function #309

Open czirkoszoltan opened 7 years ago

czirkoszoltan commented 7 years ago

In mustache.js, when the rendered value is a function, it is called with this bound to the current context, and the return value of the function is htmlescaped as well. So with this template:

<ul>
{{#people}}
    <li> Hello <button onclick="{{onclick}}">{{name}}</button>!
{{/people}}
</ul>

and this data:

  var data = {
      onclick: function () {
          return "alert("+JSON.stringify("ID is " + this.id)+")";
      },
      people: [
        { name: "Alice", id: 1 },
        { name: "Bob", id: 2 },
      ],
  };

the names become clickable, and clicking Alice produces alert "ID is 1", clicking Bob produces alert "ID is 2". Mustache.php can work with classes (the class Chris example on the intro page), but it cannot work with lambdas like this. This problem could easily be solved, by changing Template::resolveValue, the line

    ->loadLambda((string) call_user_func($value))

to

    ->loadLambda((string) call_user_func($value->bindTo($context)))

(of course, first checking if the callable is a string) or maybe by changing the line to

    ->loadLambda((string) call_user_func($value, $context))

to retain compatibility. (Currently callables are expected to have no parameters, so it's no problem in PHP, adding an extra parameter.) The latter version would allow this:

/* the template is exactly the same */
$template = <<<'EOT'
<ul>
{{#guys}}
    <li> Hello <button onclick="{{onclick}}">{{name}}</button>!
{{/guys}}
</ul>
EOT;

/* the data is the same, the lambda is almost the same as in js, changed this.id to context->find('id') */
$data = [
    'onclick' => function($context) { return $context->find('id'); },
    'guys' => [
        [ 'name'=>"Alice", 'id' => 1 ],
        [ 'name'=>"Bob", 'id' => 2 ],
    ],
];

$m = new Mustache_Engine;
echo $m->render($template, $data);

So lambda function could be used as simply as named classes.

mAAdhaTTah commented 7 years ago

You would have to drop support for PHP5.3 to do this.

czirkoszoltan commented 7 years ago

Not for the latter version.