My vision for Fluid
Estimated 15-20 minute read. Many perspectives included.
As some of you know: a while back I created a standalone version of the Fluid template engine and started banging the Twitter drums a bit about it. Since then, I have begun work on making this standalone version of Fluid a proper composer dependency of TYPO3. While this may not seem like a high priority task, it has far reaching implications in the vision I have for the future of Fluid.
Right from the start the goal has been not just to decouple Fluid from TYPO3/Flow - it has just as much been about extending and modernising the base Fluid so many, many adaptations become possible without modifying the engine itself. Humble brag: I spent a great amount of effort on making these things possible and the result is that by modifying implementations of classes that Fluid uses, one is able to modify nearly every aspect of how Fluid is processed - down to which arguments a ViewHelper supports and adding virtual ViewHelpers.
There's so much I would like to tell you about what new options have become available - but it's not exactly relevant here. Instead, I refer you to the (temporary) repository for the standalone Fluid and the documentation it contains:
Before the vision part, first a brief status and the plans so far:
- There is a modified TYPO3 source (continually rebased) which implements the standalone Fluid: https://github.com/NamelessCoder/TYPO3.CMS - you can pull this,
composer installand perform the usual install steps to test it out. At the time of writing this, approximately 80% of the functionality is either supported by standalone Fluid or implemented in system extension
fluidwhich has now become an adapter containing TYPO3-specific implementations for Fluid and a range of TYPO3-specific ViewHelpers.
- There will be a very similar Flow adapter, but the work on this has not yet started.
The standalone Fluid was initially extracted from Flow, which means that once successfully implemented in TYPO3 a similar implementation in Flow should be just a formality. So those are the plans - but what about the vision? Why do we do this?
Obviously we wish to share Fluid as a true open-source and cross-project template engine. I for one am not ashamed to point to Twig as the main inspiration for this. But there are also other visions that I never communicated. Let's change that now.
Fluid is a template engine - but also...
Fluid can be seen as much more than a mere template engine. In FluidTYPO3 we also use Fluid templates to store metadata along with the rendering instructions (this is what Flux's API handles). The metadata is constructed via a proper API that provides validation for the structure and values - and even auto completion in (select) IDEs. We use this metadata to construct a PHP representation of user input fields, grids etc. - and finally, this PHP representation is turned into TCEforms arrays which TYPO3 natively consumes.
Flux has multiple APIs to create these PHP representations:
- PHP objects
- ViewHelpers, of course (which become PHP objects)
- Arrays (which become PHP objects)
- TypoScript (which becomes arrays which become PHP objects)
What does this metadata and PHP representation actually contain? Well, things like the human-readable title of the template, an associated icon, a complex set of form fields in which to enter rendering parameters, a grid that defines where nested content can be placed and more along those lines.
In essence, Flux is theoretically able to consume and produce any number of additional storage types which it currently does not - things like JSON, YAML etc. are obvious candidates. And Flux would also be able to produce not just TCEforms arrays, but other types of configuration (runtime or static) required by a framework to both configure (via UI) and render the template. I admit you probably have to be a developer yourself to fully appreciate the avenues this approach opens up, but hope that you are currently happily busy making mental connections between your favorite framework products ;)
The vision for Fluid
Without further ado and hoping you had sufficient back story from the text above:
In my vision, all major PHP frameworks which contain CMS-type components also have an adapter for Fluid to render the templates it uses for these components. Being able to render the same templates is the natural first step.
VHS is (partially) decoupled, making whichever ViewHelpers that can be generically used, available as a generic package (without changing the API it currently has).
Flux is then (partially) decoupled, turning the API components into a library.
Then, by using Flux as a key piece of middleware with the PHP representation as the standard which all others must convert to/from, we basically can make any framework able to consume any template from any other framework that renders using Fluid. Via Flux, metadata and backend-type UI structure can be extracted and converted as required by the framework.
We then make it possible to take a template which contains all the metadata that is required and transfer it to another framework. Naturally this isn't something that "just" becomes possible, but all the key pieces for truly portable templates would be there:
- The API used to define metadata in the template would be the same.
- The PHP representation extracted from the template would be the same.
- The PHP representation can be converted to any format or read directly into runtime.
- Custom ViewHelpers would load in other frameworks too (caveat: avoid injection!)
So Fluid is the template engine and Flux is the metadata engine that has an API for Fluid (among others). Combining the two lets us create portable templates that would theoretically be usable in any framework, given sufficient conversion logic between Flux's PHP objects and whichever configuration storage framework X may use.
The other side of this vision is that Flux, because it sits as middleware and can consume converted structures as arrays, might one day be able to "understand" custom NodeTypes from Neos or CMF (when CMF uses Fluid) and use those transparently as TYPO3 content definitions. While this other side may not appear as important it does mean an easier (read: an actually feasible) sync path from FluidTYPO3 to Neos, from Neos to TYPO3 and back again. That is, for the templates.
Other projects are then suitable for centralised storage of the content itself - see for example the ForgetIT/CMIS project - with which I am heavily involved, too. A toolkit consisting of Fluid and Flux adapters for Flow and TYPO3 plus content storage in CMIS would essentially provide full content and template reusability - same exact templates and content on both systems.
Rinse and repeat for all the frameworks that we, TYPO3, wish to benefit - and benefit from ourselves.
Exactly how portable can Fluid templates be?
Completely - I should assume. As mentioned above: it is only a matter of how many adaptations must be implemented in order to operate a template, including all ViewHelpers and syntax peculiarities, in exactly the same way on any framework that can use Fluid. Templates can be pre-processed to prepare them if there are gross incompatibilities. Custom syntax parsers can be attached and built-in ones can be disabled. ViewHelpers can be silently replaced, aliased, delegated - there doesn't even have to be an actual class: 100% virtual ViewHelpers are now possible. Each View instance can use exactly the implementations and adaptations it needs.
Without going into too much detail, standalone Fluid provides ways to effectively change or freeze the parser's behavior. For example, this would allow CMS's Fluid adapter to detect which version of Fluid implementation to use for each template - thus allowing any number of iterations of features and feature sets to exist simultaneously, as pluggable implementations.
This is also one way that Fluid can become even more uniform when used on multiple platforms. One does not necessarily have to change a ViewHelper or override the parser to operate outdated templates. To do so, maybe all you need is to write a simple class that translates, delegates, converts etc.
Like I said in the start: a great amount of effort has been put into this type of resiliency. While it may seem like overkill for implementation requirements in a couple of frameworks, it should start to make sense when there are a dozen frameworks, some with LTS, that all support Fluid. Which there will be - of course ;)
Self-criticism and personal notes
I mention Flux a lot in the vision above - but this is merely for practical reasons. If we wish to go in this direction, Flux is simply a prime candidate but might not be the only one. It has the benefit of already being done-done, fully unit tested and matching very closely with existing capabilities of TYPO3 (which after all is were our focus lies). Flux has, to some developers, been seen as somehow controversial - and I can see why. It is, when you boil it all down, more APIs on top of an existing API and when you put it like that, it does sound somewhat mad.
But a main point of Flux was to make writing TCA as concise as possible while validating the structures - allowing users that don't know about the complex arrays to use the API with a strict guide and much less boilerplate configuration. This very same abstraction of APIs is what would also allow us to use Flux as glue for so many other bits and pieces. Being able to put this abstracted API in the Fluid templates themselves is just adding to the portability.
Regarding the pretty obvious challenges in just the scale of such an undertaking: actually realising this vision for TYPO3 Fluid depends on other people sharing this vision. If you feel motivated by what you've read here - then please don't wait one second before you get in touch with me. At this point, even proofs of concept are of great value!