Search by

toropyga / templates

Toropyga

A secure PHP 8.1+ template compiler and renderer

Package info

github.com/Toropyga/Templates

pkg:composer/toropyga/templates

Statistics

Installs: 337

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v6.1.0 2026-09-21 14:16 UTC

This package is auto-updated.

Last update: 2026-09-21 14:17:53 UTC


README

An HTML template compiler and renderer for PHP 8.1+.

License Version PHP CI PHPStan

The CI badge tracks GitHub Actions runs on the Toropyga/Templates repository and only lights up once .github/workflows/ci.yml (included in this package) is pushed there and has run at least once. The PHPStan badge is static: it reflects that phpstan.neon (also included) analyses src/ at level: max with zero errors as of this package's build, not a live, continuously re-checked status — re-run composer stan after any change to confirm it still holds.

Contents

Requirements

  • PHP ^8.1
  • PHP DOM extension (ext-dom)

Installation

Install the package with Composer:

composer require toropyga/templates

Then load Composer's autoloader:

require_once __DIR__.'/vendor/autoload.php';

Quick Start

The recommended API uses an explicit configuration object:

use Toropyga\TemplateConfig;
use Toropyga\Templates;

$templates = Templates::fromConfig(new TemplateConfig(
    rootPath: __DIR__,
    templatePath: 'templates',
    style: 'default',
    cachePath: 'var/cache/templates'
));

$templates->assign(['user_name' => 'Alex']);

echo $templates->render('hello.html');

With templates/default/hello.html:

Hello, {$user_name}!

The result is:

Hello, Alex!

render() returns the rendered string. The compatibility method output() can either print the template or return it when called with true:

$html = $templates->output('hello.html', true);
$templates->output('hello.html');

Template Syntax

Variables

Use {$name} for a value and array notation for nested values:

Hello, {$user_name}!
Title: {$page['title']}

Values are escaped for HTML by default. Use |raw only for trusted HTML:

{$trusted_html|raw}

You can change the escaping policy explicitly:

$templates->setEscapeHtml(false);

Includes

Include another template with:

{tmplinclude: partials/navigation.html}

Template paths are restricted to the configured style directory. Absolute paths, .. traversal and symlink escapes are rejected.

Blocks

To render only a marked block:

<!-- tmplblock: begin -->
The selected block
<!-- tmplblock: end -->

PHP in Templates

Execution of PHP from tmplphp and tmpltag is disabled by default. This is recommended for untrusted or user-editable templates. Legacy templates can opt in explicitly:

$templates = new Templates('templates', 'default', 'cache', true);

Only enable this mode when all templates are trusted application code.

Errors

Errors are reported through Toropyga\TemplateException and its specialized subclasses:

  • TemplatePathException
  • TemplateCompilationException
  • TemplateCacheException
use Toropyga\TemplateException;

try {
    echo $templates->render('hello.html');
} catch (TemplateException $error) {
    // Handle the error in the application.
}

The legacy non-throwing mode remains available during migration:

$templates->setThrowExceptions(false);
$result = $templates->output('missing.html');
$errors = $templates->getErrors();

Cache

Compiled templates are written to the configured cache directory. Each cache entry has a manifest containing the compiler version, source path and SHA-256 source hash. The cache is invalidated when the source or compiler changes.

Clear the cache explicitly with:

$templates->clearCache();

Migrating Legacy Projects

Complete the following steps before upgrading an existing project:

  1. Make sure the project runs on PHP ^8.1 and has the DOM extension enabled.

  2. Update the package with Composer and review the configured template, style and cache directories.

  3. Search templates for tmplphp and tmpltag. They are disabled by default in version 6.0.0. Enable them explicitly only when every affected template is trusted application code:

    $templates->setAllowPhp(true);
  4. Review every variable that is expected to contain HTML. Values are escaped by default now. Mark only trusted values with |raw, or temporarily call $templates->setEscapeHtml(false) during migration.

  5. Add handling for Toropyga\TemplateException. The old debug-mode exit behavior has been replaced with exceptions. A temporary non-throwing mode is available through $templates->setThrowExceptions(false).

  6. If the application relies on $_SESSION['style'], explicitly enable $templates->use_session = true and ensure sessions are available.

  7. Review template filenames and includes. Absolute paths, .. traversal, symlink escapes and unreadable files are rejected.

  8. Clear the compiled-template cache after upgrading:

    $templates->clearCache();
  9. Manually verify representative pages, especially pages using custom HTML, PHP template blocks, includes, sessions and raw HTML values.

The legacy constructor and method names remain available, but version 6.0.0 does not guarantee identical output for unsafe PHP templates, unescaped HTML, invalid paths or non-standard HTML markup.

Legacy Configuration

The constructor and legacy constants remain supported for existing projects:

$templates = new Templates('templates', 'default', 'cache');
$templates->assign(['page' => $page]);
$templates->output('page.html');

Mapping Legacy Constants

Legacy constants can be passed explicitly to TemplateConfig:

$config = new Toropyga\TemplateConfig(
    rootPath: defined('ROOT_PATH') ? (string) constant('ROOT_PATH') : __DIR__,
    templatePath: defined('TMPL_DIR') ? (string) constant('TMPL_DIR') : 'templates',
    style: defined('TMPL_STYLE') ? (string) constant('TMPL_STYLE') : 'default',
    cachePath: defined('TMPL_CACHE') ? (string) constant('TMPL_CACHE') : 'cache',
    sitePath: defined('SITE_PATH') ? (string) constant('SITE_PATH') : ''
);

$templates = Toropyga\Templates::fromConfig($config);

The mapping is:

Legacy constant TemplateConfig property
ROOT_PATH rootPath
TMPL_DIR templatePath
TMPL_STYLE style
TMPL_CACHE cachePath
SITE_PATH sitePath

Templates::fromConfig() does not read global constants automatically. This keeps modern instances isolated and allows different project roots in one process. The legacy constructor continues to read the constants automatically.

The preferred long-term approach is TemplateConfig, because it avoids global configuration, keeps the configured root and site path local to the instance, and makes filesystem paths explicit. Modern instances can safely use different roots in the same process.

Inline CSS resources are also rewritten by the DOM asset compiler:

<div style="background-image: url(images/background.png)"></div>

Testing

The distributed package includes two dependency-free regression scripts (no PHPUnit required):

php tests/smoke.php         # compilation pipeline: vars, |raw, includes,
                             # blocks, asset rewriting, allow_php guard
php tests/legacy_smoke.php  # legacy constructor + multi-instance behavior
php tests/legacy_isolation.php # legacy DOCUMENT_ROOT-based instance isolation
php tests/cache_smoke.php   # asset-method consolidation + cache hash reuse

Both exit 0 and print OK (<n> assertions) on success, or exit 1 with the first failing assertion on stderr.

Static analysis (PHPStan, level: max) is configured in phpstan.neon:

composer install   # pulls in phpstan/phpstan as a dev dependency
composer stan       # or: vendor/bin/phpstan analyse

.github/workflows/ci.yml runs both the regression suite (across PHP 8.1, 8.2 and 8.3) and PHPStan on every push and pull request.

License

This project is licensed under the MIT License.

See CHANGELOG.md for release history.