<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: coldfusion</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/coldfusion.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-07-01T19:57:46+00:00</updated><author><name>Simon Willison</name></author><entry><title>A custom template system from the mid-2000s era</title><link href="https://simonwillison.net/2025/Jul/1/mid-2000s/#atom-tag" rel="alternate"/><published>2025-07-01T19:57:46+00:00</published><updated>2025-07-01T19:57:46+00:00</updated><id>https://simonwillison.net/2025/Jul/1/mid-2000s/#atom-tag</id><summary type="html">
    &lt;p&gt;Using LLMs for &lt;strong&gt;code archaeology&lt;/strong&gt; is pretty fun.&lt;/p&gt;
&lt;p&gt;I stumbled across &lt;a href="https://simonwillison.net/2003/Jul/17/phpAndColdFusion/"&gt;this blog entry from 2003&lt;/a&gt; today, in which I had gotten briefly excited about ColdFusion and implemented an experimental PHP template engine that used XML tags to achieve a similar effect:&lt;/p&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;h1&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;h1&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span class="pl-ent"&gt;sql&lt;/span&gt; &lt;span class="pl-e"&gt;id&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
select title
from entries 
order by added desc
limit 0, %limit%
&amp;lt;/&lt;span class="pl-ent"&gt;sql&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span class="pl-ent"&gt;ul&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;output&lt;/span&gt; &lt;span class="pl-e"&gt;sql&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;li&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;li&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span class="pl-ent"&gt;output&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class="pl-ent"&gt;ul&lt;/span&gt;&amp;gt;&lt;/pre&gt;

&lt;p&gt;I'd completely forgotten about this, and in scanning through the PHP it looked like it had extra features that I hadn't described in the post.&lt;/p&gt;
&lt;p&gt;So... I fed my 22 year old &lt;code&gt;TemplateParser.class.php&lt;/code&gt; file into Claude and prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Write detailed markdown documentation for this template language&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here's &lt;a href="https://static.simonwillison.net/static/2003/template-docs.html"&gt;the resulting documentation&lt;/a&gt;. It's pretty good, but the highlight was the &lt;a href="https://claude.ai/share/1627f1f3-4b07-4eb3-af24-5ac2da96b712"&gt;Claude transcript&lt;/a&gt; which concluded:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This appears to be a custom template system from the mid-2000s era, designed to separate presentation logic from PHP code while maintaining database connectivity for dynamic content generation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Mid-2000s era indeed!&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/coldfusion"&gt;coldfusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;&lt;/p&gt;



</summary><category term="coldfusion"/><category term="php"/><category term="projects"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/></entry><entry><title>Weeknotes: datasette-template-sql</title><link href="https://simonwillison.net/2019/Nov/18/datasette-template-sql/#atom-tag" rel="alternate"/><published>2019-11-18T22:29:29+00:00</published><updated>2019-11-18T22:29:29+00:00</updated><id>https://simonwillison.net/2019/Nov/18/datasette-template-sql/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;a href="https://simonwillison.net/2019/Nov/11/weeknotes-8/#Datasette_4"&gt;Last week&lt;/a&gt; I talked about wanting to take ona a larger Datasette project, and listed some candidates. I ended up pushing a big project that I hadn&amp;#39;t listed there: &lt;a href="https://github.com/simonw/datasette/issues/622"&gt;the upgrade of Datasette&lt;/a&gt; to Python 3.8, which meant dropping support for Python 3.5 (thanks to &lt;a href="https://github.com/simonw/datasette/pull/595"&gt;incompatible dependencies&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Since Glitch &lt;a href="https://simonwillison.net/2019/Nov/11/weeknotes-8/#New_datasettecsvs_using_Python_37_on_Glitch_17"&gt;now runs Python 3.7.5&lt;/a&gt; my biggest reason for supporting 3.5 was gone, so I decided to make the upgrade.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://datasette.readthedocs.io/en/stable/changelog.html#v0-31"&gt;Datasette 0.31&lt;/a&gt; was the first version to drop support. &lt;a href="https://datasette.readthedocs.io/en/stable/changelog.html#v0-32"&gt;Datasette 0.32&lt;/a&gt; is the first to take advantage of it: I &lt;a href="https://github.com/simonw/datasette/issues/628"&gt;switched Datasette&amp;#39;s template rendering&lt;/a&gt; over to use Jinja&amp;#39;s &lt;a href="https://jinja.palletsprojects.com/en/2.10.x/api/#async-support"&gt;async template support&lt;/a&gt;, which requires Python 3.6+.&lt;/p&gt;
&lt;p&gt;This has exciting implications for the &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html#extra-template-vars-template-database-table-view-name-request-datasette"&gt;extra_template_vars plugin hook&lt;/a&gt;, which allows plugins to add extra variables (and functions) to the template scope.&lt;/p&gt;
&lt;p&gt;Plugin authors can now add custom template functions that are defined with &lt;code&gt;async def ...&lt;/code&gt; and make &lt;code&gt;await&lt;/code&gt; calls within the body of the function. When the template is rendered, Jinja will automatically await those function calls.&lt;/p&gt;
&lt;p&gt;I released a new plugin that takes advantage of this capability: &lt;a href="https://github.com/simonw/datasette-template-sql"&gt;datasette-template-sql&lt;/a&gt;. It lets you embed additional SQL queries directly in a custom Datasette template. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="xml"&gt;&lt;/span&gt;&lt;span class="hljs-template-tag"&gt;{% &lt;span class="hljs-name"&gt;&lt;span class="hljs-name"&gt;for&lt;/span&gt;&lt;/span&gt; article &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; sql(
    "select headline, date, summary from articles order &lt;span class="hljs-keyword"&gt;by&lt;/span&gt; date desc limit 5",
    "news"
) %}&lt;/span&gt;&lt;span class="xml"&gt;
    &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;h3&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="hljs-template-variable"&gt;{{ article.headline }}&lt;/span&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;h2&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt; &lt;span class="hljs-attr"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;"date"&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="hljs-template-variable"&gt;{{ article.date }}&lt;/span&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="hljs-template-variable"&gt;{{ article.summary }}&lt;/span&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class="hljs-template-tag"&gt;{% &lt;span class="hljs-name"&gt;&lt;span class="hljs-name"&gt;endfor&lt;/span&gt;&lt;/span&gt; %}&lt;/span&gt;&lt;span class="xml"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This new &lt;code&gt;sql()&lt;/code&gt; function takes a SQL query and the optional name of the database to run the query against (in case you have more than one database file attached to your Datasette instance).&lt;/p&gt;
&lt;p&gt;I&amp;#39;m really excited about this capability. I quipped about it on Twitter:&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;If you&amp;#39;re thinking &amp;quot;aren&amp;#39;t you just reinventing PHP at this point?&amp;quot; I&amp;#39;m actually more going for ColdFusion&lt;/p&gt;- Simon Willison (@simonw) &lt;a href="https://twitter.com/simonw/status/1195365376059559936?ref_src=twsrc%5Etfw"&gt;November 15, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;This is the great thing about having a plugin system: even if I&amp;#39;m not convinced this is the right technical direction for the core Datasette project, I can still expose this kind of feature in a plugin that people can opt into if they want to have this ability.&lt;/p&gt;
&lt;p&gt;The official Datasette website is going to make extensive use of this plugin. I have an early prototype of that up and running now, which inspired me to release &lt;a href="https://github.com/simonw/datasette-render-markdown/releases/tag/0.2"&gt;datasette-render-markdown 0.2&lt;/a&gt; with a custom template function for rendering markdown directly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ render_markdown(&lt;span class="hljs-name"&gt;value&lt;/span&gt;) }}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Aside from the work on Python 3, my Datasette time this week has mostly involved ongoing refactors of both the query execution code and the core &lt;code&gt;TableView.data()&lt;/code&gt; method. Hopefully these will unblock a flurry of interesting new functionality in the not too distant future.&lt;/p&gt;
&lt;h3&gt;Niche museums&lt;/h3&gt;
&lt;p&gt;This week's &lt;a href="https://www.niche-museums.com/museums?sql=select+id%2C+json_object%28%22img_src%22%2C+photo_url+%7C%7C+%22%3Fw%3D800%26h%3D400%26fit%3Dcrop%22%2C+%22width%22%2C+400%29+as+img%2C%0D%0Aname%2C+url%2C+description%2C+address%2C+latitude%2C+longitude+from+museums%0D%0Awhere+id+in+%2833%2C34%2C35%2C36%2C37%2C38%2C39%29+order+by+id"&gt;new museums&lt;/a&gt; on &lt;a href="https://www.niche-museums.com/"&gt;www.niche-museums.com&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;California State Capitol Dioramas in Sacramento&lt;/li&gt;
&lt;li&gt;Zeppelin Museum in Friedrsichshafen&lt;/li&gt;
&lt;li&gt;Dai Loy Museum in Locke (Sacramento Delta)&lt;/li&gt;
&lt;li&gt;Wallace Collection in London&lt;/li&gt;
&lt;li&gt;Cookin' in San Francisco&lt;/li&gt;
&lt;li&gt;Ramen Museum in Yokohama&lt;/li&gt;
&lt;li&gt;Cable Car Museum in San Francisco&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/coldfusion"&gt;coldfusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="coldfusion"/><category term="projects"/><category term="sql"/><category term="datasette"/><category term="weeknotes"/></entry><entry><title>What was programming use Myspace?</title><link href="https://simonwillison.net/2010/Oct/13/what-was-programming-use/#atom-tag" rel="alternate"/><published>2010-10-13T12:46:00+00:00</published><updated>2010-10-13T12:46:00+00:00</updated><id>https://simonwillison.net/2010/Oct/13/what-was-programming-use/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-was-programming-use-Myspace/answer/Simon-Willison"&gt;What was programming use Myspace?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Originally ColdFusion, but then a small army of consultants from Microsoft helped rewrite it in .NET.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/coldfusion"&gt;coldfusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/myspace"&gt;myspace&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="coldfusion"/><category term="myspace"/><category term="quora"/></entry><entry><title>Quoting Aral Balkan</title><link href="https://simonwillison.net/2009/Jan/8/commodity/#atom-tag" rel="alternate"/><published>2009-01-08T18:10:31+00:00</published><updated>2009-01-08T18:10:31+00:00</updated><id>https://simonwillison.net/2009/Jan/8/commodity/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://aralbalkan.com/1864"&gt;&lt;p&gt;The simple truth is that in the age of Web 2.0/3.0, in the era of cloud and utility computing, the application server is a commodity. A commercial, proprietary app server simply cannot survive in this environment anywhere outside the lethargic, soft-padded walls of the enterprise.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://aralbalkan.com/1864"&gt;Aral Balkan&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/appservers"&gt;appservers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/aral-balkan"&gt;aral-balkan&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coldfusion"&gt;coldfusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/commoditisation"&gt;commoditisation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/enterprise"&gt;enterprise&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;&lt;/p&gt;



</summary><category term="appservers"/><category term="aral-balkan"/><category term="coldfusion"/><category term="commoditisation"/><category term="enterprise"/><category term="open-source"/></entry><entry><title>New PHP experiment, inspired by ColdFusion</title><link href="https://simonwillison.net/2003/Jul/17/phpAndColdFusion/#atom-tag" rel="alternate"/><published>2003-07-17T09:58:53+00:00</published><updated>2003-07-17T09:58:53+00:00</updated><id>https://simonwillison.net/2003/Jul/17/phpAndColdFusion/#atom-tag</id><summary type="html">
    &lt;p&gt;I've been reading up on ColdFusion MX recently, and I have to admit it looks like a really nice piece of technology. I'd previously written ColdFusion off as being too simplistic and primitive, but having seen how much its capable of I'm reconsidering my position.&lt;/p&gt;

&lt;p&gt;If you've never seen ColdFusion before, it's a server side scripting language/application server from Macromedia (who obtained it when they bought Allaire). MX is the latest version of the language, which sees it completely rewritten in Java to allow it to integrate with Java application servers and existing Java servlet applications. This is shrewd marketing on the part of Macromedia, and I've already seen them advertising it as something to make your existing Java web deployments easier to customise.&lt;/p&gt;

&lt;p&gt;The thing that put me off ColdFusion originally is that it is a tag-based scripting language, specifically designed to make it easy for &lt;acronym title="HyperText Markup Language"&gt;HTML&lt;/acronym&gt; developers with little or no previous programming experience to pick up. Here's an example of a chunk of &lt;acronym title="ColdFusion"&gt;CF&lt;/acronym&gt; syntax:&lt;/p&gt;

&lt;pre&gt;&lt;code class="cfml"&gt;
&amp;lt;cfinclude template="some-other-file.cfm"&amp;gt;

&amp;lt;cfif isdefined("form.name")&amp;gt;
 &amp;lt;cfoutput&amp;gt;
Hi there, #form.name#
 &amp;lt;/cfoutput&amp;gt;
&amp;lt;/cfif&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The thing that most surprised me about ColdFusion is that although the above syntax looks pretty verbose, it is actually designed in a way that means you can do an awful lot with very little code. The above in &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; would look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="php"&gt;
&amp;lt;?php
include('some-other-file.php');
if (isset($form['name'])) {
    print "Hi there, $form['name']";
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's less typing, but only by a bit. Where &lt;acronym title="ColdFusion"&gt;CF&lt;/acronym&gt; gets really clever though is with its handling of &lt;acronym title="Structured Query Language"&gt;SQL&lt;/acronym&gt;. Here's the code to run a &lt;acronym title="Structured Query Language"&gt;SQL&lt;/acronym&gt; query and loop through outputting the results to a simple table:&lt;/p&gt;

&lt;pre&gt;&lt;code class="cfml"&gt;
&amp;lt;cfquery name="users" datasource="myDSN"&amp;gt;
select name, email from users order by name
&amp;lt;/cfquery&amp;gt;
&amp;lt;table&amp;gt;
&amp;lt;cfoutput query="users"&amp;gt;
&amp;lt;tr&amp;gt;
 &amp;lt;td&amp;gt;#name#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#email#&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/cfoutput&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's the same in &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt;, assuming $db contains a connection to a MySQL database.&lt;/p&gt;

&lt;pre&gt;&lt;code class="php"&gt;
&amp;lt;?php
$result = mysql_query("select name, email from users order by name", $db);
echo '&amp;lt;table&amp;gt;';
while ($row = mysql_fetch_assoc($result)) {
    echo "&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;$row['name']&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{$row['email']}&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;";
}
echo '&amp;lt;/table&amp;gt;';
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The ColdFusion example achieves the same effect but with less complicated code.&lt;/p&gt;

&lt;p&gt;ColdFusion is not without its problems: it comes with a price tag, and it encourages mixing application logic with presentation code (although as with &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; this can be avoided through discipline and careful application design). Never the less, I found it interesting enough that last night I spent a few hours putting together a &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; implementation of a couple of ColdFusion concepts. As with many of the experiments I post here this is very much experimental code - I won't be supporting it and I would not recommend using it for anything more than casual experimentation.&lt;/p&gt;

&lt;p&gt;This demo renders &lt;a href="https://web.archive.org/web/20081207194209/simon.incutio.com/code/php/phpcf/templatetest.php"&gt;this example Recent Entries page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2003/php-recent-entries.jpg" alt="Recent Entries

• New weblog, new location • Freeing the postcode • WriteRoom • Tamarin • Fun with ctypes • Graphing requests with Tamper Data • Keep your JSON valid • What I'm excited about, post-conference edition • The YDN Python Developer Center • Sticking with Opera 9" style="max-width: 100%" /&gt;&lt;/p&gt;

&lt;p&gt;Here's the Recent Entries page PHP source code:&lt;/p&gt;

&lt;pre&gt;&lt;span class="pl-ent"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="pl-c"&gt;// Set up $db as a connection to MySQL ...&lt;/span&gt;
&lt;span class="pl-k"&gt;include&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;TemplateParser.class.php&lt;/span&gt;'&lt;/span&gt;);

&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;parser&lt;/span&gt; = &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;TemplateParser&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;db&lt;/span&gt;, &lt;span class="pl-en"&gt;array&lt;/span&gt;(
    &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;title&lt;/span&gt;'&lt;/span&gt; =&amp;gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;Recent Entries&lt;/span&gt;'&lt;/span&gt;, 
    &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;limit&lt;/span&gt;'&lt;/span&gt; =&amp;gt; &lt;span class="pl-c1"&gt;10&lt;/span&gt;
));

&lt;span class="pl-k"&gt;echo&lt;/span&gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;parser&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;parse&lt;/span&gt;(&lt;span class="pl-en"&gt;implode&lt;/span&gt;(&lt;span class="pl-s"&gt;''&lt;/span&gt;, &lt;span class="pl-en"&gt;file&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;templates/recent.xml&lt;/span&gt;'&lt;/span&gt;)));
&lt;span class="pl-ent"&gt;?&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;It renders this &lt;code&gt;recent.xml&lt;/code&gt; template:&lt;/p&gt;

&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;template&lt;/span&gt; &lt;span class="pl-e"&gt;id&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;doctype&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;xhtml1strict&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;/&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;html&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;head&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span class="pl-ent"&gt;meta&lt;/span&gt; &lt;span class="pl-e"&gt;http-equiv&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Content-Type&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;content&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;text/html; charset=utf-8&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;/&amp;gt;
      &amp;lt;&lt;span class="pl-ent"&gt;title&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;title&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span class="pl-ent"&gt;style&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;text/css&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
        body {
          font-family: georgia;
          margin: 2em;
        }
      &amp;lt;/&lt;span class="pl-ent"&gt;style&lt;/span&gt;&amp;gt;
    &amp;lt;/&lt;span class="pl-ent"&gt;head&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;body&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span class="pl-ent"&gt;h1&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;h1&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span class="pl-ent"&gt;sql&lt;/span&gt; &lt;span class="pl-e"&gt;id&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
        select title
        from entries 
        order by added desc
        limit 0, %limit%
      &amp;lt;/&lt;span class="pl-ent"&gt;sql&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span class="pl-ent"&gt;ul&lt;/span&gt;&amp;gt;
        &amp;lt;&lt;span class="pl-ent"&gt;output&lt;/span&gt; &lt;span class="pl-e"&gt;sql&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;recent&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
          &amp;lt;&lt;span class="pl-ent"&gt;li&lt;/span&gt;&amp;gt;%title%&amp;lt;/&lt;span class="pl-ent"&gt;li&lt;/span&gt;&amp;gt;
        &amp;lt;/&lt;span class="pl-ent"&gt;output&lt;/span&gt;&amp;gt;
      &amp;lt;/&lt;span class="pl-ent"&gt;ul&lt;/span&gt;&amp;gt;
    &amp;lt;/&lt;span class="pl-ent"&gt;body&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span class="pl-ent"&gt;html&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class="pl-ent"&gt;template&lt;/span&gt;&amp;gt;&lt;/pre&gt;

&lt;p&gt;And here's the &lt;code&gt;TemplateParser.class.php&lt;/code&gt; class that does all the work:&lt;/p&gt;

&lt;pre&gt;&lt;span class="pl-ent"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="pl-k"&gt;class&lt;/span&gt; TemplateParser {
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;false&lt;/span&gt;; &lt;span class="pl-c"&gt;// If true, tags are "collected" to $buffer rather than being added to $output&lt;/span&gt;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;buffer&lt;/span&gt; = &lt;span class="pl-s"&gt;''&lt;/span&gt;;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;output&lt;/span&gt; = &lt;span class="pl-s"&gt;''&lt;/span&gt;;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;parser&lt;/span&gt;;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;sqlid&lt;/span&gt;;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;outputsql&lt;/span&gt;;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;sql&lt;/span&gt; = &lt;span class="pl-en"&gt;array&lt;/span&gt;();   &lt;span class="pl-c"&gt;// 2D array of extracted SQL&lt;/span&gt;
    &lt;span class="pl-c"&gt;// -&lt;/span&gt;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;db&lt;/span&gt;;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt; = &lt;span class="pl-en"&gt;array&lt;/span&gt;(); &lt;span class="pl-c"&gt;// 2D array of replacement to make on cdata&lt;/span&gt;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;templatedir&lt;/span&gt; = &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;templates/&lt;/span&gt;'&lt;/span&gt;;
    &lt;span class="pl-c"&gt;// -&lt;/span&gt;
    var &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;doctypes&lt;/span&gt; = &lt;span class="pl-en"&gt;array&lt;/span&gt;(
        &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;html4strict&lt;/span&gt;'&lt;/span&gt; =&amp;gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&amp;gt;&lt;/span&gt;'&lt;/span&gt;,
        &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;html4trans&lt;/span&gt;'&lt;/span&gt;  =&amp;gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&amp;gt;&lt;/span&gt;'&lt;/span&gt;,
        &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;xhtml1strict&lt;/span&gt;'&lt;/span&gt; =&amp;gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;&lt;/span&gt;'&lt;/span&gt;,
    );
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;TemplateParser&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;db&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt; = &lt;span class="pl-en"&gt;array&lt;/span&gt;()) {
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;db&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;db&lt;/span&gt;;
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;replacements&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt;;
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;parse&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;data&lt;/span&gt;) {
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt; = &lt;span class="pl-en"&gt;xml_parser_create&lt;/span&gt;();
        &lt;span class="pl-c"&gt;// Set XML parser to take the case of tags in to account&lt;/span&gt;
        &lt;span class="pl-en"&gt;xml_parser_set_option&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;, &lt;span class="pl-c1"&gt;XML_OPTION_CASE_FOLDING&lt;/span&gt;, &lt;span class="pl-c1"&gt;false&lt;/span&gt;);
        &lt;span class="pl-c"&gt;// Set XML parser callback functions&lt;/span&gt;
        &lt;span class="pl-en"&gt;xml_set_object&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;);
        &lt;span class="pl-en"&gt;xml_set_element_handler&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;, &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;tag_open&lt;/span&gt;'&lt;/span&gt;, &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;tag_close&lt;/span&gt;'&lt;/span&gt;);
        &lt;span class="pl-en"&gt;xml_set_character_data_handler&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;, &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;cdata&lt;/span&gt;'&lt;/span&gt;);
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;xml_parse&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;data&lt;/span&gt;)) {
            &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-en"&gt;sprintf&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;XML error: %s at line %d&lt;/span&gt;'&lt;/span&gt;,
                &lt;span class="pl-en"&gt;xml_error_string&lt;/span&gt;(&lt;span class="pl-en"&gt;xml_get_error_code&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;)),
                &lt;span class="pl-en"&gt;xml_get_current_line_number&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;)));
        }
        &lt;span class="pl-en"&gt;xml_parser_free&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;parser&lt;/span&gt;);
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-en"&gt;trim&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt;);
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;tag_open&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;parser&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;tag&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;) {
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;singletag&lt;/span&gt; = &lt;span class="pl-c1"&gt;false&lt;/span&gt;;
        &lt;span class="pl-k"&gt;switch&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;tag&lt;/span&gt;) {
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;doctype&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;handle_doctype&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;);
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;include&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;isset&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;template&lt;/span&gt;'&lt;/span&gt;])) {
                    &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;include tag has no template attribute&lt;/span&gt;'&lt;/span&gt;);
                }
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;path&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;templatedir&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;template&lt;/span&gt;'&lt;/span&gt;].&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;.xml&lt;/span&gt;'&lt;/span&gt;;
                &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;file_exists&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;path&lt;/span&gt;)) {
                    &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;"&lt;span class="pl-s"&gt;Template &lt;/span&gt;&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;path&lt;/span&gt;&lt;span class="pl-s"&gt; does not exist&lt;/span&gt;"&lt;/span&gt;);
                }
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;temp&lt;/span&gt; = &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;TemplateParser&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;db&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;);
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;temp&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;parse&lt;/span&gt;(&lt;span class="pl-en"&gt;implode&lt;/span&gt;(&lt;span class="pl-s"&gt;''&lt;/span&gt;, &lt;span class="pl-en"&gt;file&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;path&lt;/span&gt;)));
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;ignore-start&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;true&lt;/span&gt;; &lt;span class="pl-c"&gt;// Although we have no intention of using it&lt;/span&gt;
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;ignore-stop&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt; = &lt;span class="pl-s"&gt;''&lt;/span&gt;;
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;false&lt;/span&gt;;
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;sql&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;isset&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;id&lt;/span&gt;'&lt;/span&gt;])) {
                    &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;sql tag has no id attribute&lt;/span&gt;'&lt;/span&gt;);
                }
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;sqlid&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;id&lt;/span&gt;'&lt;/span&gt;];
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;true&lt;/span&gt;;
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;output&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;isset&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;sql&lt;/span&gt;'&lt;/span&gt;])) {
                    &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;output tag has no sql attribute&lt;/span&gt;'&lt;/span&gt;);
                }
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;outputsql&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;sql&lt;/span&gt;'&lt;/span&gt;];
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;true&lt;/span&gt;;
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;template&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;img&lt;/span&gt;'&lt;/span&gt;:  &lt;span class="pl-c"&gt;// These are all HTML elements with no end tag&lt;/span&gt;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;br&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;link&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;meta&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;hr&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;singletag&lt;/span&gt; = &lt;span class="pl-c1"&gt;true&lt;/span&gt;;
        }
        &lt;span class="pl-c"&gt;// Now either collect or add the tag to output&lt;/span&gt;
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt; = &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;lt;&lt;/span&gt;'&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;tag&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;makeattr&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;);
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;singletag&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt; .= &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt; /&amp;gt;&lt;/span&gt;'&lt;/span&gt;;
        } &lt;span class="pl-k"&gt;else&lt;/span&gt; {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt; .= &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;gt;&lt;/span&gt;'&lt;/span&gt;;
        }
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt;;
        } &lt;span class="pl-k"&gt;else&lt;/span&gt; {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt;;
        }
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;tag_close&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;parser&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;tag&lt;/span&gt;) {
        &lt;span class="pl-k"&gt;switch&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;tag&lt;/span&gt;) {
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;sql&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;sql&lt;/span&gt;[&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;sqlid&lt;/span&gt;] = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;convert&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt;);
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt; = &lt;span class="pl-s"&gt;''&lt;/span&gt;;
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;false&lt;/span&gt;;
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;output&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-c"&gt;// This is the tricky one. Run the SQL query, loop through it&lt;/span&gt;
                &lt;span class="pl-c"&gt;// and run convert on the saved block multiple times replacing with&lt;/span&gt;
                &lt;span class="pl-c"&gt;// the results of the query&lt;/span&gt;
                &lt;span class="pl-c"&gt;// First clear the buffer&lt;/span&gt;
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;block&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt;;
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt; = &lt;span class="pl-s"&gt;''&lt;/span&gt;;
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt; = &lt;span class="pl-c1"&gt;false&lt;/span&gt;;
                &lt;span class="pl-c"&gt;// Now run the query&lt;/span&gt;
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;sql&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;sql&lt;/span&gt;[&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;outputsql&lt;/span&gt;];
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;result&lt;/span&gt; = &lt;span class="pl-en"&gt;mysql_query&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;sql&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;db&lt;/span&gt;);
                &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;result&lt;/span&gt;) {
                    &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;"&lt;span class="pl-s"&gt;SQL query failed: '&lt;/span&gt;&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;sql&lt;/span&gt;&lt;span class="pl-s"&gt;'&lt;/span&gt;"&lt;/span&gt;);
                }
                &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;i&lt;/span&gt; = &lt;span class="pl-c1"&gt;0&lt;/span&gt;;
                &lt;span class="pl-k"&gt;while&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;row&lt;/span&gt; = &lt;span class="pl-en"&gt;mysql_fetch_assoc&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;result&lt;/span&gt;)) {
                    &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;row&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;#&lt;/span&gt;'&lt;/span&gt;] = ++&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;i&lt;/span&gt;; &lt;span class="pl-c"&gt;// '%#%' is now available as a counter var&lt;/span&gt;
                    &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;convert&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;block&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;row&lt;/span&gt;);
                }    
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;template&lt;/span&gt;'&lt;/span&gt;: &lt;span class="pl-c"&gt;// These are template elements with no end tag&lt;/span&gt;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;include&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;ignore-start&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;ignore-stop&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;doctype&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;img&lt;/span&gt;'&lt;/span&gt;:  &lt;span class="pl-c"&gt;// These are all HTML elements with no end tag&lt;/span&gt;
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;br&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;link&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;meta&lt;/span&gt;'&lt;/span&gt;:
            &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;hr&lt;/span&gt;'&lt;/span&gt;:
                &lt;span class="pl-k"&gt;return&lt;/span&gt;;
        }
        &lt;span class="pl-c"&gt;// Add the end tag to the buffer or output&lt;/span&gt;
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt; = &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;lt;/&lt;/span&gt;'&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;tag&lt;/span&gt;.&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;&amp;gt;&lt;/span&gt;'&lt;/span&gt;;
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt;;
        } &lt;span class="pl-k"&gt;else&lt;/span&gt; {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;xml&lt;/span&gt;;
        }
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;cdata&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;parser&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;data&lt;/span&gt;) {
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;collect&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;buffer&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;data&lt;/span&gt;;
            &lt;span class="pl-k"&gt;return&lt;/span&gt;;
        }
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-en"&gt;convert&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;data&lt;/span&gt;);
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;convert&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;text&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt; = &lt;span class="pl-c1"&gt;false&lt;/span&gt;) {
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt; = &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;replacements&lt;/span&gt;;
        }
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (&lt;span class="pl-en"&gt;strpos&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;text&lt;/span&gt;, &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;%&lt;/span&gt;'&lt;/span&gt;) === &lt;span class="pl-c1"&gt;false&lt;/span&gt;) {
            &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;text&lt;/span&gt;; &lt;span class="pl-c"&gt;// Nothing to replace&lt;/span&gt;
        }
        &lt;span class="pl-k"&gt;foreach&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;replacements&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;from&lt;/span&gt; =&amp;gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;to&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;text&lt;/span&gt; = &lt;span class="pl-en"&gt;str_replace&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;%&lt;/span&gt;'&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;from&lt;/span&gt;.&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;%&lt;/span&gt;'&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;to&lt;/span&gt;, &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;text&lt;/span&gt;);
        }
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;text&lt;/span&gt;;
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;makeattr&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;) {
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;return&lt;/span&gt; = &lt;span class="pl-s"&gt;''&lt;/span&gt;;
        &lt;span class="pl-k"&gt;foreach&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;name&lt;/span&gt; =&amp;gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;value&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;return&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;name&lt;/span&gt;.&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;="&lt;/span&gt;'&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;value&lt;/span&gt;.&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;"&lt;/span&gt;'&lt;/span&gt;;
        }
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;return&lt;/span&gt; != &lt;span class="pl-s"&gt;''&lt;/span&gt;) {
            &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;return&lt;/span&gt; = &lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt; &lt;/span&gt;'&lt;/span&gt;.&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;return&lt;/span&gt;;
        }
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;return&lt;/span&gt;;
    }
    &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;handle_doctype&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;) {
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;isset&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;type&lt;/span&gt;'&lt;/span&gt;])) {
            &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;doctype requires type attribute&lt;/span&gt;'&lt;/span&gt;);
        }
        &lt;span class="pl-k"&gt;if&lt;/span&gt; (!&lt;span class="pl-en"&gt;in_array&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;type&lt;/span&gt;'&lt;/span&gt;], &lt;span class="pl-en"&gt;array_keys&lt;/span&gt;(&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;doctypes&lt;/span&gt;))) {
            &lt;span class="pl-en"&gt;die&lt;/span&gt;(&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;doctype requires valid type attribute&lt;/span&gt;'&lt;/span&gt;);
        }
        &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;output&lt;/span&gt; .= &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;/span&gt;-&amp;gt;&lt;span class="pl-c1"&gt;doctypes&lt;/span&gt;[&lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;$&lt;/span&gt;attr&lt;/span&gt;[&lt;span class="pl-s"&gt;'&lt;span class="pl-s"&gt;type&lt;/span&gt;'&lt;/span&gt;]];
    }
}

&lt;span class="pl-ent"&gt;?&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;With a bit more development, something like this could be a useful tool for quick-and-dirty &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; scripts that simply pull data out of MySQL and display it as &lt;acronym title="HyperText Markup Language"&gt;HTML&lt;/acronym&gt;. I still prefer &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt;, but ColdFusion has a lot of good ideas which are well worth knowing about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update July 1st 2025&lt;/strong&gt;: I &lt;a href="https://simonwillison.net/2025/Jul/1/mid-2000s/"&gt;stumbled across&lt;/a&gt; this long-forgotten experiment today and decided to have Claude write me some documentation for it, since this blog entry didn't describe all of its features. &lt;a href="https://static.simonwillison.net/static/2003/template-docs.html"&gt;Here's the result&lt;/a&gt; (and the &lt;a href="https://claude.ai/share/1627f1f3-4b07-4eb3-af24-5ac2da96b712"&gt;Claude transcript&lt;/a&gt;). Amusingly, Claude described the code like this:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This appears to be a custom template system from the mid-2000s era, designed to separate presentation logic from PHP code while maintaining database connectivity for dynamic content generation.&lt;/p&gt;&lt;/blockquote&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/coldfusion"&gt;coldfusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/php"&gt;php&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/templating"&gt;templating&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xml"&gt;xml&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="coldfusion"/><category term="php"/><category term="projects"/><category term="sql"/><category term="templating"/><category term="xml"/></entry></feed>