Old description
It is common that a library, application or CMSs need to have a template renderer / engine for rendering data for their websites or emails. As a library author I want to make it free that my service can be used with any template engine the developer want to use.
Also for projects when somebody wants maybe switch in future from twig to latte templates as it consider better safety for xss.
The interface for the template renderer is simple with a template string and an array context:
/**
* Render the template with the given context data.
*
* @param string $template
* @param array<string, mixed> $context
*
* @return string
*
* @throw TemplateNotFoundExceptionInterface
*/
public function render(string $template, array $context = []): string;
The $template
does not need to be a real filePath. The used template engine
just need to support it. As an example in twig like template path @namespace/directory/file.twig.html
is supported or for blade something like website
, or latte something like mail.latte
. So $template
the is a string. It was considered not to be an object given
to the render
method as such complex View
class things like example in laminas view
where the View can have subviews via children are not supported in most other template engines.
So it is easier for a template engine like laminas view
to have a bridge which
converts $template
and $context
into their View object as for example Twig, Smarty, Latte to have the need to create a View object.
The context need to be an array which key are a string and can contain any additional objects in it. Where some template engines supporting to give object as context into the renderer it was for maximum compatibility implemented to support only array by this proposal.
The implementation of the TemplateRendererInterface
should throw a TemplateNotFoundExceptionInterface
when the given template was not found. Any other exception thrown are up to the template engine. There is explicit no method to check if a service exist as it is not supported by all template renderer or are in example case like twig totally different services.
The proposal should describe all requirements for the template renderers.
There are maybe template engines / renderer which will not directly implementing the PSR but it is should hopefully this way not be much work for example creating a laminas-view-psr-bridge
package to also support that type of template engine over the PSR TemplateRendererInterface.
I'm new to PHPFig and I'm sure I forget some process, so please help and direct me into the correct process.
PSR Template Renderer Proposal
A proposal for psr for rendering templates.
Goal
It is common that a library, application or CMSs need to have a template renderer / engine for rendering data for their websites or emails.
More and more application are going here to support data provider way. This application are the one which would benifit from the TemplateRendererInterface as they not only can provide them headless over an API but also make it possible that somebody can render the data via a Template Engine.
As a library author I want to make it free that my service can be used with any template engine the developer want to use. Typical usecases are PHP rendered CMSs like Sulu, Typo3, Drupal, Contao which maybe could benifit from this. But also all other data provider based libraries which ship configureable controller or have template to render like email tools / libraries.
Also for projects when somebody wants maybe switch in future from twig to latte templates as it consider better safety for xss a common interface can benifit here and avoid refractorings.
Defining the scope
The scope of the TemplateRenderer is only on rendering a given template with a given context. The template render interface will not take care of registering template paths or how to configure the template engine to find the templates. Similar how PSR-18 HttpClient does not care how the client is created or configured.
Analysis
In this section I did analyse the following existing template engines and added example how the render there templates.
Twig
Repository: https://github.com/twigphp/Twig
Current Version: v3.4.1
Supported PHP Version: >=7.2.5
Template Type Hint: string|TemplateWrapper
Context Type Hint: array
Return Type Hint: string
or output to buffer
Supports Stream: true
Render a template:
// render to variable
$content = $twig->render('test.template.twig', ['optional' => 'key-value']);
// render to output buffer
$twig->display('template.html.twig', ['optional' => 'value']);
Smarty
Repository: https://github.com/smarty-php/smarty
Current Version: v4.1.1
Supported PHP Version: ^7.1 || ^8.0
Template Type Hint: string
Context Type Hint: array
Return Type Hint: none
Supports Stream: true (only)
Render a template:
// render to output buffer
$smarty->assign('optional', 'value');
$smart->display('template.tpl');
Latte
Repository: https://github.com/nette/latte
Current Version: v3.0.0
Supported PHP Version: >=8.0 <8.2
Template Type Hint: string
Context Type Hint: object|mixed[]
Return Type Hint: string
or output to buffer
Supports Stream: true
Render a template:
// render to variable
$latte->renderToString('template.latte', ['optional' => 'value']);
// render to output buffer
$latte->render('template.latte', ['optional' => 'value']);
Laminas View
Repository: https://github.com/laminas/laminas-view
Current Version: ^2.20.0
Supported PHP Version: ^7.4 || ~8.0.0 || ~8.1.0
Template Type Hint: string
Context Type Hint: ViewModel<null|array|Traversable|ArrayAccess>
Return Type Hint: null|string
Supports Stream: false
// render to variable
$viewModel = new ViewModel(['headline' => 'Example']);
$viewModel->setTemplate('index');
$content = $this->view($viewModel)->render();
Blade
Repository: https://github.com/illuminate/view
Current Version: v9.15.0
Supported PHP Version: ^8.1
Template Type Hint: string
Context Type Hint: array
Return Type Hint: string
Supports Stream: false ?
Render a template:
// render to variable
$content = view('welcome', ['name' => 'Samantha']);
// same as:
$content = $viewFactory->make($view, $data, $mergeData)->render();
Fluid
Repository: https://github.com/TYPO3/Fluid
Current Version: 2.7.1
Supported PHP Version: >=5.5.0
Template Type Hint: string
Context Type Hint: array
Return Type Hint: string
Supports Stream: false ?
// render to variable
$view = new StandaloneView();
$view->setTemplatePathAndFilename('template.html');
$view->assignMultiple(['optional' => 'key-value']);
$content = $view->render();
Contao
Repository: https://github.com/contao/core-bundle
Current Version: 4.13.4
Supported PHP Version: ^7.4 || ^8.0
Template Type Hint: string
Context Type Hint: object<string, mixed>
via dynamic properties
Return Type Hint: string
Supports Stream: false ?
// render to variable
$template = new FrontendTemplate('template');
$template->optional = 'value';
$content = $template->parse();
Mezzio
Repository: https://github.com/mezzio/mezzio
Current Version: 3.10.0
Supported PHP Version: ~7.4.0||~8.0.0||~8.1.0
Template Type Hint: string
Context Type Hint: array|object
Return Type Hint: string
Supports Stream: false
// render to variable
$content = $templateRenderer->render('template', ['optional' => 'value']);
Plates
Repository: https://github.com/thephpleague/plates
Current Version: v3.4.0
Supported PHP Version: ^7.0|^8.0
Template Type Hint: string
Context Type Hint: array
Return Type Hint: string
Supports Stream: false
// render to variable
$content = $plates->render('template', ['optional' => 'value']);
Mustache
Repository: https://github.com/bobthecow/mustache.php
Current Version: v2.14.1
Supported PHP Version: >=5.2.4
Template Type Hint: string
Context Type Hint: array
Return Type Hint: string
Supports Stream: false
// render to variable
$content = $mustache->render('template', ['optional' => 'value']);
Yii View
Repository: https://github.com/yiisoft/view
Current Version: 5.0.0
Supported PHP Version: ^7.4|^8.0
Template Type Hint: string
Context Type Hint: array
Return Type Hint: string
Supports Stream: false
// render to variable
$content = $view->render('template', ['optional' => 'value']);
Spiral View
Repository: https://github.com/spiral/views
Current Version: 2.13.1
Supported PHP Version: >=^7.4
Template Type Hint: string
Context Type Hint: array
Return Type Hint: string
Supports Stream: ?
// render to variable
$content = $view->render('template', ['optional' => 'value']);
The proposal
The interface for a TemplateRender I would recommend is the following based on my analysis of exist template engines and what is the easiest way to put them together and have maximum interoperability:
/**
* Render the template with the given context data.
*
* @param string $template
* @param array<string, mixed> $context
*
* @return string
*
* @throw TemplateNotFoundExceptionInterface
*/
public function render(string $template, array $context = []): string;
For maximum compatibility we even could consider to publish 2 version of the template renderer v1 without typehints so exist template engine still supporting old php version can already implement it and v2 with typehints.
Exist TemplateRenderer Discussion
There was already an exist disussion about implementing a TemplateRendererInterface
here: https://groups.google.com/g/php-fig/c/w1cugJ9DaFg/m/TPTnYY5LBgAJ.
The discussion goes over several topics just to mention the main parts:
Template
should be objects
Context
should be objects
TemplateRender
should stream to output even asynchronity
To target this specific points. I would focus in this PSR on exist solution as we see most work for template with logical string based names and do not require an object.
I want mention here also developer experience as example in the past why there was created PSR 16 (Simple Cache) where we did still have PSR 6.
So the name of the proposal should maybe be "Simple TemplateRenderer" and not try to reinventing the wheel.
By analysing exist template engine, not everybody support to have an object as a context so I would keep the interface to array for context only. This way it is easy to make exist template engine compatible with PSR interface.
For streaming the output I must say I did not see one project since 7 years which did use for example the streaming functionality of twig and for maximum compatibility I would also remove that requirement from the PSR as the analysis give there are template engines which do not support that functionality.
The proposal I did write can be found here: https://github.com/php-fig/fig-standards/pull/1280/files. I know I did skip here some process by already writing something, still I hope we can put most of the template engine creators / maintainers on one template to dicuss if they are willing to add such an interface or for which we maybe can provide a bridge, so system like CMSs, Librariy, Newsletter Tools, can make use of it. I will also bring this topic on the table for our next CMS Garden call which we have every month where several members of different CMS have a call together discussion common topics.
TODO List
- [ ] Create list which libraries would benifite using a TemplateRendererInterface and which would not
- [ ] Analyse exist Template engine of their ability to check if a template exist or not
New PSR WIP