Everything on the web needs templates.

While you can argue that MVC is overkill for simple applications, even the simplest of systems need templates as soon as they're outputting HTML.

Since PHP was designed for templating we can always use it as a quick 'n dirty templating system. Quick because you already have it and dirty because its unclear separation between code and template leaves it open for abuse.

While there are many, many systems that exploit this property of PHP, I'd like to demo a simpler system that works in just 5 lines of code:

Line by line

So first we have our function declaration. We take a template as our argument, with an array of arguments to the template as an optional second argument.

Using extract with EXTR_SKIP means we unpack the array into the current scope, and skip any array elements that already exist. This means we don't have to worry about overwriting superglobals.

By giving the template and args parameters long random names we can prevent accidental collisions there too. (Since $args and $template might be things you actually want to pass to a template) You can always make these longer if you're actually having collisions here.

We turn on output buffering and include the file in place. The file will be executed with the scope we're in, so the net effect is that all the args we passed to template will be available as variables in the included file.

In this example we just look for a file in a predefined folder to include, but if you want to add more logic and have multiple include folders to choose from that wouldn't take much code either.

Lastly we stop the output buffering and return it.

What it can do

Here's a quick example of what it can do:

include 'template.php';

define('TEMPLATE_DIR', __DIR__.'/templates/');

$args = [
    'title' => 'This web page',
    'text' => 'Hello world!',
    'logo' => 'mypic.svg',
];

echo template('myview.php', $args);
<!DOCTYPE html>
<html>
    <head><?= template('head.php', ['title' => $title]) ?></head>
    <body>
        <header><?= template('header.php', ['logo' => $logo]) ?></header>
        <main><?= $text ?></main>
    </body>
</html>
<title><?= $title ?></title>
<img src="<?= $logo ?>" />

Needless to say, as it's just PHP you'll need to escape your output manually.

With extends in 25 lines

Templates that can extend other templates are fairly popular. To do this we're going to wrap it in a class which will make it a bit more complicated, but shouldn't be too hard.

So we've added a few features:

  • When we instantiate our Template class we can pass in the template dir, so it's not a hardcoded define any more.
  • We have a wrapper around our original template function which handles rendering extending parents.
  • Because we're in a class we can tuck our args and template away inside the class to make the scope completely clean.

The new example code looks like this:

include 'template.php';

$template = new Template(__DIR__.'/templates/');

$args = [
    'title' => 'This web page',
    'text' => 'Hello world!',
    'logo' => 'mypic.svg',
];

echo $template->render('myview.php', $args);
<?php $this->extend('base.php'); ?>
<header><?= $this->render('header.php', ['logo' => $logo]) ?></header>
<main><?= $text ?></main>
<!DOCTYPE html>
<html>
    <head><?= $this->render('head.php', ['title' => $title]) ?></head>
    <body><?= $contents ?></body>
</html>
<title><?= $title ?></title>
<img src="<?= $logo ?>" />