Blog about software development


PHP templating engine from scratch

01 May 2019 - by 'Maurits van der Schee'

In a previous post I have introduced PHP templating in 165 lines of code with no dependencies. I have added several features since it's initial release and the line count has roughly doubled. This post gives you an overview of the added functionality and explains how to use it.

An 'if' now supports 'else' and 'elseif'

Conditionals are an essential construct in a template. When you implement them without 'else' or 'elseif' you get either a lot of repeated conditions and unnecessary nesting.

Published: {{if:post.published}}yes{{else}}no{{endif}}

You may also use the 'elseif' statement:

    every {{post.frequency}} days

Note that the 'else' statement is not required.

Looping over the keys of an array

While it may be very useful to loop over the elements of an array, you sometimes have a associative array (or hashmap) and want to loop over the keys. This was the 'for' syntax:


Now you may also use this alternative 'for' syntax:


As you can see this alternative form of 'for' statement is detected/determined by the number of parameters of the statement.

HTML escaping to prevent XSS

Values that are inserted in the template may contain JavaScript and thus open up a XSS vulnerability (which can be used for session stealing and such). To avoid XSS, all values that are replaced in the template are escaped. This can be turned off in case you are generating plain text. This means that:

<a href="{{url}}">click here</a>

can be used without worrying that a user will insert an 'onclick' event by letting the variable 'url' contain a closing quote that is interpreted as such.

Strings don't need escaping

Since we are now using a string-aware split function (see: Split while respecting quotes in PHP) you do not have to worry that characters between quotes have effects on higher level split operations. This means that you can write:

{{date|formatDate("M j, Y")}}

without having to worry that the comma is the argument separator for the 'formatDate' function (and you can use the pipe character in the string).


The Template class has grown a bit (to 320 lines) and gained some valuable features. It's parsing strategy is not the fastest, but it will do for small to medium size templates. There are still no dependencies and it should be easily portable to other languages. You find the source code on Github:


and the tests:



PS: Liked this article? Please share it on Facebook, Twitter or LinkedIn.