Twig plugin (Kirby 2.3+)

So I started working a plugin which enables working with Twig templates. It requires Kirby’s 2.3.0 beta.

Code and documentation here:

Compatibility

  • Kirby’s chaining API for pages etc. is quite compatible with Twig
  • Page Models and Controllers should be fully compatible (haven’t checked yet but no reason why it wouldn’t work)
  • Helper functions can be exposed to the templates (e.g. {{ snippet('nav') }}) but we need to list them explicitly, which I’m a bit reluctant to do but hey. (Update: so I did that, making a list of helper functions for Kirby/Kirby Toolkit which seem relevant to templating. Not fully documented yet.)
  • I’m subclassing the Kirby\Component\Template component, so if there are bugfixes in Kirby’s code for this component the bug fixes won’t be picked up. And if its (small) API changes, the plugin will break your site. :grinning:

Performance

I’ve just used the <?php echo memory(); ?> / {{ memory() }} helper to print memory usage in equivalent PHP and Twig templates (both querying and listing all pages in a small site, with a bit more than 100 pages).

  • Empty template: PHP 2.86 MB, Twig 4.74 MB
  • Listing all pages: PHP 5.4 MB, Twig 7.8 MB

So there’s an overhead, but it seems to be rather linear (+ 2MB) and not proportional (or not by much).

Of course that’s probably a very limited and imperfect test.

3 Likes

Need feedback: exposing custom functions?

With Kirby’s default PHP templating, you can access all of PHP and also any function defined in a plugin or in a PHP file you import.

With Twig, you’re limited to what Twig provides and to variables, objects and functions provided to the Twig environment. In the plugin I’m providing the page, pages, site and kirby objects, as well as some of Kirby’s helpers and functions. This leaves two open questions:

  1. Which functions from Kirby’s helpers or the Kirby Toolkit should be available in Twig templates? Right now I’m explicitly listing Kirby helpers to expose to Twig: https://github.com/fvsch/kirby-twig/blob/master/twig.php#L36. I’m also not exposing the Toolkit’s classes at all.

  2. Functions from plugins are not exposed at all. I’m wondering if I should expose them, and how. I don’t think I can automatically expose them all (I don’t know how I would get a list, and there might be conflicts with built-in Twig functions). I could provide a way for other plugins to register their functions, though.

Any thoughts?

Updated to 1.2

  • new feature: in debug mode, Twig errors will now render a nice error page with a readable message and the relevant lines from the failing Twig template
  • many small bug fixes (and probably introduced some bugs ^^)
  • now using a namespace: Kirby\Plugin\Twig (new developer guide doc recommends using a namespace but doesn’t recommend a naming convention)
  • undefined variables will raise an error in debug mode but not in production
  • added config() as an alias to c::get() (since you can’t use static methods of a class in Twig, and getting — not setting — config values seemed useful)

Version number: for some reason I had versioned it as 1.1 then bumped to 1.2 because I forgot that previous change. That’s what I get for not having a changelog. Also adding a namespace is a breaking change but I don’t suspect people were using the class directly so I didn’t bump to 2.0 just for that.

Limitations are not so bad

Unlike PHP templates, Twig doesn’t have access to all the functions and classes in the PHP script’s scope. This means that if you write a plugin file to expose a simple function to your templates… sorry, doesn’t work. Want to use the Kirby Toolkit in your Twig templates? Sorry, doesn’t work either.

I converted my personal site to Twig templates. It had legacy Kirby 1.x and 2.0 stuff all over the place, so I wasn’t sure it was going to work. Turns out it wasn’t a problem.

  • I replaced my custom filtering function with a Page method.
  • Most of my templates had 10-20 lines of PHP at the start to fetch or prepare content. I moved that to controllers.
  • I wrote one or two Page methods, and a Site method as well to replace another utility function.

All in all, Twig’s limitations forced me to make better use of Kirby’s tools for separating concerns and moving logic out of the templates.

I’m still considering a way to tell the Twig plugin to expose some custom functions, and considering whether I should expose bits of the toolkit or even all of it to Twig templates.

For the toolkit I could expose a function that allow instantiating any toolkit class, like {{ toolkit('c').get('something', 'fallback value') }}, but frankly I’m not sure that’s useful. Like 90% of the toolkit is better suited for use in Page methods, controllers, plugins etc.

So maybe just a way to register a custom function or class and expose it to templates.

1 Like

Updates:

1 Like

Great job! Can’t wait to try it out. The documentation is really helpful thanks for taking the time.

1 Like

Updated to 2.0

Release notes:

Awesome!, Just what I was waiting for!

I Have two questions:

  1. Could you explain how I can call a static method on a twig template?

     c::set('twig.env.functions', ['str::email']);
    

    It is so?

     {{ str::email(...) }}
    
  2. It is possible to use twig for a snippets or this is only limited to the templates?

Hi and sorry for replying late.

Answer to the first question is here in the doc. Your config is okay, but the syntax when using the declared function should be {{ str__email(...) }}.

For the record, I think that if you’re sending an email on a page request you should probably manage that in a controller (site/controllers/templatename.php) and not in the template (site/templates/templatename.php or site/templates/templatename.twig). Even if not using Twig templates, it’s probably better to separate concerns this way.

It is possible to use twig for a snippets or this is only limited to the templates?

The snippet() function is Kirby’s function so snippet('test') will load site/snippets/test.php. Snippets can’t use Twig syntax. But Twig includes basically work in the same way:

{% include 'blocks/test.twig' with {var1:'lol', var2:'what'} %}

will load site/templates/blocks/test.twig.

Personally I stopped using snippets and just use the site/templates folder and some subfolders if I have many parts and want to be organized. Also template extending is great, so if you’re doing things like this:

{% include 'blocks/header.twig' %}

Page content

{% include 'blocks/footer.twig' %}

… that’s not the Twig way and it can be improved quite a bit. :slight_smile:

1 Like

Great!, thanks for the acceleration.

Yep, I love use controller too, I just was curious of how to use the helpers directly in the .twig files .

Updated to 3.0.0

  • A new twig() helper function allows rendering a template file or a template string from PHP.
  • Flexible configuration with twig.function.* config keys that allow exposing any number of functions to Twig templates from config, plugins, etc.
  • Improved error handling.
  • Much of the documentation has been rewritten.

There are some breaking changes too, so if you’re already using this plugin, make sure that you’ve read about them in the full changelog.

1 Like

As a side note, in 3.0+ and some earlier versions of this plugin, you can use the @snippets namespace to include Twig templates from the site/snippets folder:

{% include '@snippets/mysnippet.twig' %}

You can also define your own namespaces (which point to a folder) in your configuration (Kirby Twig 3.0+):

c::set('twig.namespace.foo', kirby()->roots()->site().'/bar');
1 Like

Hi,
is it possible to get the (PanelBar Plugin) working with the twig engine?

The usual way is to include the call in in your php-template:
<?php snippet('plugin.panelBar) ?>

I tried by adding the following to my config
c::set([‘twig.function.panelBar’ => panelBar]);

and calling with it in the template by
{{ panelBar }}

This doesn’t work. Not sure it its possible anyway to include plugins or more complex code in that way. I am not very skilled in respect of the php/backend stuff.

Kind regards
wolle

[quote=“wolle, post:14, topic:3823”]
The usual way is to include the call in in your php-template:
<?php snippet('plugin.panelBar) ?>[/quote]
You can use the snippet function as well in your Twig templates:

{{ snippet('plugin.panelBar') }}

If panelBar is a PHP function, you can indeed make it available in Twig templates, but you will need to use the function’s name as a string:

c::set('twig.function.panelBar', 'panelBar');

Then you can call the function in your templates:

{{ panelBar() }}

But I’m not seeing a panelBar function in the plugin’s documentation, so I don’t think this would work. The correct way to include the panelBar HTML, CSS and JS code seems to be using the snippet function.

1 Like
{{ snippet('plugin.panelBar') }}

does the job, didn’t expect it was that easy.
Thank you very much!