-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Add proposal for template renderer #1280
base: master
Are you sure you want to change the base?
Add proposal for template renderer #1280
Conversation
This proposal could be interesting for following template engines and CMSs:
And would make it so easier to implement libraries which has to render something into this systems and frameworks. But the rendered thing is project specific, specially CMSs could support so multiple template engines. I hope its fine that I tagged current maintainers of the libraries, Please feel free to unsubscribe this PR to avoid spaming your inbox if you are not interested into this topic. |
|
||
Having common interfaces for rendering templates allows developers to create libraries that can interact with many frameworks and other libraries in a common fashion. | ||
|
||
Some examples may use the Interface: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think details should be moved to a meta-document.
- Newsletter/Mail Libraries (Symfony Mailer, Abstractions over Mailchimp and other Newsletter tools) | ||
- Anything following ["Separating content from presentation"](https://en.wikipedia.org/wiki/Separation_of_content_and_presentation) where the presentation is project specific. | ||
|
||
Some examples may implement the Interface or providing a bridge: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yii has alike interface https://github.com/yiisoft/view/blob/master/src/TemplateRendererInterface.php with the difference that we're passing a rendering context as $this
for nested template rendering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx for mentioning yii views. Looking at the example it works the same way ->render($template, $params)
: https://github.com/yiisoft/view/blob/master/docs/basic-functionality.md#rendering
That yii uses then in the template
$this
is totally fine the TemplateRendererInterface doesn't force how the the variables need to access in the template. If it is {variable}
, {{variable}}
or <?php echo $this->variable ?>
is up to the template renderer.
I will add it also the the list. Please check my details in the PR description about it if I did write nothing wrong about it.
A implementor of a template renderer is allowed to support as context also an object, but a user is REQUIRED whennusing | ||
The `TemplateRendererInterface` to give the context as array to support also simple template renderers. | ||
|
||
## Usage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section definitely belongs to metadata document.
I would love to see the proposed interface in action for some template engines mentioned in the description. Having some examples of non-trivial use cases might help understanding if the abstraction is useful in real-life scenarios. |
Hi, I never understood the point of these interfaces and I certainly won't implement them, so I'm unsubscribing. Bye. |
I'm not actually a maintainer for My personal initial reaction is generally 👍, with the exception that a well-typed view model makes for a pleasant template editing experience so rejecting an object for context is a shame… that said, it would be easy enough to write a bridge component as already mentioned. |
I'd suggest having a look at the mezzio template renderer which already supports multiple engines such as laminas-view, twig, etc. Not expecting to use that, just to get an idea of how it could work. |
Reference:
|
I've been trying to make TYPO3 (as it currently heavily depends on its own rendering engine called "Fluid") more interoperable to allow e.g. twig or blade as engine under the hood. Our current implementation looks like this, where we would start creating adapters from it: Next to the render() method (with $context which is fine), I think an |
@bmack I've found What's the problem? Consider things like authenticated identities, which might persist between different requests made by different users. We'd already had I'd prefer we omit any mechanism that would provide default template variables from the interface. |
Thx you already all for your feedback. To not skip the official way I also did inform the mailing list via the google groups about this PR: https://groups.google.com/g/php-fig/c/X4e1z5IaG9E Above I did update the whole pull request description with my analysis of different template engines. Please reread it so I did not have an error in my analysis about your template renderers.
@fabpot Thx for attending here. I think CMS are a nice usecase for it. In example Sulu provide to define the form (structure) for content management in a xml file which creates the form for the content manager. Sulu itself already provides a Controller which will load all data for the the site. This controller is called when the url is triggered via a dynamic router, but the template which is render is not provided by Sulu, it is implemented in the project and configured there in the project xml file. So Sulu would not care what template engine is used to render its data it does just provide it over the configured controller and could in this case render the configured template over a configured template engine. @gsteel Thx for mentioning the correct people. @boesing @froschdesign Nice to have you onboard, didn't know mezzio has something like that looks great that you did for rendering end with similar method. I would keep the other method out of scope for a renderer. @bmack Thx for your feedback, great to see that you are also interested into this topic. I would avoid a ViewInterface as we would end then into the problems with the @weierophinney I agree the TemplateRender should by stateless service, sure we can not avoid that every template engine is stateless as its there code/class but there should not be something like |
Co-authored-by: Alexander Makarov <[email protected]>
I would add one more templating engine into the list, as you are comparing multiple ones. https://github.com/qiqphp/qiq by @pmjones . Mezzio was trying to solve the same problem some years back. So their inputs will be valuable here. |
Some quick thoughts:
I'm struggling with the use cases though. It's not similar to PSR-18 as HTTP clients has a well-defined scope, based on a standard. I can see how I can switch from Guzzle to Symfony HTTP client in a project and avoid having two HTTP client implementations. But for templating engines, I think it's very different. If you decide to switch from Twig to Blade, you would need to start from scratch. And supporting Blade and Twig for a CMS also probably mean writing helpers, filters, functions, or whatever extension points the engine has for all supported systems. I'm probably missing something here about the use cases though. |
Co-authored-by: Sergei Predvoditelev <[email protected]>
@fabpot —
What would that look like? Would you typehint it as |
I'll note that, if a template engine wants to have extra/standard/default stuff, it can always implement PSR-14 internally to pass the context set to listeners that can set default values for things. That would eliminate the need for such support in a common template interface. I mostly share @fabpot's skepticism, though. As I said on the mailing list, while a type-compatible common interface for different template engines seems simple enough, a semantically-compatible interface is a much bigger deal. Just how to identify a given template by name is right now entirely unstandardized, but would need to be standardized for the goal of a library being able to work with "whatever template engine a framework/CMS is using." If the relevant implementers are able to agree on a format for that, cool, I'm all for it, but let's not under-sell the challenge here. I'd expect an object-based context set to be more like how Go's template engine works. The |
Thank you both for your responses and attending this discussion. I think we maybe have different usecases for the TemplateRenderInterface in mind. So I will try to answer your comments in best way and my point of view on it.
I'm also curious here what did you think about and what problems you are trying to solve with it? Can you show an example you had in mind?
That is a great hint and totally false usecase or bad example "TemplateRendererInterface" from my side. As the one calling "TemplateRendererInterface" are the one which do not care how the Template is rendered. The ones implemented the template do care and so are the one which decide what is used as the engine, so they benifit as they can decide which template engine they want to use without there used library will force them. Still yeah switching is a bad example, but would be allowed or even you could maybe migrate better via the TemplateRendererInterface from old template engine by using some kind of ChainTemplateRenderer.
That is a great hint, I did go with an exception currently because I thought it is easier to implement. I did I think most TemplateEngine splitted TemplateLoading and TemplateRenderer, where I really would like to have something like
I agree with you they can not use helpers, filters, function provided by the library. But as example this is already the case in Sulu CMS, we have a punch of website which are using Sulu in Headless way. So the controller provides the data which is in headless way the data which normally the template ngine gets, serialized in JSON and requested from a Node JS (Next, Nuxt, Angular, ..) application. They also have only the data available by the controller and so not any helper. More and more are going this kind of hybrid way so the frontend technolgy can be used what they want, and the TemplateRendererInterface would fit here also well.
I think the whole PSR-18 was misleading here in the description what is the Goal and how a TemplateRendererInterface is recommended to be used. So ignore the PSR-18 part. So the scope of this proposal is really, how a library can provide data which which is rendered but don't want to take care how it is rendered.
Does really the template name, path, ... needed to be standardized. I don't think so. Because I think you are thinking from a false point of view where the
That is nice for simple application and totally aggree that they work well in there case but this kind of one to one mapping between object and template is very limited, and really do not work well for for dynamic systems like content management systems where I get a dynamic data and the template from Page A and Page B is different but the Object is the same. Forcing that every So we should not forget which libraries are the one which use a Template Engine. On the first hand there are libraries which just render HTML to represent them or send it via email. This are not the libraries which benifit a lot from a TemplateRendererInterface, because they don't care, they even need to require a specific template engine for rendering there things. Mostly its just for them something internal the template lays in there package and is mostly even not changeable over a config and looks like it looks. So what are the libraries benifit from the a TemplateRendererInterface. It are libraries where the project need to care how the Data is render, yeah I'm bringing here again the CMS example as it is the nearest for me. But maybe it is better if I use something more common here like the TemplateController of Symfony. acme_privacy:
path: /privacy
controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController
defaults:
template: 'static/privacy.html.twig' The controller is a really simple example how a CMS like Sulu works, with small differences that you do not configure a route path, but Sulu provide like here Symfony a Controller just with data in our case and like here the user of the library/framework configure template and control how it is rendered. This controller is also a great example why a one to one maching from object class to template is not great as there doesn't exist an object in this case and I have ther can be different I really don't want to say that the golang type of templating engine is bad or is even bad practice. It really is great when you create such application. I just think that the application which would benifit from the TemplateRendererInterface are others then the one using that type of template engine. So should we standardized the template name path or even have a mapping from context object to a template. From my point of view there are to this cases which speaks against it, to restrict the renderer this way and to less what speaks for it. Beside I would also link and mention again why I think that string, array is a better developer experience than having a project: https://groups.google.com/g/php-fig/c/X4e1z5IaG9E/m/Ubz4Id2GAQAJ I hope I did target all comments. For targeting the concern about the scope of the PSR I want to provide better example this day via some code of real world usecases to make it better understandable. Thank you all for attending here and for all the interests in this topic really great that this topic does have this kind of interest and is supported being discussed. |
I added 2 points to the TODO list which I will target next and will update the description of the Pull Request with it when done:
If somebody already know something let me know. |
I've been through this exercise before for my own amusement trying to find a way to migrate a CMS away from its own inconsistent homegrown rendering patterns and I do think that for the best interoperability, having a standardized naming convention is essential here (otherwise the implementation details about what's underneath the interface are just going to leak back out somewhere when trying to do a I work a lot in Laravel and Symfony; neither Blade nor Twig have a consistent template naming structure. Blade (no namespaces): Blade (with namespaces): If the goal is just to add an interface that |
I agree with @fabpot here. I don't quite see the use case yet. This doesn't mean there isn't one, of course. Maybe a list which libraries/application that would benifit from a TemplateRendererInterface as @alexander-schranz proposed will help in this regard, so standing by for that. |
I think this proposal would rather be helpful for libraries/packages/components that need to render something very simple and don't want to tie themselves to an engine. For example, if there is the need to output some formatted text in a single template file. As soon as the template itself needs some logic, you would end up providing an example in template language X. Or X and Y? Or all of them? In a CMS, on top, you have base templates, partials, custom functions, … that make things work in a useful way. Without a standard covering all of this as well, it's IMHO not really feasible to provide these in a template engine agnostic way. The Twig implementation in Contao CMS for instance allows every extension to extend from the same template without the need to know each other. This ecosystem works well - until you would allow arbitrary template engines. Then, interoperability between extensions would suffer. Besides this, here are some concerns regarding the
Also, looking at the last example above: The |
I'll chime in here on the template naming as well. As @mbabker notes, each template engine loads them differently. Some use full or relative file paths, including the extension. Others use a template "name", which a resolver than resolves to a template (whether that's in a file or in-memory or a string or something else). Some provide namespacing features (Blade, PlatesPHP, laminas-view to an extent). When we created mezzio-template, we opted to use a naming convention that all renderers then needed to support, and it looks basically like Blade's ( That said... conventions are brittle. If I were to do the exercise again, I'd use a value object for the template name, something like: interface TemplateName
{
public function getNamespace(): ?string;
public function getTemplate(): string;
} One possible addition to this interface would also be something along the lines of a "type", so that you could, for instance, vary the template resolved based on the type of content to return (e.g., "html", "text", "xml", etc."). An example of that in the wild is Twig, where you will often have the type of content being returned as an additional suffix to the template name (e.g. public function getType(): ?string; The renderer interface then consumes the interface TemplateRenderer
{
public function render(TemplateName $template, array $context): string;
} This way, an implementation can pull information from the Regarding the question posed by @Crell and @fabpot: the use case is in 3rd party libraries. Bundles, modules, etc. that provide controllers or handlers and additionally want to provide templated results, but may not know exactly which template engine is in use. (Yes, you generally know it for a framework, but it's not a guarantee.) These can then typehint on the |
The alternative could be making first argument of the renderer a |
Yes, similar idea. The main thrust is to have something that (a) is not a path, (b) allows for differentiating templates of the same name but different contexts, and optionally (c) allows for differentiating based on requested format for the final templated content. We would need to agree on what that reference looks like and what parts it would expose to the consumer (a rendering library). |
Wanted to give an update here as the last post is already 22 days ago. I still analysing different template engines, also about other features and things they support, to better understand them and their need for different things. Also understanding how different template engines are currently integrated into there core framework and also looking which engines are also integrated currently into other frameworks, as example twig into spiral or mezzio and so on. For experementig with a common interface and different template engines I created the following repository: https://github.com/schranz-templating/templating In that repository I will currently try to collect my analysis and I will try to keep you here up2date with them. So we have a good base about how a common interface could look like. |
Hello! Speaking on behalf of Laravel here. I do agree with @fabpot that it's unclear to me how this would pan out in non-trivial real world scenarios. If you are switching to a different templating engine it seems like you would want to use the syntax and features and DX affordances of that particular engine. |
@taylorotwell Thank you for attending here and representing Laravel :). For framework it would not change a lot, they would just provide that example by default "blade" is the TemplateRenderer service and in symfony "twig" the TemplateRenderer service. But example for libraries which allow to render "project" content via their code it is easier to implement this way into different framework. Usecase ALets have a look as example at the $email = (new TemplatedEmail())
->to(new Address($meail))
->htmlTemplate($this->templateName)
->context([
'url' => $url,
])
; But the library would not care which "template engine" would be behind and it could be used with "Twig" or "Blade". But a better example, I'm greating library on top of the $email = (new Email())
->to('[email protected]')
->html($linkHtml); Now as a library author I want to make it configurable how the "email" is looking for that project. But as a library author I want not force which "template" engine they use to render that email. So I'm just requiring that a TemplateRendererInterface Service implementation need to be provided and so I can go with something like this in my library: $html = '<a href="' . $url . '">' . $url . '</a>';
if ($this->templateName) {
$html = $this->templateRenderer($this->templateName, ['url' => $url]));
}
$email = (new Email())
->to('[email protected]')
->html($html); The So in this usecase it is not about using twig in laravel or using blade in symfony. But creating library which work in symfony with twig and in laravel with blade. Usecase BAnother usecase where I'm coming from is Content Management, which are typical "Data Providers". @sulu Which I'm part of it as example works for both cases as headless cms providing all data as json for a JS based rendering or as a classic cms rendering the pages via twig currently. A page template is sulu is configured in a project like this currently. The only important parts for this are the following: <view>pages/homepage</view>
<controller>Sulu\Bundle\WebsiteBundle\Controller\DefaultController::indexAction</controller> The Other content management system like example Typo3 are currently working on bringing support to different "Template Engine" and a common Usecase CAnother usecase for a TemplateRendererInterface could be projects which are migrate currently from example twig to latte oder twig to blade or whatever around. A common class ChainTemplateRenderer implements TemplateRendererInterface {
public function __construct(
TemplateRendererInterface $twig,
TemplateRendererInterface $blade,
) {
}
public function render(string $templateName, array $context = []): string
{
if (str_ends_with($templateName, '.twig')) { // or using $this->twig->exists
return $this-twig->render($template, $context);
}
return $this-blade->render($template, $context);
}
} This way without changing the whole dependency injection a project can easier step by step migrated to a new template engine. @taylorotwell I hope I could give you here better understanding about advantages of a common TemplateRendererInterface. Let me know about your thoughts about it. Also what you are thinking a RendererInterface should look like. As their are already some discussion about using objects instead of I want to thank all again about attending here and give their opinion on this topic 🙏. I also learned a lot, that there are more template engines as I thought and they focus on a lot of different things, Which is really interesting and just let me want more to get them under a common interface. |
I personally see value in this from a couple of perspectives:
The point is allowing the ecosystem to be agnostic about the implementations, so that they can be used in more than one context. I see a ton of libraries out there that simply adapt existing libraries to work under a specific framework. Interfaces such as this one would make it easier to just consume the original library without a wrapper. |
Something I don't think I've seen in the discussion yet: we don't have to change all the libraries' and frameworks' rendering engines, they can just provide (façade?) adapters that satisfy that interface. |
And even if you change them, it would be the public API anyway and not
their internals.
|
@Voltra sure that is always possible in most cases and currently also the way I'm experimenting with the exist template engines by going over adapter: https://github.com/schranz-templating/templating/tree/0.1/src/Adapter. And test and try out how the different template engines work in other frameworks and how they can be implemented. In the last days I also did found some other template engines which I was not yet aware of. PHPTAL and older engine based on xml based syntax (reminds me a little bit on how vue and angular works today via xml attributes) and Brainy a fork of smarty template engine. Both seems be maintained and got releases lately. Latest release from PHPTAL by @Ocramius and Brainy by @DavidSchnepper and (maybe you want also join this discussion). I already experimented with the engines by implementing an adapter: I'm also trying how different template engines can be used in different frameworks. Currently already got the following framework the following template engines included and working over the Symfony
Laminas
Laravel
Next one I want to have a look is Mezzio and how there the different engines could be integrated or used, I mostly want better understand the frameworks and in which direction they go with template engines and see if such an Interface is even possible. I still think it would be a great addition to the PHP eco system to have general interface for template engines to make integration of different libraries into different frameworks easier. And agree with the points mention by @weierophinney. |
Brainy isn't in active development. It was a fork of Smarty made several years ago by Box employee @mattbasta, who still does some maintenance on it (and it's still in use at Box.com). |
I just encoutered this proposal, and first of all I like it :) I see this is a bit old but hopefully not completely abandoned yet, and so I wanted to add my 50 cents. I agree there is no need to standardize the template names and I have real package that supports your cause. Namely the usecase A that you mentioned earlier - a library just wants to render some data and it doesnt care how or what rendering engine is used. This is the example you provided: $html = '<a href="' . $url . '">' . $url . '</a>';
if ($this->templateName) {
$html = $this->templateRenderer->render($this->templateName, ['url' => $url]));
}
$email = (new Email())
->to('[email protected]')
->html($html); See, in the code above See this modified version: $html = $this->template->render(['url' => $url]));
$email = (new Email())
->to('[email protected]')
->html($html); and whether a named tempate that uses an engine, whether a file or template name exists at all, where there is a default implementation that looks as below is no concern of the example library code. class MyEmailDefaultTemplate implements TemplateInterface {
public function render(array $data): string {
$url = $data['url'] ?? null;
return $url ? '<a href="' . $url . '">' . $url . '</a>' : '';
}
} Of course easy implemetation using a template engine would look like this: class EngineTemplate implements TemplateInterface {
public function __construct(private TemplateRendererInterface $renderer, private string $templateName) {}
public function render(array $data): string {
return $this->renderer->render($this->templateName, $data);
}
} Here is the It is just a very simple tool but it shows a practical example of the hypothetical usecase that you provided. Hopefully this is an interesting viewpoint on the problem at hand. Cheers, and once again I hope this initiative is not dead, because i really like the proposed interface. |
Hi @slepic, yeah that is sure one usecase I had in mind for a TemplateRendererInterface. I created a little prototype around it here: https://github.com/schranz-templating/templating After following the next steps of Twig Template Engine. I'm rethinking about the return type. While So maybe the better return type would be a TemplateRendererInterface::render(string $template, array $parameters): iterable<string> $templateParts = $this->templateRenderer->render('template', ['params' => 'value']);
foreach ($templateParts as $templatePart) {
echo $templatePart;
} But I don't like that for the end user is ideal case. Another possibility would be have 2 methods |
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:
The
$template
does not need to be a real filePath. The used template enginejust need to support it. As an example in twig like template path
@namespace/directory/file.twig.html
is supported or for blade something likewebsite
, or latte something likemail.latte
. So$template
the is a string. It was considered not to be an object givento the
render
method as such complexView
class things like example inlaminas 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 whichconverts
$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 aTemplateNotFoundExceptionInterface
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 bufferSupports Stream: true
Render a template:
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:
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 bufferSupports Stream: true
Render a template:
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
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:
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 ?
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 propertiesReturn Type Hint:
string
Supports Stream: false ?
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
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
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
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
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: ?
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:
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 objectsContext
should be objectsTemplateRender
should stream to output even asynchronityTo 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