In the first article of this two-part series on XSLT, I introduced you to some of the new features in XSLT 2.0 and showed you how to generate code from an abstract data model. To illustrate this process, I started a project to build a robust code generator that produces both the SQL for a database server and the PHP that will ultimately provide database access to a Web server. I employed XSL to build the SQL using a multilevel transformation. The first transformation turned the abstract model into a database physical schema model. I then used this schema model to build the SQL code.
The next step in this process is to build the model for the code, then generate PHP from that model. By the end of this project, you'll have an abstract model of your system, SQL code to build the database, and PHP wrappers for each table. However, before digging into PHP generation, I'd like to step back and look a bit more at XSL and the new XSLT 2.0 features that affect XSL template design and use.
Enhancements for XSL templates
Successful code generation requires both an understanding of the target language (in this case, PHP) and the code generation language (in this case, XSLT). In Part 1, I concentrated on the basics of code generation. This article provides more detail on the XSL side of the code-generation equation.
At its core, XSLT is a templating language. It takes XML as input, then uses a set of templates to transform the XML into XML, HTML, or text. In this code-generation example, I use the templates to generate both XML and text. The generator, which is a collection of related templates, uses two modes -- XML mode and Text mode -- to translate the original input XML into code. XML mode is used for the temporary models that sit between the abstract model and the code templates. Text mode is used in the code templates when you generate the PHP and the SQL.
For this code-generation system, I use the Saxon XSLT engine and a set of custom templates. For convenience, these templates reside in the same directory as the input. The output of the templates goes into directories for the PHP and SQL code. No special extensions to Saxon are required, although if you find the XSL tags or XPath functions provided in the basic installation inadequate, you can use Java™ code to extend the template engine.
The starting point of an XSL template is a template that matches the root node of the input XML. When the XSL engine starts up, it applies the input XML to the library of templates. If a particular template matches the root node (/), it is executed first. Here's the main template tag for the generator:
<xsl:template match="/"> |
This matching system is important, because XSL works by looking through the list of available templates that apply to the node it's currently processing, then applying the template with the most specific match. Take a look at the code in Listing 1, which is an example from the previous article.
Listing 1. Template with a mode type
<xsl:template match="create" mode="sql">
DROP TABLE IF EXISTS <xsl:value-of select="@name" />;
CREATE <xsl:value-of select="@name" /> (
<xsl:apply-templates mode="sql" select="field" />
PRIMARY KEY ( <xsl:value-of select="@primary-key" /> )
);
</xsl:template>
|
This code tells the XSL that this template applies to create tags. So, when the XSL comes across a create tag, it executes this template. In addition, note that this template specifies a mode. You specify the mode in the xsl:apply-templates tag, as Listing 2 shows.
Listing 2. Tag for applying templates
<xsl:apply-templates mode="sql" select="$sql-model/sql" /> |
The xsl:apply-templates tag tells XSL to apply the available templates to the section of the XML model that the select tag specifies. Additionally, you specify that the mode is sql, so the XSL finds only those templates that work in sql mode. This is a handy namespace mechanism for templates.
But what happens with the output of these templates? That's where the new xsl:result-document tag comes in (see Listing 3).
Listing 3. Code to invoke the SQL templates
<xsl:result-document href="db/gen-tables.sql" format="sql" >
<xsl:apply-templates mode="sql" select="$sql-model/sql" />
</xsl:result-document>
|
Typically, output from the template goes to whatever output file you specify when you run the XSL
translator -- usually a file or a standard out (that is, the console or the default output file). Using
the xsl:result-document tag in the XSL template, you can specify a new file to hold the output. In this case, I'm sending the output to the file
db/gen-tables.sql. This new XSL feature is critical for code generation
because you often need to create multiple output files from a single piece of XML.
Another important tag that has changed in XSLT 2.0 is xsl:variable. Like the xsl:result-document tag, xsl:variable diverts the output of the tags nested inside it. Instead of going to a file, these nested tags go to a temporary variable in memory. In this case, I take the output of the template that I call and put it in a variable called sql-model (see Listing 4). This variable is actually an XML tree to which I can then apply other templates.
Listing 4. Code to create a temporary tree variable
<xsl:variable name="sql-model">
<xsl:call-template name="gen-sql-model">
<xsl:with-param name="model" select="."/>
</xsl:call-template>
</xsl:variable>
|
This ability to have multiple XML trees in memory simultaneously opens new vistas for XSL. With it, you can have much larger template systems while you reduce the complexity of each template.
Another important enhancement to XSLT is the inclusion of the xsl:function tag, which creates new XPath functions (see Listing 5).
Listing 5. Code to create an XPath function
<xsl:function name="gen:model-type-to-sql"> |
I used this tag to create a new function that maps types specified in the abstract model (the original input file) into SQL native types for the output file (Listing 6). I could have done this with a template, but with this new syntax I can declare a function that can be used in XPath, which is much more convenient.
Listing 6. Code to invoke an XPath function
<field name="{lower-case(@name)}" type="{gen:model-type-to-sql(@type)}"/>
|
As you can see, the code in Listing 6 is much simpler than the xsl:call-template functionality that you had to use in XSLT 1.0. See Resources for more information about XSLT and the new features in XSLT 2.0.
Now I'll show you how to apply the new XSLT features that I just illustrated and build PHP from a model. I started with an abstract model, which is just the XML input file. In the first article, I used templates to create an SQL model from which I built the SQL code. Here, I use a similar technique to build PHP from a PHP model.
Generate PHP from the abstract data model
To generate the PHP, first build a model of the code you want to generate. Then, apply code templates to that new model. For the Author table I created last time, this new database access model looks like the code in Listing 7.
Listing 7. Language model corresponding to the abstract input
<?xml version="1.0" encoding="UTF-8"?>
<modules xmlns:gen="http://www.codegeneration.net/">
<module name="Author">
<class name="Author">
<constructor name="Author"/>
<method name="selectAll">
<parameters/>
<implementation>
<selectAll table="author">
<field name="author_id"/>
<field name="first"/>
<field name="last"/>
</selectAll>
</implementation>
</method>
<method name="selectOne">
<parameters>
<variable name="author_id"/>
</parameters>
<implementation>
<selectOne table="author" by="author_id">
<field name="author_id"/>
<field name="first"/>
<field name="last"/>
</selectOne>
</implementation>
</method>
<method type="insert" name="insert">
<parameters>
<variable name="first"/>
<variable name="last"/>
</parameters>
<implementation>
<insert table="author">
<field name="first"/>
<field name="last"/>
</insert>
</implementation>
</method>
</class>
</module>
|
The structure of the XML matches exactly with the structure of the code that you expect to see on the other end. However, note that nothing about this code is PHP-specific. You could use this model to generate any latently typed language. For example, you could generate Java code just by adding some type information to the model. To see why the construction of the XML is so important in this scheme, simply look at the resulting PHP in Listing 8.
Listing 8. The output Author table
<script language="php">
class Author extends DatabaseTable {
function Author ( ) { }
function selectAll ( )
{
$dbh = getDbh();
return $dbh->doQuery(
"SELECT author_id, first, last FROM author"
);
}
function selectOne ( $author_id )
{
$dbh = getDbh();
return $dbh->doQuery(
"SELECT author_id, first, last FROM author WHERE author = ?",
$author_id );
}
function insert ( $first, $last )
{
$dbh = getDbh();
$dbh->doCommand( "INSERT INTO author ( first, last ) VALUES ( ?, ? )",
$first, $last );
}
}
</script>
|
If you look back at the XML in Listing 7, you can see how each tag in the XML represents a structure in the PHP. For example, you can see the method tag of type insert in the model and the resulting input function in the output class.
I'll show you a few of these code templates to illustrate how they work.
The first template example is the Module template, which you use to build a new .php file. Listing 9 shows this template.
Listing 9. The Module template
<!-- PHP: Module tag handler -->
<xsl:template match="module" mode="php">
<xsl:result-document href="php/{@name}.php">
<script language="php">
<xsl:apply-templates select="*" mode="php" />
</script>
</xsl:result-document>
</xsl:template>
|
Notice how the template uses the xsl:result-document tag to build a new file for each module. It then uses the xsl:apply-templates tag to fill in the file with the code.
To build the implementation portion of the insert method, I use the template shown in Listing 10.
Listing 10. The SQL Insert template
<!-- PHP: SQL Insert tag handler -->
<xsl:template match="insert" mode="php">
<xsl:call-template name="php-db-function">
<xsl:with-param name="content">
<xsl:call-template name="php-sql-command">
<xsl:with-param name="command" select="gen:build-insert(.)" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
|
The other important call is to the build-insert function. The code in Listing 11 shows this function.
Listing 11. The insert command builder
<!-- Builds an SQL insert command -->
<xsl:function name="gen:build-insert">
<xsl:param name="query" />
<xsl:variable name="out">
<sql>
<query>
INSERT INTO <xsl:value-of select="$query/@table" />
(
<xsl:for-each select="$query/field"><xsl:if test="position() > 1">,
</xsl:if><xsl:value-of select="@name" /></xsl:for-each>
) VALUES (
<xsl:for-each select="$query/field"><xsl:if test="position() > 1">,
</xsl:if>?</xsl:for-each>
)
</query>
<inputs>
<xsl:for-each select="$query/field">
<input name="{@name}" />
</xsl:for-each>
</inputs>
</sql>
</xsl:variable>
<xsl:sequence select="$out" />
</xsl:function>
|
This function returns a full data structure with both the query and the required inputs. This function was possible in XSLT 1.0, but the custom functions in XSLT 2.0 make the process much easier.
The php-sql-command template uses the SQL data structure to build the PHP -- see Listing 12.
Listing 12. Template for SQL commands
<!-- SQL command template -->
<xsl:template name="php-sql-command">
<xsl:param name="command" />$dbh->doCommand(
"<xsl:value-of select="normalize-space( $command/sql/query )" />"
<xsl:for-each select="$command/sql/inputs/input">,
$<xsl:value-of select="@name" />
</xsl:for-each> );
</xsl:template>
|
The $command/sql/query and $command/sql/inputs/input tags are where the SQL query and inputs are turned into code.
As small as it seems, this is all there is to building potentially hundreds of database access classes for PHP. Of course, what I provide in these two articles is just an example: To use the generator, you have to implement the DatabaseTable base class, then add methods for deleting and updating. I left out those methods to keep the code in the article shorter.
You can separate the information in these articles into two main categories: technical and theoretical.
On the technical side, you've learned a couple of things. First, XSLT does a great job with code generation, and XSLT 2.0 makes that process even easier. Second, using multiple models and levels of translation in a generator makes your application easier to understand, develop, and maintain.
On the theoretical side, you can see that code-generation techniques are capable of quickly building a large volume of repetitive code accurately. This has several advantages, not the least of which is that you can concentrate on writing code rather than attending to the repetitive work. Also, you can quickly change your code base to accommodate changing requirements. And you can change technologies reasonably quickly as needed because the important design information is in an abstract model, not embedded in the code.
For several years, I have written, lectured, and evangelized about code-generation techniques. In that time, I talked with many engineers who had positive experiences generating code for their projects. One went so far as to tell me, "If you have a Ford vehicle manufactured in the U.S., the parts for that vehicle were delivered to Ford based on a product built through code generation." With a thoughtful, pragmatic approach to code generation, you can dramatically cut your schedule time, increase the quality of your code, and return to writing the type of interesting code that you got into this game to build.
| Description | Name | Size | Download method |
|---|---|---|---|
| XSLT templates and resulting output files | x-xslphp2-PHPgeneration.zip | 8 KB | HTTP |
Information about download methods
- Read the first article in this two-part series, "Code generation in XSLT 2.0, Part 1: Generate SQL with XSLT 2.0" (developerWorks, February 2005).
- Download the source code for generating PHP for Web server access.
- Visit the official PHP site, where you'll find everything you need to know about PHP.
- Download Saxon, an excellent XSLT processor that supports version 2 of the standard and was used in this article.
- Want to know more about XSLT 2.0 and XPath 2.0? Read XSLT 2.0 Programmer's Reference by Michael Kay, the bible on this new standard. XPath 2.0 Programmer's Reference is Michael Kay's companion book on XPath 2.0.
- Explore Generative Programming, the seminal work on this topic. This field includes code generation as well as other automatic programming techniques.
- Check out Code Generation Network, your source for code generation information, where you'll find articles, interviews, book reviews, and e-mail lists for discussing code generation.
- Read Code Generation in Action by Jack D. Herrington, which covers generating code for a wide variety of targets not limited to database access.
- Find hundreds more XML resources on the
developerWorks XML zone.
- Learn how you can become an IBM Certified Developer in XML and related technologies.
An engineer with with more than 20 years of experience, Jack Herrington is currently Editor-in-Chief of the Code Generation Network. He is the author of Code Generation in Action. You can contact him at jack_d_herrington@codegeneration.net.





