Injecting XML Content Into Page Templates With patXMLRenderer (part 2)

Cache content, log activities and report usage with patXMLRenderer.

Moving On

In the first part of this article, I introduced you to patTemplate and patXMLRenderer, and showed you how you could use them to separate the content, layout and business logic of a site. This technique is particularly valuable when developing content-heavy sites that rely on XML data sources - in the previous tutorial, I showed you a few examples of how it could be used, together with a real-world example.

In this second part, I'll discuss some of the things hidden under the patXMLRenderer hood, including how to incorporate XML attribute values in your templates, how to retrieve and calculate attribute values dynamically, how to cache pages for faster response times, how to create logs of user activity, referrers and totals, how to generate statistical usage reports and much, much more. So come on in - things are only gonna get more interesting!

Meet Moo

Thus far, I've only dealt with XML files containing elements. But what if your XML elements include attributes...and those attribute values need to appear in your rendered output?

Fear not, patXMLRenderer has you covered there too. The package automatically maps element attributes into template variables, automatically replacing placeholders with corresponding attribute values in the call to displayRenderedContent(). Consider the following example, which illustrates by placing a boatload of information about Timothy into attributes:

<?xml version="1.0"?>
<person age="18" ssn="A736AS957" location="NY">Timothy Moo</person>

And here's the corresponding template - notice how the attribute names from the XML data above map into the template variables:

<!-- main page -->
<patTemplate:tmpl name="person">
<html>
<head></head>
<body>
My name is {CONTENT}. I am {AGE} years old, located in {LOCATION} and my Social Security number is {SSN}. Drop by and meet me when you have a minute. Bye now!
</body>
</html>
</patTemplate:tmpl>

When you combine the two, the output looks like this:

My name is Timothy Moo. I am 18 years old, located in NY and my Social Security number is A736AS957. Drop by and meet me when you have a minute. Bye now!

Cool, huh?

Serving It Up

Why stop there? One of the cooler new features in patXMLRenderer is the ability to dynamically calculate attribute values at run-time, and insert them into your template. To illustrate this, consider the following XML files, which uses variables instead of static values in the attributes:

<?xml version="1.0"?>
<server name="$name" root="$root" type="$type" />

Here's the template corresponding to this XML file,

<!-- main page -->
<patTemplate:tmpl name="server">
<html>
<head></head>
<body>
This server is running {TYPE}. Server document root is {ROOT} and server name is {NAME}.
</body>
</html>
</patTemplate:tmpl>

and here's the PHP script which dynamically obtains the server information and assigns it to the variables in the XML file:

<?php

// include config file
require_once("config/patXMLRenderer.php");

// include patTemplate class
require_once("include/patTemplate.php");

// include patXMLRenderer clas
require_once("include/patXMLRenderer.php");

// initialize XML rendering class
$randy = new patXMLRenderer;

// set base directory for XML content
$randy->setXMLDir("xml/");

// set XML file to use
$randy->setXMLFile("server.xml");

// initialize template engine
$template = new patTemplate();

// set base directory for templates
$template->setBaseDir("templates/");

// connect template object to renderer object
$randy->setTemplate($template);

// set template file to use
$randy->addTemplateFile("server.tmpl");

// set list of skins
$randy->setSkins($skins);

// set up renderer
$randy->initRenderer();

// set dynamic attribute values
$randy->setParam("name", $_SERVER['SERVER_NAME']);
$randy->setParam("root", $_SERVER['DOCUMENT_ROOT']);
$randy->setParam("type", $_SERVER['SERVER_SOFTWARE']);

// parse XML file
// interpolate variables
// display output
$randy->displayRenderedContent();
?>

And here's what the output looks like:

This server is running Apache/1.3.11 (Win32) PHP/4.2.0. Server document root is c:/program files/internet tools/apache/htdocs and server name is medusa.gorgon.com.

Here, the call to setParam() assigns a value - which may be calculated dynamically by PHP - to the variables in the XML file prior to having that XML content transformed into HTML. This capability makes it possible to create portable XML files, wherein some data fragments are generated dynamically, every time the script runs. If you were to move the files above to a new server, for example, the output would automatically update itself with the new server details.

Cache Cow

Two of patXMLRenderer's most useful features are the built-in caching and logging capabilities. Let's look at caching first.

Normally, whenever patXMLRenderer encounters a request for a page, it has to parse the XML content, read the corresponding template, perform variable interpolation and then render the page. Needless to say, this is time- and resource-consuming...and most of the time, completely unnecessary, as it's unlikely that your pages will change often enough for the content to be re-generated so frequently at run-time (if I'm wrong and your site does actually change on a second-by-second basis, you can ignore this entire section and instead start saving up for a more powerful CPU). That's why patXMLRenderer includes a primitive caching mechanism to speed things up.

To activate the cache, you need to add two method calls to your PHP script, as below:

<?php
// turn on caching
$randy->setOption("cache", "auto");

// set cache directory
$randy->setCacheDir("cache/");
?>

To turn off caching, simply set the "cache" option to "off", as below:

<?php
// turn off caching
$randy->setOption("cache", "off");
?>

You can also control the amount of time for which the page is cached, by setting the "cachetime" option.

<?php
// set caching interval
$randy->setOption("cachetime", "300");
?>

Now, try accessing a page which has caching activated - you'll see that patXMLRenderer automatically creates an entry in the named cache directory for this page, and handles all subsequent requests for the page from this cached version. This reduces the server overhead and frees up processing power for more important tasks.

Floating Logs

patXMLRenderer also comes with a logging engine, which can be used to track requests on a per-page basis. The logging engine in patXMLRenderer writes a daily log which includes information on the file requested, the date and time of request, and the client's IP address, and also keeps track of the total requests made for each page - this data can then be analyzed with custom statistical analysis tools or with patXMLRenderer's own administrative utilities (more on this later).

To activate logging in patXMLRenderer, simply add the following lines to your PHP code:

<?php
// include logging class for text files
require_once("include/patXMLRendererTxtLogger.php");

// turn on logging
$randy->setOption("log", "on");
?>

Once logging is activated, you also need to tell patXMLRenderer what format (or "container") to use when logging. As of this writing, only one container - ASCII text - is supported, although the developers plan to also allow logging direct to a database or other data storage systems in future versions. To enable the ASCII text logger, also add the following lines to your PHP script:

<?php
// instantiate a text logger object
$logger = new patXMLRendererTxtLogger;

// set directory for log files
$logger->setLogDir("logs");

// connect logger to renderer
$randy->setLogger($logger);
?>

Now, try accessing the page a few times (hopefully you already turned caching on as described earlier) and the look in the named log file directory - you should see two new files, once called "totals.log" and the other called "accesslog_xxyy.log". Look inside "totals.log" and you'll see that patXMLRenderer is using this file to track the total number of requests per file - almost like a counter.

about.xml=1737
company.xml=752
contact.xml=1968
products.xml=123612
index.xml=447437

And if you look inside the access log, you'll see a daily log of all requests made - here's a sample:

2004-03-25 10:04:37;;192.168.0.3;circle.xml
2004-03-25 10:04:43;;192.168.0.6;about.xml
2004-03-25 10:05:06;;192.168.0.3;index.xml
2004-03-25 10:05:06;;192.168.0.1;circle.xml
2004-03-25 10:05:07;;192.168.0.1;server.xml
2004-03-25 10:05:09;;192.168.0.1;index.xml
2004-03-25 10:05:16;;192.168.0.3;blue.xml
2004-03-25 10:08:36;;192.168.0.10;circle.xml
2004-03-25 10:09:38;;192.168.0.10;circle.xml
2004-03-25 10:10:06;;192.168.0.10;server.xml

A new access log is created every day. You can even track unique visitors by having patXMLRenderer record a unique session ID for each visitor - simply add this line to your PHP script:

<?php
// record session IDs
$randy->useSessions();
?>

And now every entry in the log file will be marked with a session ID - this session ID can be used to track requests by visitor and monitor where users are going on your site.

Minority Report

Now, all this user tracking and logging is all very well - but it's of no use if you don't have the ability to generate useful reports from the logs. Luckily, patXMLRenderer comes with an administration module which includes a report generating engine - you can use this engine to build detailed reports of what the visitors to your site are doing.

In order to access the patXMLRenderer administration module, point your browser to the "/admin/patXMLRenderer.php" script of your installation. You should see something like this:

Selecting the "Statistics" menu item launches the report builder:

The first item in the list, "General Statistics", lists the data from "totals.log" in a neat graphical fashion - the total requests intercepted for each file.

The second item, "Visits", presents the usage data on a daily basis, including total number of unique visitors, total time online, total pages requested, and files viewed. This report is very useful to see how popular your pages are, which ones get the most traffic and the entry and exit points for each visitor.

The third item, "Referers", logs where each request comes from, and presents this data in a neat report so that you know the source of your traffic.

Extending Yourself

The administration module included with patXMLRenderer can do a lot more than just generate reports for you (although that is admittedly one of the things you'll use it most for). The module also includes three other main sections, described below:

Extensions: One of the most interesting things about patXMLRenderer is its support for custom extensions, which can be incorporated into your XML documents to add new capabilities to the rendering engine. Each such extension has its own namespace and methods, and when the rendering engine sees this namespace, it knows that it needs to do execute a custom method before rendering the page.

A number of different extensions are available for patXMLRenderer, including extensions for database connectivity, time and date manipulation, string processing, file handling, and others. The "Extensions" section of the administration module lets you activate or deactivate these extensions with a single click - as shown below:

Offline Generator: If you need to package your site's content into an easily-distributable format (read: a format that does not require a Web server, a PHP installation, an XML parser and all the associated tools), you can use the "Offline Generator" in the administration module to convert your XML files and templates into static HTML files, complete with links and images. patXMLRenderer will take care of spidering your content, allowing you to select files for inclusion in the package, and building the HTML pages.

patExtras: The patExtras module uses XML-RPC to communicate with the parent site and check if any updates to the patXMLRenderer package have been released recently. You can either force an update on demand, or have the program automatically check for updates at pre-defined intervals. Finally, you can also use this module to report a bug to the patXMLRenderer developers and (at some future point) access the entire bug database over XML-RPC.

End Of The Line

And that's about all I have time for. In this concluding segment of the patXMLRenderer tutorial, I showed you a number of advanced things you can do with patXMLRenderer - using attributes, caching, logging, and generating reports. All these features can come in handy when you need to do more with your XML-based site, and the patXMLRenderer administration module in particular can significantly ease common tasks.

That isn't all, though - patXMLRenderer comes with a wealth of features, and is constantly being improved by its author. For more information on what it can do, and how you can use it in your own PHP projects, take a look at the following links:

The patXMLRenderer home page, at http://www.php-tools.de/

Template-Based Web Development With patTemplate (part 1), at http://www.melonfire.com/community/columns/trog/article.php?id=130

As for me, I'm outta here. See you soon!

This article was first published on 24 Mar 2004.