--
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+unsubscribe@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/16475a0c-f46b-4478-ac99-5e9003a97d19%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
// my-template.php
echo "Hello, {$c->get('name')}!";
// Elsewhere
$path = 'my-template.php';
$template = new PhpTemplate($path); // TemplateInterface, currently ContextRendererInterface
$context = new DataObject(['name' => 'Xedin']); // This is a ContainerInterface, see dhii/data-container-interface:dev-develop
$block = new GreetingBlock($template, $context); // The context can of course be determined in a different way
$mainContext = new DataObject('greeting' => $block);
$mainBlock = new HtmlBlock($mainContext); // Some super block for an outer template, implementation detail really
// Outer template
<div class="greeting">
<?php echo $c->get('greeting') ?>
</div>
$file = new Stream('my-mustache-template');
$template = new MustacheTemplate($file);
$context = new DataObject(['name' => 'Xedin']);
$outputStream = $template->render($context);
This template engine uses neither:
https://github.com/mindplay-dk/kisstpl
I'm sure you won't care because it's not popular or well-known ;-)
We use this at work because:
1. code-sniffer inspections and plain PHP templates are a requirement - view data dictionaries get in the way of that, and
2. dynamic template-selection is a requirement (enabling template overrides across modules etc.) and hard-coding template paths gets in the way of that.
I know this is not the mainstream approach to templates ;-)
$template = new PrintfTemplate('Hello, %1$s!');
$context = new DataObject(['Xedin']);
$greeting = $template->render($context); // Hello, Xedin!
--
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+unsubscribe@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/c9192cc6-281e-44e7-98cc-ba2b5c1683c7%40googlegroups.com.
<?php
interface MetaDataRenderInterface extends Dhii\Util\String\StringableInterface
{
public function getMetaData(); // Or whatever else you require
}
Something else could be missing: metadata attached to the rendered result, like caching tags, contexts, which need to be gathered when assembling render results to build an actual HTTP response (coming from a Drupal "render array" background, as some can guess).
2017-09-25 8:21 GMT+02:00 Xedin Unknown <xedin....@gmail.com>:
I believe that's a great argument for not including the template *source* in the description. A template instance can encapsulate `vsprintf()`, for example, and there's never going to be a file or a path resolution. This is all implementation detail that can be handled via IoC.
$template = new PrintfTemplate('Hello, %1$s!');
$context = new DataObject(['Xedin']);
$greeting = $template->render($context); // Hello, Xedin!
On Monday, September 25, 2017 at 8:15:27 AM UTC+2, Nicolas Grekas wrote:> Granted, the majority of template engines use a template file-name and a set of name/value pairsTwig also doesn't reference templates by file name.It references them by logical name instead, and uses loaders to resolve that to whatever storage you'd like to use for the template strings.
--
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+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
In the same way that http-interop did, I created an output-interp organization on github, https://github.com/output-interop
The spec that is currently published was founded on the work that Giuseppe Mazzapica started with Foil PHP https://github.com/FoilPHP/Foil as I found it an excellent basis for a rendering system.
`Template#file()` seems extra to me, because it breaks ISP.
`Engine` also looks very non-segregated to me.
Could you elaborate on the usage of `Context`? I couldn't understand how it could be used.
I use an interop context in the form of `ContainerInterface`.
$sourcePath = 'my-template.php';
$destinationPath = 'my-template.cache';
$destinationStream = new WritableFileStream($destinationPath);
$template = new PhpTemplate($sourcePath);
$template->render($context = null, $destinationStream);
If the `$stream` parameter is provided, output will be written to it, and the returned value will evaluate to an empty string.
If I may give my two cents, I also find anything that has to do with files extra.
For reference to our own standard:
Having given it quite a bit of thought, and passing this by @mecha, I would like to provide some feedback with regard to working with streams. As a disclaimer, I would like to mention that I do not make solving a specific problem the target of this standard; however, I do want to make sure that specific problems _can_ be solved. I believe streaming to be one of them, and I guess @David would agree with me.
I really dig the need for streaming, as the rendering output can become really really huge. It may certainly be preferable to "cache" the output to disk, and then send it to the server in a more appropriate manner. As an example, imagine that your software needs to print a very large confirmation document, such as a receipt, which contains a lot of detail about every item. Streaming would be extremely useful here, because it allows you to occupy only a small amount of memory at any point in time, while still handling the rendering like a boss using the same algorithm. Duh. Cool.
The problem is that, because the template is responsible for rendering, and the return value (stream) is responsible for retrieving the value in chunks (they are two separate things), the stream would need to be buffered. Buffered streams work by having something write to the stream at the same time as something else reads from it. And this can work really well in some cases. However, it only works in cases when rendering is done in steps. When you are rendering PHTML or Mustache, there are no steps, e.g. you cannot tell the engine to render only the first e.g. 256 bytes of the _template_. You can only buffer output in chunks, and this can easily be done by using the 2 parameters of `ob_start()`, e.g. `ob_start('function_that_writes_to_disk', 256)`. This means that the events are coming from the stream, instead of the consumer of the stream. This conflicts with the whole philosophy of the stream standard, whereby it provides the consumer a way to "request" a number of bytes from it. In this case, a buffered stream would still have to contain the complete output at some point. A sub-optimal solution to that could be using the `php://temp` stream, which would start writing "overflowing" bytes to disk. However, this is not the best solution, since it will make the application slower, and most importantly ignores the advantage of the output callback, which _should_ be used for writing to the destination.The solution, in my opinion, lies in the assumption that a stream is something that `TemplateInterface#render()` should _return_. If, instead of returning a readable stream, `render()` could _accept_ a *writable* stream, the problem disappears on its own.
$sourcePath = 'my-template.php';
$destinationPath = 'my-template.cache';
$destinationStream = new WritableFileStream($destinationPath);
$template = new PhpTemplate($sourcePath);
$template->render($context = null, $destinationStream);So, here we have a signature which suggests that in certain cases, a second parameter should be accepted by the `render()` method. I would say thatIf the `$stream` parameter is provided, output will be written to it, and the returned value will evaluate to an empty string.
Always having to return an object might be bad for the developer experience
There's no need for one solution fit them all
But not worth the effort in 99% of all cases.
There should be a defined return type that's not changing based on the parameters passed
Returning a readable stream is just fine, you just need to figure out how to handle backpressure
Hi Niklas,Always having to return an object might be bad for the developer experience
Which is why I allow an object that can be cast to string. Please take another look at the code. Also, only allowing a primitive type is probably also very bad.
There's no need for one solution fit them allBut not worth the effort in 99% of all cases.FIG is widely regarded as the de-facto PHP standards body. The solution should at least try to fit all. IMHO, if you release enterprise-grade standards to be used by hundreds of thousands of people, you should at least make sure that common serious tasks can be performed, instead of understanding 6 months down the line that the new standard is useless for the big players.There should be a defined return type that's not changing based on the parameters passedThat's right, the type doesn't change. The returned value simply evaluates to an empty string, like I wrote. An empty string is also a string.
Returning a readable stream is just fine, you just need to figure out how to handle backpressureI feel like there's a lack of understanding here somewhere. Allow me to re-iterate.The way to use readable streams is to request a certain amount of data from them by using e.g. `read()`. This makes the stream's consumer the initiator of the "event". `read()` needs something to take the data from, whether an open connection or a variable. But output of templates such as PHTML does not happen like that. You cannot request a certain number of characters from it; instead, you request it to be rendered, and *handle* a certain number of characters of output at a time.
This is how you handle the "backpressure" - in a kind of event-driven way.
But like this, the template is the initiator of the event, instead of the consumer. Therefore, they must sync somewhere, presumably in the stream's internal buffer. However, the operations with it do not happen in a "write - read - write - read" way. Instead, in order to utilize this approach, the whole output would need to be buffered, and then read - perhaps in chunks. But the buffer's size can grow unpredictably large.Also, the stream that writes to e.g. a file handle must already have that file handle when output is available to the buffer. But the `render()` method doesn't know where to write to. Given a stream, it would be able to write to any destination in an abstract way. Without this, it just doesn't seem possible. If you believe it is, perhaps you could demonstrate?
On Thursday, September 28, 2017 at 9:03:24 AM UTC+2, Niklas Keller wrote:Having given it quite a bit of thought, and passing this by @mecha, I would like to provide some feedback with regard to working with streams. As a disclaimer, I would like to mention that I do not make solving a specific problem the target of this standard; however, I do want to make sure that specific problems _can_ be solved. I believe streaming to be one of them, and I guess @David would agree with me.I don't agree. There's no need for one solution fit them all. Always having to return an object might be bad for the developer experience. On the other hand, it's mostly some library that implements the interface, so it might not really matter.I really dig the need for streaming, as the rendering output can become really really huge. It may certainly be preferable to "cache" the output to disk, and then send it to the server in a more appropriate manner. As an example, imagine that your software needs to print a very large confirmation document, such as a receipt, which contains a lot of detail about every item. Streaming would be extremely useful here, because it allows you to occupy only a small amount of memory at any point in time, while still handling the rendering like a boss using the same algorithm. Duh. Cool.But not worth the effort in 99% of all cases.The problem is that, because the template is responsible for rendering, and the return value (stream) is responsible for retrieving the value in chunks (they are two separate things), the stream would need to be buffered. Buffered streams work by having something write to the stream at the same time as something else reads from it. And this can work really well in some cases. However, it only works in cases when rendering is done in steps. When you are rendering PHTML or Mustache, there are no steps, e.g. you cannot tell the engine to render only the first e.g. 256 bytes of the _template_. You can only buffer output in chunks, and this can easily be done by using the 2 parameters of `ob_start()`, e.g. `ob_start('function_that_writes_to_disk', 256)`. This means that the events are coming from the stream, instead of the consumer of the stream. This conflicts with the whole philosophy of the stream standard, whereby it provides the consumer a way to "request" a number of bytes from it. In this case, a buffered stream would still have to contain the complete output at some point. A sub-optimal solution to that could be using the `php://temp` stream, which would start writing "overflowing" bytes to disk. However, this is not the best solution, since it will make the application slower, and most importantly ignores the advantage of the output callback, which _should_ be used for writing to the destination.The solution, in my opinion, lies in the assumption that a stream is something that `TemplateInterface#render()` should _return_. If, instead of returning a readable stream, `render()` could _accept_ a *writable* stream, the problem disappears on its own.
$sourcePath = 'my-template.php';
$destinationPath = 'my-template.cache';
$destinationStream = new WritableFileStream($destinationPath);
$template = new PhpTemplate($sourcePath);
$template->render($context = null, $destinationStream);So, here we have a signature which suggests that in certain cases, a second parameter should be accepted by the `render()` method. I would say thatIf the `$stream` parameter is provided, output will be written to it, and the returned value will evaluate to an empty string.Sorry, but what? There should be a defined return type that's not changing based on the parameters passed. Returning a readable stream is just fine, you just need to figure out how to handle backpressure. You might want to have a look at https://amphp.org/byte-stream/, which is async, but the same thing applies to sync, just that you can skip the promises.Regards, Niklas
--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/w1cugJ9DaFg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+unsubscribe@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/a9d36028-2354-43a9-83c2-7ca75f1b54eb%40googlegroups.com.
Let's not repeat the failure of PSR-7 using __toString(). It can't throw exceptions and it's impossible to handle errors in a sane way.
An empty string is worthless here. The return value doesn't matter and could also be null. If you pass a stream you change the behavior from returning to writing to a stream passed as a parameter. While it's true that a consumer always knows what will be returned, it's not possible for static analysis tools to know that. A separate method would be a better choice.
If you can handle a certain number of characters of output at a time, you can also implement it with a `read()` API. Just start rendering if it didn't start yet and return "a certain number of characters of output", continue rendering on the next `read()` request like you would after writing to the stream like you suggested.
Where's the event in a writable destination stream approach?
The implementation will probably involve a layer of indirection. It's probably simpler to go with a `WritableStream` as you suggested, but I prefer separate methods for that as mentioned above. It's trivial for an implementation that supports streaming to provide an implementation that renders to a string and the other way around.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.