weierophinney edited this page Sep 14, 2010 · 2 revisions
Clone this wiki locally

phly_mustache is a Mustache implementation in PHP 5.3.

While the Mustache landing page links to a PHP implementation already, I found several things lacking in that implementation:

  • Lack of namespacing. Call me old-fashioned, but everyone should be using a vendor namespace of some sort; it makes the class easier to include in a set of library classes, prevents naming collisions, and more.
  • Odd organization. The unit tests depend on the examples; why not have the unit tests be the examples? (Did I mention I’m a bit of a testing zealot?)
  • Mixing responsibilities. A single class was responsible for both compiling the template, as well as rendering it. This feels difficult to extend, as well as potentially brittle later as new requirements are added.
  • Pragma handling requires extending the base Mustache class. This makes it difficult to mix-and-match pragmas.
  • Known issues with things like delimiter changes indicate potential brittleness of the lexer used.

Examining it more closely, it does a fairly good job of lexing. However, I noticed some immediate issues. Because it’s using preg_match with offsets, it becomes difficult to limit the scope of tokens. For instance, setting delimiters or pragmas within a section should not affect anything outside the section, nor within partials. One known issue in the implementation is scoping of the delimiter changes — which is a symptom of the lexing approach. Additionally, each and every template gets compiled each and every time it is compiled — instead of being cached in memory. If a partial is used many, many times (and quite often partials are used within loops), this will lead to a lot of unnecessary overhead. Without a caching mechanism, you lose the benefits of a compiling lexer as well; the point of compiling is to do it once, and re-use the tokens created many times.

My approach has a potentially slower lexer (I step through the template one character at a time), but one that is more scope-safe, and one that allows for caching of tokens. It also avoids a number of potential recursion issues when using partials.

The architecture is as follows:

  • Lexer: tokenizes a template
  • Renderer: parses token list and renders tokens using the view as context
  • Pragma: interface for pragmas, which are used by the renderer to alter how the template is rendered and/or how the context is interpreted.
  • Mustache: facade/gateway class. Tokenizes and renders templates, caches tokens, provides partials aliasing, and acts as primary interface for end users.

This leads to more classes, but also a more granular structure which makes configuration and extension simpler. Want to create a new pragma? Create a class, and attach it to your renderer. Want automated caching? Extend the Mustache class, and build it in. Want to alter the tokenization process? Extend the Lexer, and inject your implementation into Mustache.

How can you help?

Browse the TODO file, or grep the source for ‘@todo’ annotations, fork the project, and send a pull request. Alternately, help write end-user documentation and examples of different operations you can perform. Do some code review on the project, and let me know where I can improve; just as I strive to improve on the other implementation, I’m sure there’s room for improvement within mine (hint: I think the compile() method of the Lexer can likely be made more granular and extended to allow additional tokens).