pgr
pgr

Reputation: 928

Extending Twig, are custom handlers for the dot operator (getAttribute) possible?

Using Twig 3.0, I was wondering if it is possible to create Twig extensions that intervene when the dot operator is being processed?

So, suppose I have this expression

{{ myRecord.myField }}

and it won't work even though myRecord is a known thing, because it doesn't have a field called myField.

But I would want Twig to run my own custom extension code to figure out what to use for .myField, is that possible? How?

I know this can be achieved with different notations using a function or filter (e.g. myRecord|getCustomField(myField)), but I want to keep the dot notation because I feel it's particularly adequate for my purposes.

Upvotes: 2

Views: 110

Answers (1)

soulseekah
soulseekah

Reputation: 9162

The CoreExtension supports get*, is*, has* method lookups for the dot.

https://github.com/twigphp/Twig/blob/3.x/src/Extension/CoreExtension.php#L1563

So if your class has a myRecord::getMyField method you can access it via myRecord.myField in Twig.

Another option is to use the __call magic method: https://www.php.net/manual/en/language.oop5.overloading.php#object.call

The more complex and flexible approach that doesn't require altering the myRecord class would involve writing a custom Extension to replace nodes. Here's a simple example that you can modify to create MethodCall nodes on the class, for example.

$twig->addExtension(new class extends Twig\Extension\AbstractExtension {
      public function getNodeVisitors(): array {
          return [
              new class implements Twig\NodeVisitor\NodeVisitorInterface {
                  public function enterNode(Node $node, Environment $env): Node {
                      return $node;
                  }

                  public function leaveNode(Node $node, Environment $env): ?Node {
                      if (!$node instanceof Twig\Node\Expression\GetAttrExpression) {
                          return $node;
                      }

                      if ('myRecord' !== $node->getNode('node')->getAttribute('name')) {
                          return $node;
                      }

                      // Custom lookup logic goes here...
                      $attribute = $node->getNode('attribute')->getAttribute('value');

                      $node->setNode('attribute', new Twig\Node\Expression\ConstantExpression('get_' . $attribute, 0));

                      return $node;
                  }

                  public function getPriority(): int {
                      return 1;
                  }
              }
          ];
      }
  });

Hope this helps.

Upvotes: 0

Related Questions