This project should help to find a way for a general Template Render Interface. And show an integration of different Template Engines into all common PHP frameworks.
Discussion in the PHP-FIG:
Target is that Content Management Systems like Sulu CMS or even Typo3
but also any library which a project defines how the data is rendered can use the
TemplateRendererInterface
to render their content to give 100%
freedom about the used Template engine to their projects and easier to implement this kind of libraries
into different PHP frameworks.
- Motivation
- Different to exist abstractions
- Project Status
- Example Applications
- Usage
- Package List
- Analyses
- Tooling
Why a TemplateRendererInterface?
Somebody mostly don't see a lot of effort what a TemplateRendererInterface
can do for the PHP ecosystem.
While most Frameworks has a goto Template Engine, there are also some Frameworks not provided with
a specific one and want to support multiple (e.g.: Mezzio
, Laminas
, Yii
, ...). A TemplateRendererInterface
can help it make the integration of every Template engine easier. And even allow to easier upgrade Legacy
projects to newer Frameworks as not the whole templates need to be migrated. But also major frameworks like
Symfony
and Laravel
could benefit from supporting a more secure Template engine like Latte
.
But not only Framework can benefit from a general TemplateRendererInterface, a common interface also
allows CMS like Sulu CMS
, Typo3
, ...
and more allow to give the Frontend Developer the full freedom how to write their Templates.
While working on Sulu CMS and working alot with Hexagonal Architecture from DDD.
I was thinking that the PHP-FIG is missing a template abstraction for PHP template engines. So system can
provide controllers which provide data and projects define how that data should be rendered with the template
engine of there choice. Specially as CMS today allow to provide Content via JSON and render a website via React,
Angular, Vue, ... they should also be free to render their website via Twig
, Blade
, Latte
, ... or whatever
Template engine they prefer.
Beside Frameworks and CMSs a general TemplateRendererInterface also helps Library Authors to integrate their
library easier into different Frameworks. E.g. a mail library like symfony/mailer
which provides a
TemplatedEmail
class could be changed to render the email body via the TemplateRendererInterface
. And so
could be also be used to render emails with Blade
, Latte
and not only Twig
. Which make it easier to use
this component also in other Frameworks. Also, other functionality like inky
or inline_css
functionality
could be provided to every Template Engine via an adapter pattern. Even Controllers can more easily be provided
by Libraries as they would no longer need to be written for all Frameworks template engine.
I think a common TemplateRendererInterface
would be a great addition to the PHP ecosystem and would make
it easier to use different PHP Libraries, Framework and CMS with the Template Engine somebody prefers and
even make Project upgrade and Framework changes easier.
There are currently existing abstraction for Template engines. Most common inside some frameworks in
the past there did exist the symfony/templating
. Other abstraction are mezzio/mezzio-template
,
laminas/laminas-view
, template-interop/engine
, and some more.
A common mistake in these abstractions which also are integrations are that they are doing more than they
should. Some like example Mezzio
is also abstracting configuration. As example paths
, this is bad in
two ways, it forces that it can only support template engine which supports multiple paths
or even
require template engine which load there template
via a Filesystem loader. So this forced configuration
would not allow us to use a Template engine which is using different kind of loading of does not support
multiple directories like Mustache
, Latte
(current state), Handlebars
.
So this abstraction follows the Single-Responsibility-Principle
which we defined on a very limited scope and that is rendering a given template with the given data.
All template engines are different and there integrations into the different frameworks requires that the template engines have their own configuration and no config abstraction around it. The project decides how to configure the template engine and library authors just use the Interface to render a configured template with the data that library provides.
Also, a common mistake in integrations of Template Engine in Frameworks are not providing the inner
template engine to the outside. I did stumble over integration of Blade
into Symfony
Framework
which does not provide Blade itself just a Wrapper service around Blade. This does example not allow
me use a Library which requires Blade directly. So all integrations not only provide the Adapter
for the TemplateRendererInterface
but also the Template Engine
service itself. This way libraries
which do not yet support the TemplateRendererInterface
can easily be integrated into any Framework.
As the provided Integrations provides also the inner Template Engine as a service which could be used
in these edge case.
So the target is a simple Abstract Interface for Rendering a Template with given data. And providing well documented integrations of Template engines into all kind of Frameworks without limiting there direct usage and extensibility.
Following table should show the process of integration of different template engines and the abstract
Interface into the different Frameworks. The first part shows the main supported template engines
Twig, Blade and Latte. This is actively maintained template engines. The second part shows some older
template engines which are supported but not actively maintained.
The third part shows framework specific view integrations which will only be supported in the specific
framework they are used.
The last part are some exotic template engines which did come up and are also adapters implemented for
them.
On the right part of the table shows frameworks which are planned to be supported but are not yet
implemented.
Template Engine | Adapter | Symfony | Laravel | Laminas | Mezzio | Spiral | Yii | Typo3 | Cake | CodeIgniter | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Twig | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Blade | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Latte | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Plates | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Smarty | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Handlebars | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Mustache | âś… | âś… | âś… | âś… | âś… | âś… | ||||||
Laminas View | âś… | âś… | ||||||||||
Mezzio Template | âś… | âś… | ||||||||||
Spiral View | âś… | âś… | ||||||||||
Yii View | âś… | |||||||||||
Aura View | âś… | |||||||||||
Fluid | âś… | |||||||||||
Cake View | ||||||||||||
Contao | ||||||||||||
Qiq | âś… | |||||||||||
PHPTAL | âś… | |||||||||||
Brainy | âś… |
There exist for all already implemented frameworks an example application. Which shows you how to use the Interface or a specific adapter inside the framework.
Go into one of the application directory and run:
composer install
php -S 127.0.0.1:8000 -t public
Open then http://127.0.0.1:8000/ to get a list of available integrations in the example.
- http://127.0.0.1:8000/twig
- http://127.0.0.1:8000/blade
- http://127.0.0.1:8000/latte
- http://127.0.0.1:8000/plates
- http://127.0.0.1:8000/smarty
- http://127.0.0.1:8000/handlebars
- http://127.0.0.1:8000/mustache
- http://127.0.0.1:8000/laminas-view
- http://127.0.0.1:8000/mezzio-template
- http://127.0.0.1:8000/laminas-view
If you create a library, framework or whatever reusable package you should just require the template renderer in the
require
section of your composer.json
:
composer require schranz-templating/template-renderer
As library author every service which render a template should use the TemplateRendererInterface
.
Also every templateName should come from a configuration which can be defined by the user of your
library. That configuration depends on which framework your library is integrated into.
Example Controller:
namespace Your\Package;
use Schranz\Templating\TemplateRendererInterface;
class YourController
{
public function __construct(
private TemplateRendererInterface $templateRenderer,
private string $templateName,
) {
}
public function yourAction(): YourResponse
{
$content = $this->templateRenderer->render($this->templateName, ['your' => 'data']);
return new YourResponse($content);
}
}
To test your library you can use any of the provided adapters and integrations as require-dev
dependency.
There should be no requirement to an adapter or integration in the require
section of your
libraries composer.json
. This allows the user of your library to choice which one matches best for their
used project and framework.
Projects depending on libraries which where build on top of the schranz-templating/template-render
abstract
should require the renderer package and an adapter to the template engine they want to use:
composer require schranz-templating/twig-adapter
composer require schranz-templating/blade-adapter
composer require schranz-templating/latte-adapter
composer require schranz-templating/plates-adapter
composer require schranz-templating/smarty-adapter
composer require schranz-templating/brainy-adapter
composer require schranz-templating/handlebars-adapter
composer require schranz-templating/mustache-adapter
composer require schranz-templating/aura-view-adapter
composer require schranz-templating/fluid-adapter
composer require schranz-templating/laminas-view-adapter
composer require schranz-templating/mezzio-template-adapter
composer require schranz-templating/spiral-view-adapter
composer require schranz-templating/yii-view-adapter
composer require schranz-templating/phptal-adapter
composer require schranz-templating/qiq-adapter
Why Adapters?
As it would be too much work to create forks of every template engines to implement
the interface for prototyping it is easier to use the Adapter Design Pattern
to transfer from the TemplateRendererInterface
to underlying template engines.
To use the integration in the Symfony Framework the following packages are currently provided, which will register the adapter service and integration of the selected template engine:
composer require schranz-templating/symfony-twig-integration
composer require schranz-templating/symfony-blade-integration
composer require schranz-templating/symfony-latte-integration
composer require schranz-templating/symfony-plates-integration
composer require schranz-templating/symfony-smarty-integration
composer require schranz-templating/symfony-handlebars-integration
composer require schranz-templating/symfony-mustache-integration
To use the integration in the Laravel Framework the following packages are currently provided, which will register the adapter service and integration of the selected template engine:
composer require schranz-templating/laravel-spiral-view-integration
composer require schranz-templating/laravel-twig-integration
composer require schranz-templating/laravel-blade-integration
composer require schranz-templating/laravel-latte-integration
composer require schranz-templating/laravel-plates-integration
composer require schranz-templating/laravel-smarty-integration
composer require schranz-templating/laravel-handlebars-integration
composer require schranz-templating/laravel-mustache-integration
To use the integration in the Laminas Framework the following packages are currently provided, which will register the adapter service and integration of the selected template engine:
composer require schranz-templating/laminas-laminas-view-integration
composer require schranz-templating/laminas-twig-integration
composer require schranz-templating/laminas-blade-integration
composer require schranz-templating/laminas-latte-integration
composer require schranz-templating/laminas-plates-integration
composer require schranz-templating/laminas-smarty-integration
composer require schranz-templating/laminas-handlebars-integration
composer require schranz-templating/laminas-mustache-integration
To use the integration in the Mezzio Framework the following packages are currently provided, which will register the adapter service and integration of the selected template engine:
composer require schranz-templating/mezzio-mezzio-template-integration
composer require schranz-templating/mezzio-twig-integration
composer require schranz-templating/mezzio-blade-integration
composer require schranz-templating/mezzio-latte-integration
composer require schranz-templating/mezzio-plates-integration
composer require schranz-templating/mezzio-smarty-integration
composer require schranz-templating/mezzio-handlebars-integration
composer require schranz-templating/mezzio-mustache-integration
To use the integration in the Spiral Framework the following packages are currently provided, which will register the adapter service and integration of the selected template engine:
composer require schranz-templating/spiral-spiral-view-integration
composer require schranz-templating/spiral-twig-integration
composer require schranz-templating/spiral-blade-integration
composer require schranz-templating/spiral-latte-integration
composer require schranz-templating/spiral-plates-integration
composer require schranz-templating/spiral-smarty-integration
composer require schranz-templating/spiral-handlebars-integration
composer require schranz-templating/spiral-mustache-integration
The current project already provides about more than ~50 packages to integrate different template engines into different frameworks.
- TemplateRendererInterface (
schranz-templating/template-renderer
) - Adapters
- Twig Template Renderer (
schranz-templating/twig-adapter
) - Blade Template Renderer (
schranz-templating/blade-adapter
) - Latte Template Renderer (
schranz-templating/latte-adapter
) - Plates Template Renderer (
schranz-templating/plates-adapter
) - Smarty Template Renderer (
schranz-templating/smarty-adapter
) - Handlebars Renderer (
schranz-templating/handlebars-adapter
) - Mustache Renderer (
schranz-templating/mustache-adapter
) - Laminas View Renderer (
schranz-templating/laminas-view-adapter
) - Mezzio Template Renderer (
schranz-templating/mezzio-adapter
) - YiiView Renderer (
schranz-templating/yii-view-adapter
) - Aura View Renderer (
schranz-templating/aura-view-adapter
) - Spiral View Template Renderer (
schranz-templating/spiral-view-adapter
) - Fluid Renderer (
schranz-templating/fluid-adapter
) - Qiq Template Renderer (
schranz-templating/qiq-adapter
) - PHPTAL Renderer (
schranz-templating/phptal-adapter
) - Brainy Renderer (
schranz-templating/brainy-adapter
) - Cake View
- Contao
- Twig Template Renderer (
- Integrations
- Symfony
- Twig (
schranz-templating/symfony-twig-integration
) - Blade (
schranz-templating/symfony-blade-integration
) - Latte (
schranz-templating/symfony-latte-integration
) - Plates (
schranz-templating/symfony-plates-integration
) - Smarty (
schranz-templating/symfony-smarty-integration
) - Handlebars (
schranz-templating/symfony-handlebars-integration
) - Mustache (
schranz-templating/symfony-mustache-integration
) - Brainy
- PHPTAL
- ...
- Twig (
- Laravel
- Blade (
schranz-templating/laravel-blade-integration
) - Twig (
schranz-templating/laravel-twig-integration
) - Latte (
schranz-templating/laravel-latte-integration
) - Plates (
schranz-templating/laravel-plates-integration
) - Smarty (
schranz-templating/laravel-smarty-integration
) - Handlebars (
schranz-templating/laravel-handlebars-integration
) - Mustache (
schranz-templating/laravel-mustache-integration
) - Brainy
- PHPTAL
- ...
- Blade (
- Spiral
- SpiralView (
schranz-templating/spiral-spiral-view-integration
) - Twig (
schranz-templating/spiral-twig-integration
) - Blade (
schranz-templating/spiral-blade-integration
) - Latte (
schranz-templating/spiral-latte-integration
) - Plates (
schranz-templating/spiral-plates-integration
) - Smarty (
schranz-templating/spiral-smarty-integration
) - Handlebars (
schranz-templating/spiral-handlebars-integration
) - Mustache (
schranz-templating/spiral-mustache-integration
) - Brainy
- PHPTAL
- ...
- SpiralView (
- Laminas
- LaminasView (
schranz-templating/laminas-laminas-view-integration
) - Twig (
schranz-templating/laminas-twig-integration
) - Blade (
schranz-templating/laminas-blade-integration
) - Latte (
schranz-templating/laminas-latte-integration
) - Plates (
schranz-templating/laminas-plates-integration
) - Smarty (
schranz-templating/laminas-smarty-integration
) - Handlebars (
schranz-templating/laminas-handlebars-integration
) - Mustache (
schranz-templating/laminas-mustache-integration
) - Brainy
- PHPTAL
- ...
- LaminasView (
- Mezzio
- Mezzio (
schranz-templating/mezzio-mezzio-template-integration
) - Twig (
schranz-templating/mezzio-twig-integration
) - Latte (
schranz-templating/mezzio-blade-integration
) - Latte (
schranz-templating/mezzio-latte-integration
) - Plates (
schranz-templating/mezzio-plates-integration
) - Smarty (
schranz-templating/mezzio-smarty-integration
) - Handlebars (
schranz-templating/mezzio-handlebars-integration
) - Mustache (
schranz-templating/mezzio-mustache-integration
) - Brainy
- PHPTAL
- ...
- Mezzio (
- Yii
- YiiView
- ...
- Typo3
- Fluid
- ...
- Cake
- ...
- CodeIgniter
- ...
- Symfony
- Subtree Split
- Register Packages
In the following table we will ist all yet found interesting template engines and view renderers. What kind of version they currently are and what PHP Version they are supporting. Also, what kind of features are supported by them.
Engine | Version | PHP Version | Inheritance | Subviews | Namespaces | Functions | Filters | Exist | Partial | Streaming | String | Raw | Globals |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Twig | 3.4.1 |
>=7.2.5 |
[x] | [x] @ |
[x] | [x] | [x] | [x] | [x] | [x] | [x] | [x] | |
Blade | 9.15.0 |
^8.1 |
[x] | [x] :: |
? | ? | ? | [x] | ? | [x] | ? | ? | |
Latte | 3.0.0 |
>=8.0 <8.2 |
[x] | ? | [x] | ? | ? | ? | [x] | [x] | ? | ||
Plates | 3.4.0 |
^7.0 ^8.0 |
[x] | [x] :: |
[x] | ? | ? | [x] | ? | ? | |||
Smarty | 4.1.1 |
^7.1 ^8.0 |
[x] | ? | [x] | ? | ? | ? | [x] | [x] | ? | ? | |
Brainy | 4.0.0 |
>=7.3 |
[x] | ? | [x] | ? | ? | ? | [x] | [x] | ? | ? | |
Mustache | 2.14.1 |
>=5.2.4 |
? | ? | ? | ? | ? | ? | [x] | ? | ? | ||
Handlebars | 3.0 |
>=5.4.0 |
? | ? | ? | ? | ? | ? | [x] | ? | ? | ||
Mezzio Template | 3.10.0 |
~7.4.0 ~8.0.0 ~8.1.0 |
[x] | [x] @ |
? | ? | ? | ? | [x] | ? | ? | ||
Spiral View | 2.13.1 |
>=7.4 |
[x] | ? | ? | ? | ? | ? | ? | ? | ? | ? | |
Laminas View | 2.20.0 |
^7.4 ~8.0.0 ~8.1.0 |
[x] | [x] | ? | ? | ? | ? | ? | ? | [x] | ? | ? |
Yii View | 5.0.0 |
^7.4 ^8.0 |
[x] | [x] | [x] @ |
? | ? | ? | ? | ? | [x] | ? | ? |
Fluid | 2.7.1 |
>=5.5.0 |
? | ? | ? | ? | ? | ? | ? | ? | [x] | ? | ? |
Contao | 4.13.4 |
^7.4 ^8.0 |
? | ? | ? | ? | ? | ? | ? | ? | [x] | ? | ? |
Aura View | 2.4.0 |
>=5.4.0 |
? | ? | ? | ? | ? | ? | ? | ? | [x] | ? | ? |
Qiq | 1.0.2 |
^8.0 |
? | ? | ? | ? | ? | ? | ? | ? | [x] | ? | ? |
PHPTAL | 1.7.0 |
~8.0.0 ~8.1.0 ~8.2.0 |
? | ? | ? | ? | ? | ? | ? | ? | [x] | ? | ? |
In the table above it is listed which features are supported by different template engines. Beneath a more detail description about the requirement to fulfill it is given.
With support for template inheritance it means that a rendered template can inherit from a parent template and overwrite parts in the template language itself. This feature is not fulfilled for template engines which only allow to give subviews into a parent view or other way around.
E.g.: This is achieved in twig via extends and blocks. In blade via extends and sections keywords. In latte via layout and blocks keywords. Laminas view via the layout helper.
This is the opposite way of creating layout and base template. Why it is possible via all engines to call several render calls and put template together. This is only fulfilled for template engines explicit working with a view, layout system and support given view with subviews them in the renderer call.
E.g.: In laminas view this is possible via the setLayout before calling the rendering of the template.
This is not fulfilled by twig, blade or latte as they support layout and inheritance only on template level not on render caller level.
The template engine allow to render specific template from different directories via namepaces. Also how a namespace is reference should be listed.
E.g. This is achieved in twig via the @Namespace/
and in Blade and Latte via namespace::
template names.
The template engine allows to define custom functions which can be called via the that name in the template.
E.g.: This is achieved in twig via twig extension and twig functions.
The template engine allows to check if a template with a specific name does exist or not.
E.g.: This is achieved in twig via the getLoader()->exist method.
The template engine allows to define custom filters which can be called via the that name on a specific variable in the template.
E.g.: This is achieved in twig via twig extension, twig filters and the filter |
operator.
The template engine allows to render only a subpart of a template.
E.g. This is achieved in twig via loading the template and use the
templates renderBlock
method. In blade via the fragment method on
the view newly added in lately.
Supports to stream the template directly to the output and not have the need to keep all in a string variable.
E.g. This is achieved in twig via the display
method.
Supports to return the template as a string.
E.g. This is achieved in twig via the render
method, in Smarty/Brainy view the fetch
method.
Some engines provide auto escaping like twig and latte. In extensions there need to be a way to disable it.
E.g.: In twig this is handled via ['is_safe' => 'html']
, in Latte via Latte\Runtime\Html($var)
class.
If the template engine supports to define globals.
E.g.: In twig globals can be defined via extensions.
Tooling around template engines:
-
Twig
- twig2latte Twig to Latte converter
- twigfiddle Twig Fiddle
- VincentLanglet/Twig-CS-Fixer
- twigstan PHPStan for Twig
- symfony-twig-lint
bin/console lint:twig
- Prototypes or removed tools kept for references:
- reveal/reveal-twig PHPStan for Twig (look at TwigStan now)
- driveto/phpstan-twig PHPStan for Twig (look at TwigStan now)
- matthiasnoback/phpstan-twig-analysis PHPStan for Twig (look at TwigStan now)
- friendsoftwig/twigcs Not longer maintained look at Twig-CS-Fixer now
- asm89/twig-lint Not longer maintained look at Twig-CS-Fixer now
- k10r/twig-cs-fixer Not longer exists look at Twig-CS-Fixer now
-
Latte
- twig2latte
- lattefiddle
- reveal/reveal-latte PHPStan Latte Rules
-
Blade
Please let me know about more tools around template engines.