dzentota/template-engine

A secure template engine with native PHP syntax and context-aware escaping

dev-main 2025-07-10 21:24 UTC

This package is auto-updated.

Last update: 2025-07-13 21:50:10 UTC


README

A secure template engine for PHP with native syntax and context-aware escaping, built on top of dzentota/template-variable and dzentota/typedvalue.

Features

🔒 Security First: Built following the AppSec Manifesto principles

  • Context-aware automatic escaping (HTML, attributes, JavaScript, CSS, URLs)
  • Protection against XSS attacks
  • Secure by default configuration
  • Template security auditing

🚀 Native PHP Syntax: Similar to bareui, uses familiar PHP syntax

  • No custom template language to learn
  • Full PHP power when needed
  • Clean, readable templates

Performance:

  • Optional template caching
  • Optimized for production use
  • Minimal overhead

🛡️ Developer Experience:

  • Comprehensive error handling
  • Debug mode for development
  • Template includes and partials
  • Global variables support
  • Namespaced template directories

Installation

composer require dzentota/template-engine

Note: This package depends on development versions of dzentota/template-variable and dzentota/typedvalue. You may need to adjust your minimum-stability setting in composer.json to "dev" or use --prefer-source flag during installation.

Quick Start

<?php
use Dzentota\TemplateEngine\TemplateEngine;

// Initialize the engine
$engine = new TemplateEngine([
    'auto_escape' => true,
    'debug' => true
]);

// Add template directory
$engine->addPath(__DIR__ . '/templates');

// Render template with data
echo $engine->render('welcome', [
    'title' => 'Hello World',
    'user' => ['name' => 'John Doe']
]);

Template (templates/welcome.php):

<!DOCTYPE html>
<html>
<head>
    <title><?= $title ?></title>
</head>
<body>
    <h1>Welcome, <?= $user['name'] ?>!</h1>
</body>
</html>

Security Features

Automatic Context-Aware Escaping

The template engine wraps all variables as TemplateVariable instances, which provide automatic escaping through magic methods:

  • <?= $variable ?> - Uses __toString() for HTML context escaping
  • <?= $variable('attr') ?> - Uses __invoke('attr') for attribute context escaping
  • <?= $variable('js') ?> - Uses __invoke('js') for JavaScript context escaping

The template engine automatically escapes variables based on their output context:

<!-- HTML Context (default via __toString magic method) -->
<p><?= $userInput ?></p>

<!-- Attribute Context -->
<input value="<?= $userInput('attr') ?>">

<!-- JavaScript Context -->
<script>var data = <?= $userInput('js') ?>;</script>

<!-- CSS Context -->
<style>.class { color: <?= $userInput('css') ?>; }</style>

<!-- URL Context -->
<a href="<?= $userInput('url') ?>">Link</a>

<!-- Raw Output (use with extreme caution) -->
<div><?= $trustedContent('raw') ?></div>

XSS Protection

All variables are automatically escaped unless explicitly marked as raw:

$data = [
    'safe_text' => 'Hello World',
    'unsafe_html' => '<script>alert("XSS")</script>'
];

// This is safe - script tags will be escaped
echo $engine->render('template', $data);

Template:

<p><?= $unsafe_html ?></p>
<!-- Output: &lt;script&gt;alert("XSS")&lt;/script&gt; -->

Template Syntax

Variables

<!-- Escaped output (automatic via __toString) -->
<?= $variable ?>

<!-- Different contexts -->
<?= $variable('attr') ?>  <!-- For HTML attributes -->
<?= $variable('js') ?>    <!-- For JavaScript -->
<?= $variable('css') ?>   <!-- For CSS values -->
<?= $variable('url') ?>   <!-- For URLs -->
<?= $variable('raw') ?>   <!-- Raw/unescaped (use with caution) -->

Control Structures

Use native PHP syntax:

<!-- Conditionals -->
<?php if ($user['is_admin']): ?>
    <p>Admin panel available</p>
<?php endif; ?>

<!-- Loops -->
<?php foreach ($items as $item): ?>
    <div><?= $item['name'] ?></div>
<?php endforeach; ?>

<!-- Switch statements -->
<?php switch ($user['role']): ?>
    <?php case 'admin': ?>
        <p>Administrator</p>
        <?php break; ?>
    <?php case 'user': ?>
        <p>Regular user</p>
        <?php break; ?>
<?php endswitch; ?>

Template Includes

<!-- Include other templates -->
<?= $include('partials/header', ['title' => 'Page Title']) ?>

<!-- Content here -->

<?= $include('partials/footer') ?>

Global Variables

// Set global variables
$engine->addGlobal('app_name', 'My App');
$engine->addGlobal('version', '1.0.0');

// Use in templates
<p><?= $app_name ?> v<?= $version ?></p>

Configuration

$engine = new TemplateEngine([
    'auto_escape' => true,           // Enable automatic escaping
    'strict_variables' => true,      // Throw errors for undefined variables
    'debug' => false,                // Enable debug mode
    'cache' => true,                 // Enable template caching
    'cache_dir' => '/tmp/templates', // Cache directory
    'default_context' => 'html',     // Default escaping context
    'file_extension' => '.php'       // Template file extension
]);

Template Directories

Single Directory

$engine->addPath('/path/to/templates');

Multiple Namespaced Directories

$engine->addPath('/path/to/app/templates', 'app');
$engine->addPath('/path/to/admin/templates', 'admin');

// Use namespaced templates
echo $engine->render('@admin/dashboard');
echo $engine->render('@app/welcome');

Security Auditing

The template engine includes security auditing capabilities:

use Dzentota\TemplateEngine\Security\SecurityManager;

$security = new SecurityManager();

// Audit a template
$templateSource = file_get_contents('template.php');
$audit = $security->auditTemplate($templateSource, 'template.php');

echo "Security Score: " . $audit['security_score'] . "/100\n";

foreach ($audit['issues'] as $issue) {
    echo "- " . $issue['message'] . " (Severity: " . $issue['severity'] . ")\n";
}

Advanced Usage

Custom Security Configuration

use Dzentota\TemplateEngine\Security\SecurityManager;

$security = new SecurityManager([
    'strict_mode' => true,
    'allow_php_functions' => false,
    'max_template_size' => 1024 * 1024
]);

$engine = new TemplateEngine(['security_manager' => $security]);

Content Security Policy

$security = new SecurityManager();
$headers = $security->generateCSPHeaders();

foreach ($headers as $name => $value) {
    header("$name: $value");
}

Template Metadata

$template = $engine->load('welcome');
$metadata = $template->getMetadata();

echo "Template: " . $metadata['name'] . "\n";
echo "Size: " . $metadata['size'] . " bytes\n";
echo "Modified: " . date('Y-m-d H:i:s', $metadata['modified']) . "\n";

Error Handling

try {
    $output = $engine->render('template', $data);
    echo $output;
} catch (RuntimeException $e) {
    // Template not found, rendering error, etc.
    error_log("Template error: " . $e->getMessage());
    echo "Template error occurred";
} catch (InvalidArgumentException $e) {
    // Invalid configuration, variable names, etc.
    error_log("Configuration error: " . $e->getMessage());
}

Best Practices

1. Use TemplateVariable for Automatic Escaping

<!-- ✅ Good - automatic escaping via TemplateVariable -->
<p><?= $userInput ?></p>

<!-- ❌ Bad - raw PHP variable (bypasses TemplateVariable) -->
<p><?= $_GET['user_input'] ?></p>

2. Use Context-Appropriate Escaping

<!-- ✅ Good -->
<input value="<?= $value('attr') ?>">
<script>var data = <?= $data('js') ?>;</script>

<!-- ❌ Bad -->
<input value="<?= $value ?>">
<script>var data = "<?= $data ?>";</script>

3. Validate Template Security

// In development, audit your templates
if ($developmentMode) {
    $security = new SecurityManager();
    $audit = $security->auditTemplate($templateSource, $templateName);
    
    if ($audit['security_score'] < 80) {
        throw new Exception("Template security score too low: " . $audit['security_score']);
    }
}

4. Use Template Caching in Production

$engine = new TemplateEngine([
    'cache' => true,
    'cache_dir' => sys_get_temp_dir() . '/template_cache'
]);

Examples

See the examples/ directory for complete working examples:

  • basic_usage.php - Basic template rendering
  • templates/welcome.php - Comprehensive template with security features
  • templates/partials/ - Template includes and partials

Security

This template engine is designed with security as a primary concern:

  • XSS Prevention: All output is escaped by default
  • Context Awareness: Different escaping for HTML, attributes, JS, CSS, URLs
  • Directory Traversal Protection: Templates cannot access files outside designated directories
  • Function Restrictions: Dangerous PHP functions are blocked in templates
  • Security Auditing: Built-in tools to audit template security

Requirements

  • PHP 8.1 or higher
  • dzentota/template-variable (dev-main)
  • dzentota/typedvalue (dev-master)

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Support

For issues and questions: