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_attr or attr – 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 &nbsp;
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       - &lt;script&gt; → 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>&copy; [ ' 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_dbg if 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:

  1. Patterns as reusable components
  2. Variables for dynamic content
  3. Filters for data transformation
  4. Pull/Push duality for flexible rendering
  5. Caching for performance

Developers can create maintainable, high-performance templates with strong security defaults (HTML escaping) and extensibility through custom filters and pull handlers.