Download the PHP package maurice2k/zenplate without Composer
On this page you can find all versions of the php package maurice2k/zenplate. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download maurice2k/zenplate
More information about maurice2k/zenplate
Files in maurice2k/zenplate
Package zenplate
Short Description Simple and fast PHP based template engine
License LGPL-3.0
Informations about the package zenplate
Zenplate
A small, fast PHP template engine — meant for things like emails, simple config files, and one-shot text rendering. Templates compile to plain PHP, which is then evaluated with the assigned variables in scope.
- Tiny surface area (two classes:
Compiler,Runner) - No dependencies beyond PHP 8.1+
- Variable substitution with deep dot/bracket access
if/elseif/else//ifblocks- A small whitelist of safe functions inside
if(strlen,strtoupper,strtolower) - Custom delimiters
- Compile-once / run-many for cached templates
Install
Requires PHP ^8.1.
Quick start
Assign many variables at once with an associative array:
Variables
Variables start with %. They're wrapped in the configured delimiters
(default { / }).
Simple
Dot access (nested arrays / objects)
Dot access nests arbitrarily deep — missing keys at any level render as the empty string, no warnings:
Bracket access
Mixed dot + bracket
Empty / falsy values
Variables are rendered through PHP's empty(). That means all of these
render as the empty string:
| Value | Output |
|---|---|
null |
'' |
'' |
'' |
0 (int) |
'' |
'0' (str) |
'' |
false |
'' |
[] |
'' |
This is intentional historical behavior — handy for templates that print
a default for "missing-ish" values, surprising if you actually wanted to
print 0. If you need 0 to render, pass '0 ' or convert upstream.
Conditionals
elseif chains and else clauses work as expected:
else is optional:
Operators
Allowed inside {if ...}:
- Comparison:
==,===,!=,!==,<>,<,>,<=,>= - Logical:
&&,||, unary! - Arithmetic / bitwise:
+,-,*,/,%,&,|,^,<<,>> - Grouping:
( ... )
A single = in a comparison context is treated as == but reported as
an error (so the template still compiles to something sensible while the
typo is surfaced via Compiler::getErrors()).
Functions inside if
Only an explicit whitelist is callable from inside an if condition:
strlenstrtoupperstrtolower
Extend the list per Compiler instance:
Note: Runner::run() constructs its own internal Compiler, so to use
custom functions or delimiters with the high-level API you'll currently
need to compile separately and use Runner::runCompiled() (see below).
Custom delimiters
Compile once, run many
Compilation is regex-driven and not free; if you render the same template
repeatedly, compile it once and feed the cached PHP into runCompiled():
runCompiled() rejects input that doesn't carry the Zenplate header so a
random PHP file can't be smuggled through.
Error handling
Runner::run() throws ExecuteException on either compile errors or
runtime PHP errors:
For finer-grained inspection, drive the compiler directly:
Compiler::ERROR_* constants identify each error kind (ERROR_IF_NO_ENDIF,
ERROR_IF_SINGLE_EQUAL_SIGN, etc.).
Safety notes
The compiler emits PHP source that gets eval()ed by Runner::run(),
so injection prevention is critical. Current defenses:
- Literal
<?escaping —<?xml,<?php,<?=and lone<?sequences are wrapped so they pass through as inert text. - String literals are normalized — quoted strings inside bracket
subscripts (
{%foo["key"]}) and if-conditions ({if "x" == "y"}) are decoded and re-emitted as single-quoted PHP literals. This blocks PHP's${expr}and{$expr}interpolation syntax, which would otherwise allow arbitrary expression evaluation including function calls. - Variable / selector names are constrained to
[a-zA-Z0-9_]+— no path for injection via identifiers. iffunction whitelist — only names in$supportedIfFuncs(defaultstrlen,strtoupper,strtolower) can appear in conditions; anything else is a compile error.runCompiled()header check — rejects PHP files that don't carry the Zenplate header so a random PHP blob can't be smuggled in.
The injection probes in tests/SecurityProbeTest.php exercise the
known attack surface.
Note on string literals: double-quoted strings used as array keys or in if-conditions are decoded with C-style escape rules (
\n→ newline,\\→\,\"→", etc.) at compile time, then re-emitted safely. Your template's{%foo["a\"b"]}still resolves to the array keya"b. Single-quoted strings retain PHP's stricter escape rules (only\\and\'are special).
Even with these defenses in place, treat compiled templates as code:
don't compile attacker-controlled template source unless you've
threat-modelled what they could read from the eval scope ($this->vars,
in particular).
Limitations
- No modifier syntax yet (
{%foo|upper},{%foo|default:"x"}). The integration points are noted inCompiler::__construct. PRs welcome. - No
foreach/ loop blocks. Runner::run()always builds its ownCompiler, so custom delimiters or extendedsupportedIfFuncsneed the compile-once path.
Testing
Test infrastructure (Dockerfile, compose file, phpunit config, fixtures,
test cases) lives under tests/. Everything runs in Docker so there's
nothing to install on the host. A Makefile at the repo root wraps the
common commands:
If you'd rather not use Make, the equivalent docker compose invocation is:
CI runs the same image across PHP 8.1 / 8.2 / 8.3 / 8.4 / 8.5 on every
push and PR (see .github/workflows/ci.yml).
License
LGPL-3.0. See lgpl-3.0.txt.