Sunday, August 29, 2010

Pagelib Retrospective

I have always been in love with magic: one of the first languages I taught myself once I was old enough to understand what I was doing was Perl, and it fit me like a skin-tight sci-fi spacesuit that we thought we were all going to wear in the future, back in the 1980's.  Steve Yegge's rant about Perl was entertaining, but I can't say as I identified with it much, since references weren't hard for me, nor do I have his problem of forgetting everything when I don't use the language for a while.  Maybe I don't drink enough.

Still, being in love with magic and DWIM doesn't mean that there aren't any hazards.  It's rather like fairy-tale magic, which can result in nasty, surprising, unexpected consequences if used without the utmost care.  This is a story about me exercising a complete and utter lack of care, utmost or otherwise.

Pagelib was a templating system I devised, which used straight PHP in the templates, as well as being embedded in a larger PHP system.  One of pagelib's design goals was to give the template programmer (also myself, and sometimes Brian) nigh ultimate power! to create without limits.  Because as everyone knows, limitations are absolutely horrible.

The principle of operation of the system was simple: the caller set up some variables, and maybe a callback if the template being invoked needed one, and then launched the template.  Of course, this meant that the caller had to know everything the template could possibly want, as well as whether it would ask for another template to be embedded.  If it did, then the callback would need to launch another template with another set of variables.  Theoretically, it could also set up another callback to continue to darker depths, but I'm not sure this capability was actually used.

Meanwhile, I didn't really understand proper template design, so instead of passing in a domain object and letting the template pull data from it, I extracted and computed all the property values that the template would need, and assigned them all to individual variables.  Neither "$user->first $user->last" nor "$user->getFullname()" would sully my code!  "$user_fullname" all the way!  An unfortunate consequence of this decision was that the code naturally wanted to be "$data['user_fullname']" because the variables were given to the page as an associative array in $data.  But I didn't like typing $data[''] all the time, so the templates all called extract($data) at the beginning.

Pagelib was also designed as a strictly one-pass system, since I hadn't yet convinced myself of the need for multiple passes to allow fully self-contained modules. As a result, the uppermost caller needed to set up any CSS or script inclusions that any template would need, totally breaking the self-contained modularity that the lower templates were supposed to provide.  Which is also (partly) why all the CSS for individual pages ended up in one giant CSS file.

Finally, I'm pretty sure that pagelib took some measures to work around the fact that calling include() in PHP runs in the context of the code calling include, which means it will have access to $this or self:: if called from inside a class method.  This is why it only offered nigh ultimate power, rather than the real thing.

Everything about pagelib makes me cringe today.

Ur Doin it Wrong

The variables being passed to a pagelib template were effectively hidden: only the template to be called next was being constructed at any time, so variables to nested templates were completely invisible.  Even from the point of view of a template, only the variables passed by the given execution path were visible.  Of course there was no documentation, because we could always grep the code... right?

The power exposed by the doCallback() system also turned out to be basically useless.  In almost every case, the callback was no more complicated than a simple "include page-specific template" instruction, but since this happened in the Ultimately Powerful Callback, the code was essentially six lines of boilerplate everywhere.  The template could even pass arguments into doCallback, which would be passed along to the actual callback function, but that turned out to be even more useless.  The upper level page knew everything anyway, since it had to set up the callback, so why bother passing a message from it to itself via the template?  Yo dawg....

I'm not entirely convinced that PHP was the best choice of template language, either.  Walking that path either requires short tags to be on, or litters the template with <?php echo ?> every time a variable needs printed.  And even with short tags, the bracket-question combinations are somewhat difficult to type so frequently.

Pagelib's Legacy

Ultimately, Pagelib and its larger system became a victim of the software Peter principle.  Quite scorched, and with newfound humility, I designed a much simpler system (unimaginatively named "Output") for my next and final project with that employer.  Output was still a single-pass system, so the caller of a template still needed to know that it had a slot for including another template.

But this time, the association was done by name, not implicitly in the control flow.  Also, Output's variable system was designed that each template had its own set of local variables, and inherited its parents' as well.  So when the top-level code assigned variables to a template, it was clear where they would be visible, and that they were being made available.

Output's simplicity and clarity was the seed of my acceptance of the Zen of Python.

No comments: