Web template delimiters: a chronology

This page presents a chronology of delimiters used in web template systems. The delimiters are the markers that separate the logic from the HTML in a template. Common examples of delimiters include <?php ... ?>, {% ... %} (Django, Jinja), and {{ ... }} (Mustache). I created the page because I became interested in the question of where the delimiters <% ... %>, which I had used in my own template parsers, came from. It seems that my original impression was correct: they come from ASP.

For every template system below, the overview will cover the initial version. An attempt will be made to trace the influences. Besides the raw delimiters themselves, there will be a brief look into the syntax inside the delimiters. Itt is relevant in systems that do not allow arbitrary code in the host language.

The items are be sorted by their date of release. They use different terminology. For example, “tags” (Smarty, Django) and “directives” (SSI) can mean the control flow and functions in the template, but “directives” can also mean only those elements that change the template configuration (JSP). I am going to refer to them as “code”.

Server Side Includes (SSI) appeared in one of the earliest web servers, NCSA HTTPd. SSI uses HTML comments with # after the opening comment tag. It has the following delimiter syntax:

  • Code: <!--#foo -->

The first version of NCSA HTTPd to implement SSI that I could find is 1.2. It adds src/http_include.c. The file is signed by web pioneer Rob McCool.

From the source code to my SDF homepage:

<details>
<summary>
View contents.
</summary>
<pre><!--#include file="B2SUMS" --></pre>
</details>
[...]
<footer>
<p><a href="https://sdf.org/"><img src="/sdfbanner.png" alt="SDF.org banner depicting the S, D, F keyboard keys." /></a></p>
<!-- Server Side Includes power! --><!--#config timefmt="%Y-%m-%d %H:%M:%S %Z" -->
<p>Served <!--#echo var="DATE_GMT" --></p>
</footer>

The first version PHP was created by Danish-Canadian programmer Rasmus Lerdorf only a year after SSI. It also used HTML comments.

  • Code: HTML comments (<!-- ... -->)

PHP 1.0.8 that implements this syntax is available from the PHP museum. You can see the delimiter literals in the source code. I couldn’t get PHP 1.0.8 to run. A binary built on Debian 12 segfaulted.

From “History of PHP”:

<!--include /text/header.html-->

<!--getenv HTTP_USER_AGENT-->
<!--ifsubstr $exec_result Mozilla-->
  Hey, you are using Netscape!<p>
<!--endif-->

<!--sql database select * from table where user='$username'-->
<!--ifless $numentries 1-->
  Sorry, that record does not exist<p>
<!--endif exit-->
  Welcome <!--$user-->!<p>
  You have <!--$index:0--> credits left in your account.<p>

<!--include /text/footer.html-->

The second release of PHP changed the syntax. This was already the case in version 1.99s, which acted as a preview for the next major version.

  • Code: <? ... ?>

From examples/demo_dns.html:

<?echo gethostbname("www.yahoo.com")>

Embedded Perl, or ePerl, was created by Ralf S. Engelschall in 1996. Although ePerl was only developed from 1996 to 1998, its official web page remains available in 2024. It is implemented in C. The earliest version you can download from the page is 1.3. According to the changelog, it was the first version released on the web.

  • Code: <? ... ?>

From sample/demo-01.phtml:

Welcome user from <? print $ENV{'REMOTE_HOST'}; !>, nice to meet you!

In late 1996 Microsoft releaised Active Server Pages, a server-side scripting extension for the ISS web server on Windows NT 4.0.

  • Code: <% ... %>
  • Expressions: <%= ... %>

From Programming Active Server Pages, Scot Hillier1997, listing 1-6:

<%For x = 1 to 6%>
    <FONT FACE="ARIAL" SIZE=<%=x%>>
    ActiveX Is Cool!
    </FONT>
    <P>
<%Next%>

The PHP 3.0.1 release added the familiar <?php ... ?> tags and an expression syntax.

  • Code: <?php ... ?>, <? ... ?>
  • Expressions: <?= ... ?>

Soon after, version 3.0.4 added optional support for <% ... %> and <%= ... %>, which never became popular.

From examples/dir.php3:

<P>Code:

<PRE>
&lt;?
<? $code = "\$dir = opendir(\".\");
while(\$file = readdir(\$dir)) {
echo \"\$file<BR>\";
}
closedir(\$dir);
";
echo HTMLSpecialChars($code);
echo "?>";
?>
</PRE>

<P>Output:

<PRE>
<?eval($code);?>
</PRE>

Embedded Ruby or ERB is a template system created by Masatoshi Seki that is included in the standard library of the Ruby programming language. The earliest ERB commit on GitHub is from 2002. The Wayback Machine has preserved an earlier version from 1999. The archived copy of the author’s Japanese-language page calls ERB “one of ePerl-like eRuby implementations”. Version 1.3.1 from 2000 uses the name “Tiny Tiny eRuby”.

  • Code: <% ... %>
  • Code line: % ... (optional line processing)
  • Expression: <%= ... %>
  • Comment: <%# ... %>

If line processing is enabled, lines that begin with %% have one % removed and are used verbatim. <%% and %%> are replaced with literal <% and %> respectively.

From sample/cgi_test.rhtml in version 1.3.1 modified to use h for escaping:

<UL><% ENV.each do |k,v| %>
<LI><%= h k %>:<%= h v %>
<% end %></UL>

JavaServer Pages, now Jakarta Server Pages, embeds Java code in HTML.

  • Code: <% ... %>
  • Expressions: <%= ... %>
  • Configuration: <%@ ... %>

From “Jakarta Server Pages Syntax” in Jakarta EE Programming on Wikibooks:

<%@ page errorPage="myerror.jsp" %>
<%@ page import="java.text.DateFormat" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Calendar" %>

<html>
  <body>
<% DateFormat dateFormat =
              new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
   Calendar cal = Calendar.getInstance();
%>
    <span style="color: blue;">
      <%= dateFormat.format(cal.getTime()) %>
    </span>
  </body>
</html>

The Smarty template engine was originally conceived by Monte Ohrt and rewritten and extended by Andrei Zmievski. I looked at version 1.0, which the project got to quickly.

  • Code: {foo}. Paired tags are closed with {/foo}.
    • Conditional: {if $foo eq ""} ... {elseif $bar eq ""} ... {else} ... {/if}
    • Iteration: {section name=foo loop=$items} ... {sectionelse} ... {/section}
  • Variables: {$foo}, {$foo|bar:"Baz"} where bar is called a “modifier”, {%foo%} for internal variables.

From QUICKSTART:

--------- templates/index.tpl --------
{include file="header.tpl" title="Home Page"}
    Hello, {$Name}!<br>
    {$FirstName}, {$LastName}<br>
    {$Address}, {$Zipcode}
{include file="footer.tpl"}

--------- templates/header.tpl --------
<HTML>
<TITLE>{$title|default:"Home Page"}</TITLE>
<BODY>

--------- templates/footer.tpl --------
</BODY>
</HTML>

Cheetah or CheetahTemplate is an early template engine for Python impemented by the Cheetah Development Team. The team consisted of Tavis Rudd, Mike Orr, Chuck Esterbrook, Ian Bicking, and Tom Schwaller according to the readme for version 0.9.6, the earliest available for download. (Version 0.9.5 was announced but doesn’t seem archived.) Cheetah has line-based statements, statements that continue from the marker to the end of the line.

  • Code: # ...
  • Variables: $foo

Version 0.9.6 documentation does not have a detailed sample. This sample comes from the version 0.9.8a3 user’s guide:

<HTML>
<HEAD><TITLE>$title</TITLE></HEAD>
<BODY>

<TABLE>
#for $client in $clients
<TR>
<TD>$client.surname, $client.firstname</TD>
<TD><A HREF="mailto:$client.email">$client.email</A></TD>
</TR>
#end for
</TABLE>

</BODY>
</HTML>

The Django template language appeared in a very recognizable form in first 2005 public release of the Django web framework. Django was created by Adrian Holovaty and Simon Willison. Django templates proceeded to inspire Jinja (Python), Nunjucks (JavaScript), and other template languages.

In the first downloadable release, version 0.90, docs/templates.txt says (links changed):

If you have any exposure to other text-based template languages, such as Smarty or CheetahTemplate, you should feel right at home with Django’s templates.

  • Tags: {% ... %}
  • Variables: {{ foo }}, {{ foo|bar:"Baz" }}

Here, bar is called a “filter” and is similar to a Smarty modifier.

From the same docs/templates.txt:

{% extends "base_generic" %}

{% block title %}{{ section.title }}{% endblock %}

{% block content %}
<h1>{{ section.title }}</h1>

{% for story in story_list %}
<h2>
  <a href="{{ story.get_absolute_url }}">
    {{ story.headline|upper }}
  </a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}

CTemplate is a public version of the Google Template System, the actual template system used for Google search result pages. Version 0.1 was open-sourced in 2006 on SourceForge because Google Code did not exist yet. The documentation is bears the name of Craig Silverstein, the first employee at Google. The popular Mustache template spec essentially specified CTemplate before building on it, so what used to be Google’s internal template syntax is “everywhere” now (is implemented for lots and lots of languages).

  • Code: {{ ... }}
    • Conditional/iteration: {{#FOO}} ... {{/FOO}}
    • Partial: {{>FOO}}
  • Variable: {{FOO}}, {{FOO:html_escape}}, {{FOO:javascipt_escape}}, {{FOO:h:j}}
  • Comment: {{! Lorem ipsum. }}

html_escape or h and javascipt_escape or j are called “modifiers” and change how the value is formatted. They can be chained.

From doc/index.html. The dollar signs are not part of the syntax; they simply mean American dollars.

Hello {{NAME}},
You have just won ${{VALUE}}!
{{#IN_CA}}Well, ${{TAXED_VALUE}}, after taxes.{{/IN_CA}}

Mustache is a template system developed by Chris Wanstrath. It popularized the term “logic-less” for templates that do not allow arbitrary code. The readme cites CTemplate and et as inspiration. Muschare is heavily influencrd by CTemplate and uses the “taxes” example in the readme and tests.

According to the commit history, the initial version of Mustache was developed in 39 commits over the course of one day. We’ll use the last commit of the day.

  • Code: {{ ... }}
    • Conditional/iteration: {{#foo}} ... {{/foo}}
    • Inverted conditional: {{^foo}} ... {{/foo}}
    • Partial: {{<foo}}
  • Variable: {{foo}}
  • Comment: {{! Lorem ipsum. }}

The variable syntax is also used for lambdas, user-supplied functions. Mustache does not implement modifiers like CTemplate.

The syntax for partials was changed a few weeks later to {{>foo}} for compatibility with CTemplate.

From mustache(5):

{{#repo}}
  <b>{{name}}</b>
{{/repo}}
{{^repo}}
  No repos :(
{{/repo}}