My new blog software uses a PHP template engine for its presentation layer. Because the templates are just plain PHP, you can put any logic in them that you like. The point here is that presentation requires logic, and if you limit the logic that your presentation layer can do, you have to make up that deficit somewhere else. For example, the template engine can use functions like PHP’s date() function to format dates that are to be displayed. If you didn’t use PHP for the template engine, you would either have to pre-format the dates before passing them to the template, or implement some method in your own template engine for providing this functionality, which in both cases adds to code bloat and in the former makes your code much less clean.
A template engine which is just PHP makes it possible to implement ‘Model View Controller’, or something like it. In this case, the ‘View’ is your template. The template is 90% HTML, and has PHP embedded in it whenever required. As described in the article about the template engine I’m using, the syntax for doing this is pretty light (and not much more complex than most other template engines).
I’ve implemented a simple controller system that ties in with the templates, and here is a brief description of how it works. The controller is the object that handles the incoming web requests. In my implementation, you can have sub-controllers to which the request can be handed off if needed - this works very well with the template system, because templates can be very easily nested.
The controller is responsible for checking any input and deciding what to do. It will usually work in conjunction with one main template, and it does two things for the template:
- It has to get any data from the database that the template needs. This is passed to the template with a call like $template->set('bloglist', $list);, and since the template can do advanced formatting, the controller passes the data fairly ‘raw’. In my code, this happens in the function handleRequest().
- It provides functions that the template will need to generate its output. For example, the ‘bloglist’ template needs to be able to generate permalinks, but the knowledge of how to do this lies with the controller. So the controller has a function permalink() which accepts a post ID as a parameter, and returns a URL.
How exactly the controller and template are wired together, and by whom, is flexible - for example a controller could be passed a template to attach to, or could choose the template for itself. The strategy will often depend on whether the controller is the main one for the page, or a sub-controller. If the template needs ‘callback’ functions from a controller, then at some point setController() needs to be called on the template, passing a reference to the controller.
After all the controllers are done with processing, the fetch() function on the main template is called. The first thing the template does is call fetch() on any embedded template variables, replacing the nested template with its string output. It then executes the template file (which is just a case of include() ing it), and returns the output from this as a string. The string (hopefully now a complete web page or RSS feed) is then usually echoed back to the web browser.
A diagram is shown below, that shows the case with just a single controller and template. Note that the instantiation start lines are dotted because which is actually instantiated first is flexible. Also, whether the main controller calls the fetch() method on its template, or whether that’s called from outside, is flexible too so that’s also represented as ‘fuzzy’. With nested templates and/or controllers, the basic flow is just the same, and the first fetch() is done when all the controllers are done with their work.
In some cases, whether the controller should ‘push’ data to the template or the template should get it from the controller using functions is debatable too - you have to use your judgement.
The output of all this is a string, which gives you amazing flexibility. It means that you can use one of these templates in any other PHP page which isn’t based on this template system - just echo the output of fetch() at the appropriate place. You could also save the output to disk, or do string replacements, or HTML validation, or e-mail it to the user as well etc. etc.
One of the things I’ve stressed here is the flexibility of the system, and the low cost barrier - an include that is only 40 lines of PHP, and uses normal PHP for templating, isn’t going to break the performance bank, nor does it force you into a certain way of working. Also, because (with the normal way of using them) nothing is output until everything is calculated, the controllers can change their mind at any point and, for example, redirect you to a different page, or send cookies and headers at any time.
Another advantage of this method is simplicity - the lifecycle of a web request is in just two distinct phases - process input, create ouput - and following it through is very easy. These phases correspond to the application logic and presentation logic parts of the process respectively, and the code for these two is separated physically as well. Compare this to the Control Execution Lifecycle in ASP.NET that you have to understand to develop anything complex. The ‘event’ driven model might seem to have some advantages, but it brings a multitude of entry points to your code, and working out what order events happen it etc. becomes a total nightmare, and brings all the disadvantages and complexities of Windows like programming into what is essentially a very simple process.
Hopefully that helps to understand the basic system. I’ve more examples to bring of how this works in practice, and some other techniques you can easily add (such as simple form persistance).
In conclusion, I believe this is a great way to develop web pages. The only time you might have difficulties is if you have a team of web designers who really don’t want to see a jot of PHP. My templates freely mix PHP and HTML, provided the PHP is for presentation logic only. This usually means it is usually pretty simple PHP, and which bits are HTML and which are PHP is very obvious, but sometimes that won’t be enough to keep designers happy.