Templating with Apache actions

It’s generally less useful than piping requests through a dispatcher file, but you can use Apache actions to pass all files with a certain extension (or MIME type) through a script of your choosing. If you fill a site with .text text files in Markdown syntax, for example, you can do something like this:

AddHandler markdown .text
Action markdown /markup.cgi

Or this:

AddType text/prs.markdown .text
Action text/prs.markdown /markup.cgi

Every .text file will be passed to markup.cgi, which will add headers, footers, and process the text into HTML.

Since that’s of little use to most people — though I use it quite satisfactorily — I’ll move on to something far more common: automagic syntax colouring for script source files.

As the PHP manual notes, some servers are automatically configured to highlight PHP syntax in .phps files, only requiring the line:

AddType application/x-httpd-php-source .phps

This essentially causes PHP’s highlight_file() to be run on the source in question, and there are a few reasons why that isn’t optimal. The first is that it generates invalid HTML, since highlight_file()’s output can also be embedded: just a <code> block with no <html>, <body> etc.; the second is that the output isn’t particularly useful or pretty. No line numbers, for example, and no way to change the (ugly) colours.

Instead, we can do this:

AddHandler highlighter .phps
Action highlighter /highlighter.php

The file path of the requested document is sent in the PATH_TRANSLATED environment variable, so highlighter.php can contain:

<?php
if (! $path = $_SERVER['PATH_TRANSLATED'] )
    die('error: no file'); 

highlight_file($path);
?>

Please note that while I know of no way to spoof PATH_TRANSLATED, it’s better to be safe than sorry. These examples contain no security/sanity checks, but it’s wise to include them in any production code.

This can be further customized, with, say, a header and footer:

<?php
if (! $path = $_SERVER['PATH_TRANSLATED'] )
    die('error: no file'); 

include('header.php');
highlight_file($path);
include('footer.php');
?>

They might contain nothing more than a wrapper to make the regular highlight_file() output valid (X)HTML; but they could contain links, support or license information, a PayPal donation system, statistics about file creation/update dates — anything.

The other advantage is in the ability to use better syntax highlighters, like enscript, Beautifier, or GeSHi, which are customizable, usable with files written in other programming languages, and generally prettier out-of-the-box.

<?php
if (! $path = $_SERVER['PATH_TRANSLATED'] )
    die('error: no file'); 

require('geshi/geshi.php');
$source = file_get_contents($path);
$geshi =& new GeSHi($source, 'php');
$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 5);

include('header.php');
echo $geshi->parse_code();
include('footer.php');
?>

There are problems with going this route, however. GeSHi in particular is incredibly slow at parsing large files, so its output must be cached. Some syntax highlighters split code blocks into ordered lists for display, which prevents them being copied directly unless a plain-text alternative is provided. The parser’s logic may be worse, even if the output is usually better: GeSHi, again, doesn’t play well with HTML fragments embedded in PHP source.

Here’s a more complete example, currently displayed by a somewhat more complicated version of itself.

   
This entry was posted on Friday, February 24th, 2006, in the categories “PHP”, “code”, “syntax highlighting”, “apache” and “htaccess”.

Leave a Reply