toropyga / templates
A secure PHP 8.1+ template compiler and renderer
Requires
- php: ^8.1
- ext-dom: *
Requires (Dev)
- phpstan/phpstan: 2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
An HTML template compiler and renderer for PHP 8.1+.
The CI badge tracks GitHub Actions runs on the
Toropyga/Templatesrepository 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 thatphpstan.neon(also included) analysessrc/atlevel: maxwith zero errors as of this package's build, not a live, continuously re-checked status — re-runcomposer stanafter any change to confirm it still holds.
Contents
- Requirements
- Installation
- Quick Start
- Template Syntax
- Errors
- Cache
- Migrating Legacy Projects
- Legacy Configuration
- License
Requirements
- PHP
^8.1 - PHP
DOMextension (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:
TemplatePathExceptionTemplateCompilationExceptionTemplateCacheException
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:
-
Make sure the project runs on PHP
^8.1and has theDOMextension enabled. -
Update the package with Composer and review the configured template, style and cache directories.
-
Search templates for
tmplphpandtmpltag. 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);
-
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. -
Add handling for
Toropyga\TemplateException. The old debug-modeexitbehavior has been replaced with exceptions. A temporary non-throwing mode is available through$templates->setThrowExceptions(false). -
If the application relies on
$_SESSION['style'], explicitly enable$templates->use_session = trueand ensure sessions are available. -
Review template filenames and includes. Absolute paths,
..traversal, symlink escapes and unreadable files are rejected. -
Clear the compiled-template cache after upgrading:
$templates->clearCache();
-
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.