KBD

Keith Devens .com

Saturday, August 30, 2008 Flag waving
Everybody makes their own fun. If you don't make it yourself, it ain't fun -- it's entertainment. – David Mamet (as relayed by Joss Whedon)
← Send all requests to a single script with mod_rewritePHP has too many functions in the core →

Daily link icon Wednesday, November 12, 2003

Design of my CMS, or Template problem

I've been writing "version 2" of the software that runs my web site. While going down the mod_python route, I initially thought that the ability to override any Apache handler would give me a lot of power. Then I realized that the only handler I'd probably override would be the default one, and I'd get dumped to the same handler that, for instance, PHP starts you in. In addition, most of the changes I really wanted to make had to do with the design of my CMS and were not really dependent on what language I was using. Though I still hate PHP and all its problems, version 5 is coming out soon, and that will take care of a lot of what's wrong with it[1]. I still like Python much better as a language, but as of version 5 PHP should at least be tolerable again. Finally, I'll miss out on getting to store persistent data like I can with mod_python, but for now I'm going to be sticking with PHP for my site.

Now, for version 2 of my CMS[2] I've changed a lot from the current version, and a lot of things are simpler. Here's a basic outline of the structure:

  1. All requests on my web site that aren't for an existing file or directory get sent to dispatcher.php.
  2. dispatcher.php instantiates my CMS class and calls $cms->initialize() to have the CMS set itself up.
  3. The CMS, contstructor sucks in all configuration data and sets up the environment (like $_SERVER['SCRIPT_NAME'], $_SERVER['PATH_INFO'], etc., since they won't be right because of dispatcher.php sucking up all requests). initialize() essentially runs any "application" you specify, and runs any "actions" if a form was submitted for which there is an action.
  4. After initialize(), the dispatcher prints out any HTTP headers and displays the root template. The root template can have named "sections" to fill in with whatever the configuration file says. So, you can have nested templates.

That's the life-cycle of a request in my CMS. As for terminology: an "application" is code that is responsible for handling the request. Really, only one piece of code can know what to do with a given URL (for instance, my weblog application handles any URLs that are anchored at /weblog/ on my site). Though, it's possible that more than one application can be "anchored" at a given base URL, so applications are called in order until one application "accepts" the URL and does its thing. If no applications accept the request, then that's a 404.

An application's main job is to set up the "state", which involves the CMS being in one or more "modes". A mode can be something like "weblog.day" or "weblog.month" (each mode, as well as many other things within my CMS, must be prefixed by the appropriate "namespace" -- in this case 'weblog'), and the mode allows the CMS to determine which templates should be displayed, depending on the configuration. An application can also pre-cache any data it wants to be around for later, tell the CMS to redirect the browser to somewhere else, specify any particular HTTP headers it wants sent, control caching, set things like the title of the page, set up the "breadcrumbs" you see at the top of a page, etc.

Template philosophy: Each template should have no business logic in it. That's different from display logic. I like Cheetah's philosophy: "Logic that belongs in the front end shouldn't be relegated to the back end simply because it's complex." But essentially, a template should consist in displaying variables that have already been set up by the environment[3], and specifying that a given "section" should be filled in "here" so you can nest templates arbitrarily.

Now, there are definitely tasks that only have to do with display (front-end code), but are complex enough that you don't want to have the code right in the template itself. Two examples are my "breadcrumb" display code ("Home > Weblog > etc.") and the code that displays my list of tabs at the top of the page. But you can think of many other examples. For instance, displaying the elements of an array as a list in HTML, or maybe a nested array as a table. The point is that you should be able to have reusable front-end components. How that's done isn't really important at this point, but it's important to note that that's an important capability for template tools to have. I also don't have a problem with including other templates by name within a template (this could be how reusable template components could be included, though this would require each one be in its own file), but usually I prefer to have named "sections" that can be filled in with whatever your CMS configuration dictates.

Finally, the last important capability for a template tool to have, besides the ability to display variables, is the ability to format those variables. Formatting is completely the business of the front-end, and your template tool should allow you a lot of flexibility. Smarty seems capable in this regard. Formatting tasks involve formatting things like dates and numbers, or rendering StructuredText, for instance. A template should also have the ability to set variables that last throughout the template (if you want to only type a color, or maybe a date format, once, for instance), and to get metadata about other variables, such as the number of elements in an array.

Oh, one more thing. Templates should have different "rules" for template code than for programming language code. For instance, some lanugages complain if you use a variable that hasn't been assigned or declared. Templates should be very lenient in this regard... Even if something relatively bad happens while a template is running, system error messages shouldn't propagate through to your front-end code unless you specifically choose to display them.

That's the philosophy... as for my CMS, any data that an application decides to make available, or any base settings (like, what's included in the configuration file), are automatically available to the template and can be used as variables. However, there are data that you don't want to always have around, and should only be available on request (such as a weblog entry). The problem is that you need a way for the template to request this data.

I originally had expected to have an area in my configuration file where, right next to where you specify the template, you specify what data the template needs (that isn't already provided by the application or the environment). However, this breaks the DRY principle. Because the template already knows what data it needs, you shouldn't have to specify this information in more than one place. You may wind up changing your template so that you wind up retrieving more data than you need, or you may wind up wanting data in your template that you never specified in your config file.

Movable Type seems to allow this with their template system. You say what you want in your template, and the system makes it available to you, no questions asked. Of course, the templates aren't interpreted dynamically on every request.

So, that's my major problem right now: how to have the template report what data it needs. Something like the following could work: while the template is being evaluated, if the variable is empty, request the data from the CMS and cache the value for next time. However, no template systems I know will allow this. I can still specify what data each template need in the configuration file as a last resort, but I'd rather not do that. Another way this could work is if a template system operated by compiling the template into a class, it could have a method that returned a list of the data it needed, which could then be injected into the object at runtime before the template is executed.

Footnotes:
[1]: If anyone could give me an educated estimate of when you expect PHP 5 will be released, I'd appreciate it if you'd leave a comment
[2]: I keep calling it a CMS, but really it's more of a "web application framework", meant to enforce MVC, etc. Think Struts with a few built-in applications like a weblog and a wiki
[3]: If you have a powerful enough language, of course, your "variables" could really be objects, iterators, or generators, etc.

← Send all requests to a single script with mod_rewritePHP has too many functions in the core →

Comments XML gif

Harry Fuecks (http://www.phppatterns.com) wrote:

Keith,

If you can hold up a little, the first release of WACT is scheduled for end of this month. I firmly believe it has the most advanced template engine out there and's it's very fast.

To whet your appetite: ASP.NET compatibility - by the end of the month, expect alot more examples in there... (feel like designing your site with Visual Studio while still using PHP?)

∴ Harry Fuecks | 12-Nov-2003 5:22pm est | http://www.phppatterns.com | #3250

M. Bean wrote:

Jesus dude... TLDR... T-L-D-R.

∴ M. Bean | 12-Nov-2003 5:24pm est | #3251

jan wrote:

This is very interesting XUL CMS:
Elixon CMS

Looks realy great! XUL future is sweat! :-)

jan

∴ jan | 20-Jul-2005 6:52am est | #7931

Feel free to post a comment below. Please see my comment policy.

Formatting Rules (No HTML):

  • **bold**, *italic*, _underlined_, --strikeout--
  • "text"="url" creates a link, and URLs are auto-highlighted
  • Blockquote: Like e-mail, begin paragraph with > (greater-than sign)
  • Lists: begin paragraph with *,-, or + (unordered), or # (ordered)
  • Code block: ?!code:language=perl|php|sql|javascript|etc.{\n}...{\n}?!/code

:
(will be your IP address if blank)
: (optional)
(Will not be shown on site)

: (optional)
:

August 2008
SunMonTueWedThuFriSat
 12
3456789
10111213141516
17181920212223
24252627282930
31 



RSS feed RSS feed for Keith's Weblog
Atom feed Atom feed for Keith's Weblog
Weblog archive
Recent comments
  on 2 posts

Recent comments XML

new⇒Johnny Walker Blue Label

Wow, thanks for the scotch review​:D

Lagavulin and Laphroaig are​some of...

Keith: Aug 29, 3:35pm

Girls, please don't get breast implants

Wow, After all this time, the​comments on this page continue to​grow. It wa...

Ajeet: Aug 25, 2:36am

Generated in about 0.138s.

(Used 8 db queries)

mobile phone