Magic Methods

Justin Hileman edited this page Apr 15, 2014 · 4 revisions

Mustache.php supports PHP magic methods. Specifically, it supports:

  • __toString
  • __isset and __get
  • __invoke

__toString

Whenever an object or value is interpolated in Mustache, it is coerced into a string. This means, if you want to have control over how your object shows up inside {{ myInstance }} tags, you'll want to implement the __toString method.

Note that you must return a string from __toString. If you return anything else, you'll get anything from an error message in the middle of your template, to a PHP warning, to a full blown meltdown, depending on your current PHP settings.

__isset and __get

These two come in pairs. If you want to use __get magic accessors inside Mustache, you must implement __isset as well. This is just good practice, in general, but it's essential for Mustache to prevent objects from masking other properties and objects higher on the context stack.

__invoke

In PHP 5.3 or newer, objects which implement __invoke are considered "callable", and can be used as higher-order sections.

Note that __call is missing

Unfortunately, PHP lacks an equivalent to __isset for __call. Short of trying to call a method on an object, there is no way to tell whether it will handle the call. This is fundamentally incompatible with the Mustache context stack. Take this, for example:

{{# foo }}{{ label }}: {{ name }}{{/ foo }}

And the ViewModel:

<?php
class Foo {
    public $name;
    public function __call($method, $args) {
        return 'unknown value';
    }
}

And the data:

<?php
$foo = new Foo;
$foo->name = 'Bob';
$tpl->render(array(
  'label' => 'name',
  'foo'   => $foo,
));

This will render as:

unknown value: unknown value

This is because inside the {{# foo }} section, the __call method on Foo would mask the {{ label }} lookup higher in the context stack, returning unknown value instead of name. Because methods win over properties, it would even do the same for Bob's $name property. This is obviously not what you would expect.

The good news is that you can use __isset and __get instead, just like you would normally use __call :)