Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Twig_SimpleFunction modify the context?

Is it possible to modify the current twig context by call a Twig_SimpleFunction?

I have the below function registered:

<?php
namespace Craft;

class TwiggedTwigExtension extends \Twig_Extension
{
    public function getName()
    {
      return 'Twigged';
    }

    public function getFunctions()
    {
        return array(
            'setContextVar' => new \Twig_SimpleFunction('setContextVar', array($this, 'setContextVar'), array('needs_context' => true)),
        );
    }

    public function setContextVar($context, $str, $val)
    {
        $context['context'][$str] = $val;

        var_dump(array_keys($context['context']));
    }
}

When called from a template like so {{ setContextVar('hellow', 'world') }}, the var_dump shows the modified context. But a quick check in the template like so {{ dump(_context|keys) }} does not show the modified context.

Am I going about this the wrong way?

like image 910
Fyrebase Avatar asked Sep 11 '15 13:09

Fyrebase


2 Answers

This is not possible with functions because the context is not passed by reference.

In your extension, you are even accessing $context['context'], which would mean a variable named context, not the context itself (_context is a special variable name used to access the context in the template, but it does not appear in the context array sent to function as it is the array itself).

There might be a way to perform this with a custom compilation of the function by changing the node class. but I have not tried it and it may end up a bit hard to maintain the expression semantic.
And anyway, I would recommend against writing such function. Assigning a variable in Twig does not have a function semantic and cannot be done as part of an expression (functions are usable in expressions of course). Changing this semantic may lead to weird behaviours.

like image 86
Christophe Coevoet Avatar answered Oct 23 '22 22:10

Christophe Coevoet


Like @DarkBee mentioned in his comment, you can modify the context by passing it by reference:

function setContextVar(&$context, ...)

However, adding just a function to Twig doesn't seem to work (I'm using Twig 2.4.4):

$twig->addFunction(new Twig_Function('setContextVar', function(&$context, $name, $value) {
    $context[$name] = $value;
}, ['needs_context' => true]));

When you use the function in a Twig file, the context doesn't get modified, and you get this warning:

Warning: Parameter 1 to {closure}() expected to be a reference, value given in C:\...\vendor\twig\twig\lib\Twig\Environment.php(378) : eval()'d code on line ...

Instead, you need to create a Twig extension:

$twig->addExtension(new class extends Twig_Extension {
    public function getFunctions() {
        return [
            new Twig_Function('setContextVar', [$this, 'setContextVar'], ['needs_context' => true]),
        ];
    }

    public function setContextVar(&$context, $name, $value) {
        $context[$name] = $value;
    }
});

Then you can use it in Twig without getting a warning:

{{ dump() }}
{% do setContextVar('foo', 'bar') %}
{{ dump() }}

The above prints e.g.:

array(0) {
}

array(1) {
  ["foo"]=>
  string(3) "bar"
}
like image 5
Matias Kinnunen Avatar answered Oct 24 '22 00:10

Matias Kinnunen