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|rawwhere 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
|raworMarkup, disable viasetAutoEscape(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><script>alert(1)</script></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 teststests/EngineTest.php- Template engine unit tests (if, for, filter, macro)tests/AutoEscapeTest.php- Autoescape, safe rules,Markup, disablingtests/Integration/TemplateIntegrationTest.php- Integration teststests/PerformanceTest.php- Performance teststests/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(): arraysource(string $html)/getSource(): stringoutput(): 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(): stringgetFileSource(): stringoutput(): string(reads file and compiles)
Documentation
Changelog
For version history and changes, see CHANGELOG file.
License
This project is licensed under the MIT license.
Author
Narankhuu https://github.com/codesaur