Skip to Content
Course content

140: Practice Exercise: Building a Simple Templating Engine

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Before we dive into the code, I want to address a hurdle I see almost every junior dev hit when they start thinking about templating. Most people assume that building a "templating engine" is just a fancy way of saying "I'm going to use include 'header.php'; and include 'footer.php';."

It's Just a Fancy Wrapper for include()

If you're just including files, you aren't using an engine; you're just organizing your files. The problem with the include approach is that it allows any PHP code to run inside your template. While that sounds powerful, it's a nightmare for maintenance. If your template file contains a complex SQL query or a loop that crashes the page, your presentation layer is now tightly coupled to your business logic. You've basically just moved the mess from one file to another.

A real templating engine treats the template as a string or a resource to be parsed, not as a script to be executed. The engine acts as a gatekeeper, deciding exactly what data gets passed in and how it's displayed, without letting the template "reach out" and touch the database itself.

Decoupling Data from Presentation with Token Replacement

The core of a simple engine is the "token." Instead of writing <?php echo $username; ?>, we use a placeholder that the engine recognizes—something like {{ username }}. This makes the template readable for designers who might not know a lick of PHP.

I usually start with a basic replacement loop. Here is the logic: we load the template file into a string, look for our tokens, and swap them out for values from an associative array. It's surprisingly simple when you strip away the complexity of frameworks like Twig or Blade.

class SimpleEngine {
    public function render($templateFile, $data) {
        $content = file_get_contents($templateFile);
        
        foreach ($data as $key => $value) {
            // We look for {{ key }} and replace it with the value
            $content = str_replace('{{ ' . $key . ' }}', $value, $content);
        }
        
        return $content;
    }
}

I'll be honest: str_replace is fine for a tiny project, but it's blunt. If you have a template with 50 variables, you're running 50 string searches. In a production environment, we'd use regular expressions or a compiled cache, but for our purposes, this proves the concept: the template is data, and the engine is the processor.

Adding Safety and Flexibility to Your Parser

There's a glaring security hole in the code above: XSS (Cross-Site Scripting). If $value contains a <script> tag from a user's profile, your engine will happily render it, and your site will be hacked. A professional engineer never trusts the data being injected into a template.

We need to wrap our values in htmlspecialchars(). I also recommend adding a way to handle "missing" tokens so your page doesn't just show {{ missing_var }} to the end user, which looks broken and unprofessional.

foreach ($data as $key => $value) {
    $safeValue = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
    $content = str_replace('{{ ' . $key . ' }}', $safeValue, $content);
}

Now, we've moved from "just including files" to a system that manages data flow, ensures security, and maintains a strict boundary between how the app works and how it looks. That is the essence of a templating engine.




📋 Practical Task

The Mini-Template Engine: User Profile Generator

Your task is to build a functional TemplateEngine class that can dynamically render a user profile page. You need to ensure that the engine is secure and can handle multiple variables.

  • Create a file named profile.html. Inside it, use placeholders like {{ name }}, {{ bio }}, and {{ location }}.
  • Implement a TemplateEngine class with a render($filePath, $data)` method.
  • The render method must:
    1. Read the content of the profile.html file.
    2. Iterate through the $data array and replace the corresponding placeholders.
    3. Use htmlspecialchars() on all values to prevent XSS attacks.
    4. Return the final string.
  • Test your engine by passing an associative array containing a name, a bio with some HTML tags (to test the escaping), and a location.

Example Test Case:

$data = [
    'name' => 'Jane Doe',
    'bio' => 'I love PHP and <script>alert("hack")</script>!',
    'location' => 'New York'
];

$engine = new TemplateEngine();
echo $engine->render('profile.html', $data);
// The output should show the bio text literally, not execute the script or render the bold tag.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.