🦖
[■] Full guide
codesaur/template View on GitHub

codesaur/template

A self-contained PHP template engine. During its evolution, adopted syntax and design patterns inspired by Twig.

codesaur/template is part of the codesaur ecosystem and provides everything from simple text-based templates to powerful templates with control structures, filters, functions, macros, and expression parsing - all without any external dependencies.

The package consists of these classes:

  • MemoryTemplate - full template engine (if, for, filter, function, macro, expression parser, 33 built-in filters)
  • FileTemplate - file-based template loader wrapper (extends MemoryTemplate)
  • Markup - wrapper marking an HTML value as "safe" (exempt from autoescape)

Autoescape: like Twig, {{ }} HTML-escapes string values by default. Use |raw where actual HTML must be output: {{ record.content|raw }}. See Autoescape.


CI/CD

This project is automatically tested using GitHub Actions. The CI/CD pipeline checks the following:

  • Syntax check on PHP 8.2
  • Composer dependencies installation
  • PHP file syntax error check
  • PHPUnit unit tests execution
  • PHPUnit integration tests execution

CI/CD status can be viewed on the GitHub Actions page.


Features

  • Supports all whitespace/no-whitespace formats such as {{ key }}, {{key}}, {{ user.profile.email }}
  • Nested variable support (multi-level arrays)
  • Full template syntax: if/elseif/else, for loops, macros, filters, functions, expressions
  • Autoescape on by default (XSS protection), exempt via |raw or Markup, disable via setAutoEscape(false)
  • 33 built-in filters (e, escape, date, length, keys, slice, split, merge, json_encode, number_format, capitalize, upper, lower, default, round, nl2br, url_encode, format, first, last, etc.)
  • Custom filter/function registration: addFilter(), addFunction()
  • MemoryTemplate runs the full engine. FileTemplate is just a file-reading wrapper
  • Framework-agnostic, fully compatible with codesaur, Laravel, Symfony, Slim and all other PHP frameworks
  • Complete PHPDoc documentation
  • Unit, Integration, Performance and Memory tests (156 tests, 449 assertions)

Installation

Install via Composer:

composer require codesaur/template

Usage Example 1 - MemoryTemplate

MemoryTemplate now includes the full engine - if, for, filter, function all work directly:

use codesaur\Template\MemoryTemplate;

$template = new MemoryTemplate(
    '{% for user in users %}{{ user.name|upper }}, {% endfor %}',
    ['users' => [
        ['name' => 'Narankhuu'],
        ['name' => 'Erdenebat'],
    ]]
);

echo $template; // NARANKHUU, ERDENEBAT,

Simple placeholder example:

$template = new MemoryTemplate(
    'Hello, {{ user.name }}!',
    ['user' => ['name' => 'Narankhuu']]
);

echo $template; // Hello, Narankhuu!

Register custom function:

$template = new MemoryTemplate('{{ link("home") }}');
$template->addFunction('link', fn($route) => "/app/$route");
echo $template; // /app/home

Autoescape

{{ }} escapes string values with htmlspecialchars by default. User-supplied values placed directly into a template cannot cause XSS:

$template = new MemoryTemplate('<p>{{ comment }}</p>', ['comment' => '<script>alert(1)</script>']);
echo $template; // <p>&lt;script&gt;alert(1)&lt;/script&gt;</p>

Use |raw to output trusted HTML:

$template = new MemoryTemplate('<article>{{ content|raw }}</article>', ['content' => '<h1>Title</h1>']);
echo $template; // <article><h1>Title</h1></article>

Values that are not escaped: results of |raw and |e, Markup objects, nested MemoryTemplate/FileTemplate objects ({{ content }} in a layout), macro return values, numbers/bool/null. Filters that produce HTML-significant characters such as json_encode and nl2br must be followed by |raw:

<script>var data = {{ data|json_encode|raw }};</script>
{{ text|nl2br|raw }}

Custom filters/functions returning HTML should wrap the result in Markup:

use codesaur\Template\Markup;

$template->addFilter('badge', fn($v) => new Markup('<span class="badge">' . htmlspecialchars((string) $v) . '</span>'));

Disable it for non-HTML templates (plain text, e-mail, ...):

$template->setAutoEscape(false);

Full safe-value rules and Twig comparison: API - Autoescape.


Usage Example 2 - FileTemplate

FileTemplate reads from files and processes through MemoryTemplate's engine:

use codesaur\Template\FileTemplate;

$template = new FileTemplate(__DIR__ . '/page.html', [
    'title' => 'Hello Codesaur',
    'items' => ['Home', 'About', 'Contact']
]);
$template->addFunction('text', fn($key) => $translations[$key] ?? $key);

echo $template->output();

Usage Example 3 - FileTemplate (Bootstrap example)

example/index.php:

use codesaur\Template\FileTemplate;

$template = new FileTemplate(__DIR__ . '/example.html', [
    'title' => 'Template Example',
    'menu'  => ['Home', 'About', 'Products', 'Contact'],
    'items' => [
        ['title' => 'Lightweight', 'text' => 'Fast, simple template system.'],
        ['title' => 'Flexible', 'text' => 'Supports Plain and File-based Templates.'],
        ['title' => 'Powerful', 'text' => 'Can use nested variables, filters, functions.'],
    ]
]);

$template->render();

example/example.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">

<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
    <div class="container">
        <a class="navbar-brand" href="#">codesaur/template</a>
        <ul class="navbar-nav ms-auto">
            {% for item in menu %}
                <li class="nav-item"><a href="#" class="nav-link text-uppercase">{{ item }}</a></li>
            {% endfor %}
        </ul>
    </div>
</nav>

<div class="container">
    <div class="text-center mb-5">
        <h1>{{ title }}</h1>
    </div>

    <div class="row g-4">
        {% for box in items %}
        <div class="col-md-4">
            <div class="card shadow-sm border-0 h-100 text-center">
                <div class="card-body">
                    <h4>{{ box.title }}</h4>
                    <p class="text-muted">{{ box.text }}</p>
                </div>
            </div>
        </div>
        {% endfor %}
    </div>
</div>

</body>
</html>

Running Tests

This package includes complete tests using PHPUnit. All tests can be run using Composer:

1. Install Dependencies

composer install

2. Run All Tests

composer test

3. Run Specific Test File

vendor/bin/phpunit tests/MemoryTemplateTest.php

Windows users: Replace vendor/bin/phpunit with vendor\bin\phpunit.bat

Test Files

  • tests/MemoryTemplateTest.php - MemoryTemplate class unit tests
  • tests/EngineTest.php - Template engine unit tests (if, for, filter, macro)
  • tests/AutoEscapeTest.php - Autoescape, safe rules, Markup, disabling
  • tests/Integration/TemplateIntegrationTest.php - Integration tests
  • tests/PerformanceTest.php - Performance tests
  • tests/MemoryTest.php - Memory usage tests

Test Statistics

  • Total Tests: 156 tests
  • Assertions: 449 assertions

API Overview

MemoryTemplate

Full template engine - includes if, for, filter, function, macro, and expression parser:

  • __construct(string $template = '', array $vars = [])
  • set(string $key, $value) / setVars(array $values) / get(string $key) / getVars(): array
  • source(string $html) / getSource(): string
  • output(): string / render() / __toString()
  • addFilter(string, callable) / addFunction(string, callable)
  • setAutoEscape(bool) / isAutoEscape(): bool

Markup

  • __construct(string $html) / __toString(): string - safe HTML marker

FileTemplate

Inherits all MemoryTemplate API + file management:

  • file(string $filepath) / getFileName(): string
  • getFileSource(): string
  • output(): string (reads file and compiles)

Documentation

  • API - Complete API documentation
  • REVIEW - Code review report

Changelog

For version history and changes, see CHANGELOG file.


License

This project is licensed under the MIT license.


Author

Narankhuu https://github.com/codesaur