Content Management: Bootstrapping

I’ve been bootstrapping the code for my Personal Publishing System (nicknamed “Spokane”) that I wrote about here and here, and since I had intended this to be an open process that I’d blog about, I’m writing up some of what I’m doing and my thoughts on how to do it.

What I’ve done so far is repurpose my old blogging functions to better map to the database schema, but haven’t yet changed the essential functionality. There’s a function to retrieve and display the last X items (what you see on the home page), a function to retrieve and display a given item, that sort of thing. They’re still very straightforward, nothing fancy. And that works, under the older blog software (which is still running the site as I write this); but one of the features I want the Spokane PPS to embrace is the ability to dynamically handle any type of content, not just HTML. So the next step is to deal with that, dynamically.

In the content table I have a field called mime_type, which will indicate the type of data that particular item of content happens to be: text/plain, text/html, image/jpeg, etc. In the case where I want to store extended metadata on that item, I’d set the MIME type to text/xml or application/xml and store the resultant XML data into the body field. I’m also thinking that in the case of binary files (stored on the filesystem, not in the database itself), I would also use the body field to store XML data on that item: for a JPEG, for instance, the name field would be a pointer to a file on the filesystem where the actual binary file resides, the mime_type field would contain image/jpeg, and the body field might contain:

     <?xml version="1.0" ?>
     <file type="jpeg">
        <width unit="pixels">1024</width>
        <height unit="pixels">768</width>
        <source>SONY Cyber-shot DSC-P50</source>

And so on and so forth. The real question here becomes, how to display the various types of data dynamically—knowing that each type of content may very well be displayed differently. That’s where the beauty of PHP comes in: dynamic file includes and function calls.

Here’s a fragment in PHP of what the individual item display function might look like (it’s not going to be production quality, be warned):

     // Select the item from the database
     $sql = "SELECT * FROM content WHERE content_id = $content_id ";

     $mime_type = $db->f('mime_type');
     // Make this "filename friendly"
     $mime_type = preg_replace('/[^a-zA-Z0-9_-]+/', '_', $mime_type);

     // Don't assume we know how to handle this item; load helper function file
     if ( file_exists('content_' . $mime_type . '.inc') ) {
         @include_once('content_' . $mime_type . '.inc');
         // Each helper file should have a standard "main" function in it
         if ( function_exists('display_main') ) {
             // Sanity check
         } else {
             // Fall back to some default handling behavior
     } else {
         // Fall back to some default handling behavior

See what’s happening there? The code is looking for a helper file whose name is based on the MIME type—automatically. And it’s left to each helper file to define just how a particular type of content will be handled. The big benefit of doing it this way is that I can arbitrarily add new MIME types of content to the system, but I don’t have to hard-code how to handle that MIME type in the display function, nor hack the code to insert new file names to look for; I can write the new helper file whenever I want, no hurry, because the system will fall back to some sort of default behavior without breaking in the meantime.

The downside, of course, is maintaining a library of helper functions, which could grow into a large number of files. However, this is all modular, and I can easily remove or swap out files without breaking the system.

But what if a particular file is broken, and won’t parse? PHP will throw a parse error at the include point, breaking the system, right? No, look more closely. The “@” symbol in the line of code reading @include_once('content_' . $mime_type . '.inc'); is an error suppression mechanism—if there’s a parse error in the include file, then it won’t load, the @ will suppress the error and the script will keep running, and the fallback scenario will be triggered. Hopefully, if it’s all done right, the end user would never know things are going wonky.

Okay, that’s enough on this topic for tonight. Next time I might talk about administering the data on the back end.