I am lazy (loading)

I ran into a few snags while playing around with codeigniter. The first was the realization that it was actually a subset of the work that was originally pmachine.

Now perhaps I'm being slightly unfair, since I didn't have a lot of hands on experience back in the day, but from my role as a sys admin for a web hosting company, pmachine was the early candidate for "ugh, yet another security hole", and whenever I hear that word I cringe at the awful memories.

Luckily PHP-Nuke came around not long after and took all the major security issues and seemingly put it into every release they made.

That being said, codeigniter does look pretty solid, the source code I've managed to review is well commented and fits my goals of lightweight, robust, with an attention to security.

Everyone and their pet iguana have a template engine

The next snag I ran into was their templating system. Codeigniter ships with two basic systems, one is just using PHP as your template engine, which of course is as fast as you get, but leads to tag soup.

The other system is a very basic find and replace, that doesn't provide any sort of view logic. In short, it's basically unusable for anything slightly serious.

I've worked with a number of template engines over the years, including Smarty, but most of them seem to either be way too bloated for my needs, or don't build templates that validate.

PHP5 has ushered in a new round of engines which look to solve both those goals. PHPTal is a "port" (or was) of Zope's template system. Of course when you're stuck still developing for PHP4, that presents a sad reality.

I'm unsure at this point if I'm going to bother trying to roll my own. There's a lot to be said about PHP developers constantly trying to reinvent the wheel, and I suspect some of that comes from not being able to fully embrace PHP5 (an odd thing to say, with PHP6 right around the corner).

Ideally for me, a template engine should:

  • Be lightweight, and convert into native PHP for caching purposes
  • Implement very minimal view logic (conditionals)
  • Avoid tag soup, and have templates pass validation
  • Allow for easy lazy loading

Isn't being lazy bad?

Lazy loading allows you to call data only when it's needed, which can save unnecessary processing.

Here's a quick example:

/*
 *  Let's pull the main data we need out of a
 *  database from a fictional forums application,
 *  using a fictional Active Record style access
 *  layer.
 */
  function get_threads($forum_id) {
    $query = $DB->get('threads', (int)$forum_id);
      return $query->results();
  }

We can imagine that the basic thread information (topic, date it was created, user it was created by, etc...) is contained in that result set.

What if we wanted to include all posts in that thread though? For the sake of example, let's assume our database is fully normalized, so having the post count in that table was not a design decision we chose.

Well easy, we might just throw the result set into a decorator:

...
  return new ThreadDecorator($query->results() );
...
/*
 *  Here's our thread decorator, which just adds
 *  the post count to our result set
 */
  class ThreadDecorator {
    
    function __construct($dataset) {
      foreach($dataset as $key=>$data) {
        $dataset[$key]['post_count'] =
          $Thread->get_post_count();    
       }
    }
  }

In this example we can see that we've passed our result set to the decorator, which then iterates over the results and adds a new field to each row.

The problem here being, if "get_post_count()" requires another query, we've just created piles more queries, and we don't even know if we'll need the thread's post count value yet.

Now consider this example of an expanded decorator which uses overloading instead:

/*
 * we'll use PHP5 overloading syntax for convenience
 * since PHP4 overloading is... a bit sketchy.
 */

  class ThreadDecorator {
    private $dataset = array();
        
    function __construct($dataset) {
      $this->dataset = $dataset;
    }

    function __get($field) {
      if ($field == 'post_count') {
        return $Thread->get_post_count();
      }
    }
  }

So what we've done is delay the retrieval of data, so that it is only accessed when we actually need it. Now, if the view (template) calls for the 'post_count' information, it will be retrieved on the spot. If the view does not need this information, "get_post_count()" will never be called, saving us all that overhead of additional database access, and storing information we'll never use.

Ultimately, this does depend on our template engine being somewhat aware. Of course, you can accomplish this fairly easily using just PHP as your template engine, which of course leads you back to tag soup land.