Skip to content

Documented how to create lazy-loaded Twig extensions #9204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 101 additions & 1 deletion templating/twig_extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,114 @@ Create a class that extends ``\Twig_Extension`` and fill in the logic::
`global variables`_.

Register an Extension as a Service
----------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Next, register your class as a service and tag it with ``twig.extension``. If you're
using the :ref:`default services.yml configuration <service-container-services-load-example>`,
you're done! Symfony will automatically know about your new service and add the tag.

You can now start using your filter in any Twig template.

Creating Lazy-Loaded Twig Extensions
------------------------------------

.. versionadded:: 1.26
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we refer to Symfony versions here? Having a Twig version seems weird to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's not very common, but we add those version numbers for Twig and other apps different than Symfony in some places of the docs.

Support for lazy-loaded extensions was introduced in Twig 1.26.

Including the code of the custom filters/functions in the Twig extension class
is the simplest way to create extensions. However, Twig must initialize all
extensions before rendering any template, even if the template doesn't use an
extension. This means that performance can be slow if you define extensions with
lots of complex dependencies (e.g. those making database connections).

That's why Twig allows to decouple the extension definition from its
implementation. Following the same example as before, the first change would be
to remove the ``priceFilter()`` method from the extension and update the PHP
callable defined in ``getFilters()``::

// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;

use AppBundle\Twig\AppRuntime;

class AppExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
// the logic of this filter is now implemented in a different class
new \Twig_SimpleFilter('price', array(AppRuntime::class, 'priceFilter')),
);
}
}

Then, create the new ``AppRuntime`` class (it's not required but these classes
are suffixed with ``Runtime`` by convention) and include the logic of the
previous ``priceFilter()`` method::

// src/AppBundle/Twig/AppRuntime.php
namespace AppBundle\Twig;

class AppRuntime
{
// for complex Twig extensions you may need to define a constructor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would even say that if you don't have dependencies, you should never create a runtime.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I've reworded some things. Thanks!

// in this class to inject external services

public function priceFilter($number, $decimals = 0, $decPoint = '.', $thousandsSep = ',')
{
$price = number_format($number, $decimals, $decPoint, $thousandsSep);
$price = '$'.$price;

return $price;
}
}

Register the Lazy-Loaded Extension as a Service
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Finally, register your new class as a service and tag it with ``twig.runtime``
(and optionally inject any service needed by the Twig extension runtime):

.. configuration-block::

.. code-block:: yaml

# app/config/services.yml
services:
app.twig_runtime:
class: AppBundle\Twig\AppRuntime
public: false
tags:
- { name: twig.runtime }

.. code-block:: xml

<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="app.twig_runtime"
class="AppBundle\Twig\AppRuntime"
public="false">
<tag name="twig.runtime" />
</service>
</services>
</container>

.. code-block:: php

// app/config/services.php
use AppBundle\Twig\AppExtension;

$container
->register('app.twig_runtime', AppRuntime::class)
->setPublic(false)
->addTag('twig.runtime');

.. _`Twig extensions documentation`: http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension
.. _`global variables`: http://twig.sensiolabs.org/doc/advanced.html#id1
.. _`functions`: http://twig.sensiolabs.org/doc/advanced.html#id2
Expand Down