Hi Alex,

I was wondering how my very simple template locator would fit with this:

https://github.com/mindplay-dk/kisstpl

As you can see, the interface for this is radically different - there is no 
string for the template name, and no array for the data.

After some thinking though, I realized this locator could actually use this 
PSR internally - meaning, my library, which currently uses plain PHP 
templates only, would be able to internally dispatch this interface, which 
means you could use the view-model approach (no string, no array) to 
dispatch any standard template engine. Pretty cool. :-)

The one area where this proposal falls short (as, from the discussion, you 
already know) is with its inability to stream. As you can see here, my 
library addresses this by having two methods, render() and capture()

https://github.com/mindplay-dk/kisstpl/blob/7d59e10da48b2a86f14244c490231a56f78c2611/src/Renderer.php#L10-L41

What I would suggest, is to do the same for this proposal - rename render() 
to capture() and just have a second method with return-type void:

/**
 * Render the template with the given context data, directly to output.
 *
 * @param string $template
 * @param array<string, mixed> $context
 *
 * @return void
 *
 * @throw TemplateNotFoundExceptionInterface
 */
public function render(string $template, array $context = []): void;

/**
 * Render the template with the given context data.
 *
 * @param string $template
 * @param array<string, mixed> $context
 *
 * @return void
 *
 * @throw TemplateNotFoundExceptionInterface
 */
public function capture(string $template, array $context = []): string;

Now, I'm sure there will be some engines that are incapable of rendering 
directly to output - in those cases, render() would simply be implemented 
as e.g. echo $this->capture() and, of course, you wouldn't get the 
performance or memory advantage of streaming here.

But the proposal also wouldn't *stand in the way* of getting this advantage 
from template engines that *are* capable of streaming.

Looks like a good proposal, but addressing this issue seems easy enough and 
worth while, since it doesn't add any real complexity for implementors.

- Rasmus Schultz


On Wednesday, June 1, 2022 at 1:15:24 AM UTC+2 Alexander Schranz wrote:

Hi my name is Alex,

I want to bring a new proposal to PHP-Fig, which could be interesting 
specially for Frameworks and CMSs, as I'm one of the core developers of 
Sulu CMS we have todo much with template renderers.

*PSR Template Renderer Proposal* 

A proposal for psr <https://www.php-fig.org/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 the 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 
<https://twig.symfony.com/> to latte templates <https://latte.nette.org/> 
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 <https://github.com/php-fig/fig-standards/pull/1280#twig> 
   (Symfony, Sulu CMS, Drupal, Contao (experimental))
   - Smarty <https://github.com/php-fig/fig-standards/pull/1280#smarty>
   - Latte <https://github.com/php-fig/fig-standards/pull/1280#latte> 
   (Nette)
   - Laminas View 
   <https://github.com/php-fig/fig-standards/pull/1280#laminas-view>
   - Blade <https://github.com/php-fig/fig-standards/pull/1280#blade> 
   (Laravel)
   - Fluid <https://github.com/php-fig/fig-standards/pull/1280#fluid> 
   (Typo3)
   - Contao <https://github.com/php-fig/fig-standards/pull/1280#contao> 
   (Contao CMS)
   - Mezzio <https://github.com/php-fig/fig-standards/pull/1280#mezzio> 
   (Laminas) (already abstract Twig and Plates)
   - Plates <https://github.com/php-fig/fig-standards/pull/1280#plates> 
   (PHP League)
   - Mustache <https://github.com/php-fig/fig-standards/pull/1280#mustache>

*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: v3.4.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/illuminate/view
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 a template:
// render to variable
$view = new StandaloneView();
$view->setTemplatePathAndFilename('template.html');
$view->assignMultiple(['optional' => 'key-value']);
$content = $view->render();

*Contao* 

Repository: https://github.com/TYPO3/Fluid
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 a template:
// 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 a template:
// 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 a template:
// 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 a template:
// render to variable
$content = $mustache->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 table to dicuss 
if they are willing to add such an interface or for which we maybe can 
provide a bridge, so system like CMSs, Library, Newsletter Tools, can make 
use of it. I will also bring this topic on the table for our next CMS Garden 
<https://www.cms-garden.org/en> call which we have every month where 
several members of different CMS have a call together discussion common 
topics. So maybe I can reach there a CMS which maybe did not yet part of 
this mailing list / github discussion.

Best Regards,
Alex

-- 
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to php-fig+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/ed76c8be-e11a-4d79-8f61-1e68d5996a17n%40googlegroups.com.

Reply via email to