Templatest Template Engine: Technical Documentation
Comprehensive technical documentation for the Templatest template engine:
1. Introduction
Templatest is a sophisticated, transition-style template engine for WackoWiki that combines:
- Push-style architecture with pull methods (for CSRF tokens, i18n, etc.)
- Lazy evaluation and topologically-ordered compilation
- Incremental building freed from traditional von Neumann applicative constraints
- Efficient caching with automatic cache invalidation
The engine prioritizes performance through compilation-time optimization while maintaining flexibility for runtime customization.
1.1. Key Characteristics
- Compile-time focused: Templates are parsed, compiled, and cached for optimal performance
- Modular filter system: Extensible filtering pipeline with escape handling
- Static analysis: Identifies and optimizes static content at compile time
- Push/Pull duality: Supports both data pushing (pull functions) and variable setting
- Hierarchical processing: Nested pattern support with automatic indentation
- --
2. Architecture Overview
2.1. Class Hierarchy
Templatest (Compiler & Factory)
↓
TemplatestSetter (Setting & Patching)
↓
TemplatestFilters (Filter Pipeline)
↓
TemplatestEscaper (Security & Encoding)
↓
TemplatestUser (Public API & Rendering)
2.2. Processing Pipeline
Template File
↓
[Parsing] → parse_file()
↓
[Definition Extraction] → inline_definitions()
↓
[Compilation] → compile()
↓
[Static Optimization] → inline_static_subs()
↓
[Default Application] → inline_defaults()
↓
[Caching] → serialize to cache file
↓
TemplatestUser Instance (Ready for use)
- --
3. Core Concepts
3.1. 1. Patterns
Patterns are named template blocks that serve as reusable components. They're the fundamental building unit of Templatest.
[ ==== patternname ==== ]
template content here
[ ==== otherpattern ==== ]
other content
- Main pattern: The first pattern in a file (stored as
$store[0]) - Named patterns: Can be called by name as subpatterns or variables
- Anonymous patterns: Temporary patterns with autogenerated names (when using
_as name)
3.2. 2. Variables
Variables are placeholders within patterns that receive data at runtime.
[ ==== mypattern ==== ] Hello, [ ' name ' ]!
Variables are:
- Marked with quotes:
[ ' varname ' ] - Assigned data through the
set()method - Subject to filtering and escaping
- Can use dot notation for array access:
[ ' user.profile.name ' ]
3.3. 3. Subpatterns
Subpatterns are pattern references within other patterns, enabling composition.
[ ==== layout ==== ] <div class="header">[ ' header ' ]</div> <div class="content">[ ' content ' ]</div> [ ==== page ==== ] This is the page content
When layout is rendered with set('content', 'page'), the page pattern is inserted into the content placeholder.
3.4. 4. Pull Actions
Pull functions are runtime callbacks for dynamic content generation (CSRF tokens, internationalization, etc.).
[ ==== form ==== ]
<form>
[ ' csrf: ' ]
[ ' form_fields: ' ]
</form>
Format: [ ' actionname: args ' ]
- --
4. Template Syntax
4.1. Tag Structure
Tags use a quoted-content pattern with optional pipes:
[ ' tag_content | filter1 arg1 | filter2 arg1 arg2 ' ]
4.2. Quote Levels
The number of quotes determines tag recognition:
[ ' content ' ] - single quotes (standard) [ '' content '' ] - double quotes (requires double quotes to open) [ ''' content ''' ] - triple quotes (requires triple quotes to open)
This prevents premature tag matching when quotes appear in content.
4.3. Comments
[ ==== // this is a comment ==== ] [ ==== # this is also a comment ==== ]
4.4. Directives (Outside Patterns)
Directives appear before any pattern definition:
4.4.1. .escape – Set default escaping mode
.escape html # Apply HTML escaping to all patterns .escape js mypattern # Apply JavaScript escaping only to mypattern
Valid modes:
-
raw– No escaping -
html– HTML entity escaping -
js– JavaScript string escaping -
css– CSS escaping -
url– URL encoding -
html_attrorattr– HTML attribute escaping
4.4.2. .patch – Set default values
.patch mypattern varname defaultvalue
4.4.3. .include – Include external templates
.include path/to/template.tpl
Maximum inclusion depth: 5 levels (circular includes prevented)
- --
5. Patterns and Variables
5.1. Pattern Definition
Patterns are defined with equals signs indicating hierarchy:
[ ==== main ==== ] # Main pattern [ === section === ] # Section header (not used for separation) [ == subsection == ] # Subsection (flexible)
All are equivalent; the number of = signs is cosmetic. The pattern name must:
- Start with a letter (a-z, A-Z)
- Contain only alphanumeric characters and underscores
- Be unique within the template file
5.2. Variable Assignment
Variables are declared and referenced with quotes:
Template Definition:
[ ' username ' ]
[ ' user.email ' ]
[ ' status | default "pending" ' ]
PHP Usage:
$tpl->set('username', 'John');
$tpl->set('user', ['email' => 'john@example.com']);
$tpl->set('status', null); // Uses default
5.3. Block vs. Inline Usage
Templatest automatically detects usage context:
Block Usage (tag alone on line with optional indentation):
<div class="content">
[ ' message ' ]
</div>
Result: Automatic indentation applied
html
<div class="content">
Rendered content here
</div>
Inline Usage (tag within line content):
<p>Hello [ ' name ' ]!</p>
Result: No indentation, whitespace trimmed
html <p>Hello John!</p>
5.4. Nested Pattern Usage (Subpatterns)
Reference patterns within patterns:
[ ==== header ==== ] <h1>[ ' title ' ]</h1> [ ==== page ==== ] [ ' header ' ] <p>[ ' content ' ]</p>
Usage:
php
$page = Templatest::read('template.tpl');
$page->set('header', ['title' => 'Welcome']); // Header pattern
$page->set('content', 'Page content'); // Page pattern var
5.5. Context Navigation
For complex hierarchies, use context helpers:
php
$tpl = Templatest::read('template.tpl');
$i = $tpl->enter('user'); // Enter user context
$tpl->set('name', 'John'); // Sets user_name
$tpl->set('profile', 'Developer'); // Sets user_profile
$tpl->leave(); // Exit context
// Equivalent to:
$tpl->set('user_name', 'John');
$tpl->set('user_profile', 'Developer');
- --
6. Tag System
6.1. Tag Components
A complete tag has the structure:
[ ' primary_action | filter1 args | filter2 args ' ]
6.2. Primary Actions
6.2.1. 1. Variable Reference
[ ' varname ' ] [ ' varname | filter1 | filter2 ' ]
6.2.2. 2. Pattern Reference
[ ' patternname ' ] # Call pattern with its own variables [ ' varname patternname ' ] # Assign pattern to variable
6.2.3. 3. Pull Action
[ ' action: arg1 arg2 ' ]
Registers a callback that will be invoked during rendering.
6.2.4. 4. Dot Notation (Sugar)
[ ' user.name ' ] → [ ' user | .name ' ]
Automatically converted to index filter application.
6.3. Pipe System
Pipes connect filters in sequence:
[ ' value | filter1 arg1 | filter2 arg2 | filter3 ' ]
Each filter's output becomes the next filter's input.
- --
7. Filters and Processing
7.1. Filter Execution
Filters are applied in order during the assign() phase:
1. Input value 2. Apply filter1(value, arg1) → intermediate 3. Apply filter2(intermediate, arg2) → result 4. Apply filter3(result) → final 5. Apply default escaping 6. Return final value
7.2. Built-in Filters
7.2.1. String Transformation
php filter_lower($value) // Lowercase filter_upper($value) // Uppercase filter_trim($value, $character_mask) // Trim whitespace filter_replace(args...) // Multiple replacements filter_regex($value, $re, $to, $limit, $strict) // Regex replacement
7.2.2. Formatting
php filter_format($value, $fmt) // sprintf() filter_date($value, $fmt) // date() filter_number($decimals, $dec_pt, $thousands_sep) // number_format() filter_join($value, $glue) // implode() filter_stringify($value) // Convert to string
7.2.3. HTML/Content
php filter_nl2br($value) // Newlines to <br> filter_spaceless($value) // Collapse whitespace (preserve <pre>, <textarea>) filter_striptags($value, $allowable_tags) // strip_tags() filter_truncate($value, $limit, $ellipsis) // Truncate with ellipsis
7.2.4. Data Processing
php filter_index($value, $path) // Array/object access filter_split($value, $delimiter, $limit) // Explode/str_split filter_json_encode($value, ...options) // JSON encode filter_json_decode($value) // JSON decode filter_list($index, ...items) // Select from list filter_default($value, $default) // Provide default filter_void($value) // Return null
7.2.5. URL/Encoding
php filter_escape_also_e($value, $mode) // Escape: html, js, css, url, attr, raw filter_url_encode($value) // URL encoding (handles arrays)
7.2.6. Utility
php filter_check($value, $on) // Checkbox value/checked helper filter_checkbox($value) // Checkbox checked helper filter_select($value, $on) // Select option selected helper filter_enclose($value, $pref, $post) // Wrap with prefix/postfix filter_sp2nbsp($value) // Spaces to filter_dbg($value) // Debug output (returns value) filter_pre($value) // Suppress indentation for value
7.3. Escaping
7.3.1. Default Escaping
Values are automatically escaped based on pattern configuration:
php // At compile time .escape html // HTML escaping .escape js // JavaScript escaping // At runtime filter_escape_also_e($value, 'html') // Explicit escaping
7.3.2. Escape Modes
html - <script> → HTML entities
js - "string" → \x22string\x22 (Unicode escaping)
css - #id { → \23 id\20 \7b\20 (hex escaping)
url - spaces → %20 (percent encoding)
attr - comprehensive HTML attribute safety
raw - No escaping (use with caution)
7.4. Custom Filters
Register custom filter functions:
php
$tpl = Templatest::read('template.tpl');
// Add a custom filter
$tpl->filter('customfilter', function($value, $arg1, $arg2) {
// Process and return value
return $value;
});
// Now usable in templates
[ ' myvar | customfilter arg1 arg2 ' ]
- --
8. Advanced Features
8.1. 1. Static Optimization
Templatest identifies static content at compile time and optimizes it:
Pattern with static subpattern:
[ ==== box ==== ]
<div>[ ' content ' ]</div>
[ ==== footer ==== ]
<footer>© 2026</footer> ← Static (no variables)
Optimization:
- footer pattern marked as static
- Inlined into parent pattern during compilation
- Reduces rendering overhead
8.2. 2. Auto-Indentation
Block-level variables automatically inherit parent indentation:
Template:
<ul>
[ ' items ' ]
</ul>
Setting:
$tpl->set('items', "
<li>Item 1</li>
<li>Item 2</li>
");
Result:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
The 4-space indent is automatically applied to each line.
8.3. 3. Caching
Templates are automatically cached for performance:
php
// Cache operations
$tpl = Templatest::read('template.tpl', '/path/to/cache/dir');
// Cache features:
// - Serialized compiled template stored
// - Automatically invalidated if source changes
// - Write-bit control: chmod 000 disables caching for that file
// - CODE_VERSION check prevents version mismatch
Cache file naming:
template.tpl → template@tpl (slashes converted to @)
8.4. 4. Conditional Defaults
The default filter with static assignment:
Template:
[ ' status | default "pending" ' ]
Setting with null:
$tpl->set('status', null); # Uses "pending"
$tpl->set('status', false); # Uses "pending"
$tpl->set('status', 'active'); # Uses "active"
8.5. 5. Pattern Cloning
Access different patterns through magic methods:
php
$main_tpl = Templatest::read('template.tpl'); // Main pattern
$header = $main_tpl->header(); // Clone with header pattern
$footer = $main_tpl->footer(); // Clone with footer pattern
// Each is independently renderable
echo $header->set('title', 'Welcome');
echo $footer->set('year', 2026);
- --
9. Developer Guide
9.1. Loading and Initialization
php
// Basic loading
$tpl = Templatest::read('/path/to/template.tpl');
// With caching
$tpl = Templatest::read('/path/to/template.tpl', '/path/to/cache');
// Clone for specific pattern
$header = $tpl->header();
$page = $tpl->page();
9.2. Setting Data
php
// Simple variable
$tpl->set('name', 'John');
$tpl->set('age', 30);
// Array (will use default filter)
$tpl->set('items', ['Apple', 'Banana', 'Orange']);
// Nested variables (underscore syntax)
$tpl->set('user_name', 'John');
$tpl->set('user_email', 'john@example.com');
// Equivalent with context
$i = $tpl->enter('user');
$tpl->set('name', 'John');
$tpl->set('email', 'john@example.com');
$tpl->leave();
// Array-style setting
$tpl->set(['user' => ['name' => 'John', 'email' => 'john@example.com']]);
// Multiple values
$tpl->set('header', 'Welcome', 'content', 'Body text', 'footer', 'Footer');
9.3. Adding Pull Functions
php
// Define a pull handler
$tpl->pull('csrf', function($is_block, $location) {
return htmlspecialchars($_SESSION['csrf_token']);
});
$tpl->pull('i18n', function($is_block, $location, $key, $lang = 'en') {
return get_translation($key, $lang);
});
// Usage in template:
// [ ' csrf: ' ]
// [ ' i18n: "welcome.message" "fr" ' ]
9.4. Adding Custom Filters
php
// Single filter
$tpl->filter('md5', function($value) {
return md5($value);
});
// Filter with parameters
$tpl->filter('truncate_custom', function($value, $length, $suffix = '...') {
return substr($value, 0, $length) . $suffix;
});
// Usage in template:
// [ ' email | md5 ' ]
// [ ' description | truncate_custom 50 "..." ' ]
9.5. Character Encoding
php
// Set encoding (defaults to UTF-8)
$tpl->setEncoding('utf-8');
$tpl->setEncoding('iso-8859-1');
// Get current encoding
$encoding = $tpl->getEncoding();
// Supported encodings (per htmlspecialchars):
// iso-8859-1, iso-8859-5, iso-8859-15
// cp866, cp1251, cp1252
// koi8-r, koi8-ru
// big5, gb2312
// shift_jis, euc-jp, macroman, etc.
9.6. Rendering
php // Direct rendering (uses __toString) echo $tpl; // Explicit render echo (string) $tpl; // Get as string $html = $tpl->__toString();
9.7. Error Handling
Errors are triggered with E_USER_WARNING:
php
// Configure error handler
set_error_handler(function($errno, $errstr) {
if (strpos($errstr, 'templatest') !== false) {
// Handle template error
error_log($errstr);
}
});
// Template errors include:
// - Unknown filters
// - Invalid tags
// - Undefined variables
// - Pattern redefinition
// - File not found
- --
10. Examples
10.1. Example 1: Basic Layout
Template File (layout.tpl):
[ ==== layout ==== ]
<!DOCTYPE html>
<html>
<head>
<title>[ ' title ' ]</title>
</head>
<body>
<header>
[ ' header ' ]
</header>
<main>
[ ' content ' ]
</main>
<footer>
[ ' footer ' ]
</footer>
</body>
</html>
[ ==== simple_header ==== ]
<h1>[ ' h1_text ' ]</h1>
[ ==== simple_footer ==== ]
<p>© [ ' year | default "2026" ' ]</p>
PHP Code:
php
$tpl = Templatest::read('layout.tpl', '/cache');
$tpl->set('title', 'My Website');
$tpl->set('header', 'simple_header');
$tpl->set('h1_text', 'Welcome!');
$tpl->set('content', '<p>Main content here</p>');
$tpl->set('footer', 'simple_footer');
$tpl->set('year', 2026);
echo $tpl;
10.2. Example 2: Form with CSRF Protection
Template File (form.tpl):
.escape html
[ ==== form ==== ]
<form method="POST">
[ ' csrf: ' ]
[ ' form_fields ' ]
<button type="submit">[ ' button_text | default "Submit" ' ]</button>
</form>
[ ==== text_field ==== ]
<div class="form-group">
<label>[ ' label ' ]</label>
<input type="text" name="[ ' name ' ]" value="[ ' value | default "" ' ]">
</div>
[ ==== textarea_field ==== ]
<div class="form-group">
<label>[ ' label ' ]</label>
<textarea name="[ ' name ' ]">[ ' value | default "" ' ]</textarea>
</div>
PHP Code:
php
$tpl = Templatest::read('form.tpl', '/cache');
// Add CSRF pull handler
$tpl->pull('csrf', function($is_block, $location) {
$token = htmlspecialchars($_SESSION['csrf_token']);
return '<input type="hidden" name="csrf_token" value="' . $token . '">';
});
// Build form fields
$fields_html = '';
// Text field
$field = $tpl->text_field();
$field->set('label', 'Email');
$field->set('name', 'email');
$field->set('value', 'user@example.com');
$fields_html .= $field;
// Textarea field
$field = $tpl->textarea_field();
$field->set('label', 'Message');
$field->set('name', 'message');
$fields_html .= $field;
$tpl->set('form_fields', $fields_html);
$tpl->set('button_text', 'Send');
echo $tpl;
10.3. Example 3: Data List with Filters
Template File (list.tpl):
[ ==== product_list ==== ]
<ul class="products">
[ ' items ' ]
</ul>
[ ==== product_item ==== ]
<li class="product">
<h3>[ ' name | upper ' ]</h3>
<p>[ ' description | truncate 100 ' ]</p>
<span class="price">[ ' price | number 2 "," "." ' ]</span>
<span class="status">[ ' active | select 1 ' ]Status: In Stock</span>
</li>
PHP Code:
php
$tpl = Templatest::read('list.tpl', '/cache');
$items_html = '';
$products = [
['name' => 'laptop', 'description' => 'High-performance computing device...', 'price' => 999.99, 'active' => 1],
['name' => 'mouse', 'description' => 'Wireless mouse for productivity...', 'price' => 29.99, 'active' => 1],
['name' => 'keyboard', 'description' => 'Mechanical keyboard...', 'price' => 149.99, 'active' => 0],
];
foreach ($products as $product) {
$item = $tpl->product_item();
$item->set('name', $product['name']);
$item->set('description', $product['description']);
$item->set('price', $product['price']);
$item->set('active', $product['active']);
$items_html .= $item;
}
$tpl->set('items', $items_html);
echo $tpl;
10.4. Example 4: Internationalization with Pull
Template File (i18n.tpl):
[ ==== page ==== ] <h1>[ ' title: "page.title" ' ]</h1> <p>[ ' description: "page.description" ' ]</p> <button>[ ' button: "actions.submit" ' ]</button>
PHP Code:
php
$translations = [
'en' => [
'page.title' => 'Welcome',
'page.description' => 'This is a test page',
'actions.submit' => 'Submit',
],
'fr' => [
'page.title' => 'Bienvenue',
'page.description' => 'Ceci est une page de test',
'actions.submit' => 'Soumettre',
],
];
$current_lang = 'fr';
$tpl = Templatest::read('i18n.tpl', '/cache');
$tpl->pull('title', function($is_block, $location, $key) use ($translations, $current_lang) {
return htmlspecialchars($translations[$current_lang][$key] ?? $key);
});
$tpl->pull('description', function($is_block, $location, $key) use ($translations, $current_lang) {
return htmlspecialchars($translations[$current_lang][$key] ?? $key);
});
$tpl->pull('button', function($is_block, $location, $key) use ($translations, $current_lang) {
return htmlspecialchars($translations[$current_lang][$key] ?? $key);
});
echo $tpl;
- --
11. Performance Considerations
11.1. Caching
- Always enable caching in production:
Templatest::read($path, $cache_dir) - Cache invalidation is automatic when source files change
- Disable caching per-file:
chmod 000 template.tpl
11.2. Static Optimization
The engine automatically optimizes:
- Static patterns are inlined
- Non-dynamic content is compiled into chunks
- Reduces runtime interpretation
11.3. Large Templates
- Break templates into included files (max 5 levels)
- Use patterns for reusable components
- Profile with
filter_dbgif needed - --
12. Troubleshooting
12.1. Template Not Found
Error: template file /path/to/template.tpl not found
Solution: Verify file path and readable permissions
12.2. No Main Template Found
Error: no main template found in /path/to/template.tpl
Solution: Define at least one pattern with [ ==== name ==== ]
12.3. Unknown Filter
Warning: unknown filter 'myfilter' at /path/to/template.tpl:15
Solution: Register filter with $tpl->filter('myfilter', $callback)
12.4. Too Deep Template Inclusion
Error: too deep template inclusion
Solution: Maximum inclusion depth is 5 levels. Reorganize template includes.
12.5. Pattern Redefinition
Error: attempt of redefining pattern 'name' at /path/to/template.tpl:10
Solution: Pattern names must be unique within a file.
- --
13. Summary
Templatest provides a powerful, efficient template engine optimized for WackoWiki. By understanding:
- Patterns as reusable components
- Variables for dynamic content
- Filters for data transformation
- Pull/Push duality for flexible rendering
- Caching for performance
Developers can create maintainable, high-performance templates with strong security defaults (HTML escaping) and extensibility through custom filters and pull handlers.