nicmart/string-template
nicmart/string-template is a fast, lightweight PHP library for rendering templates with a clean, elegant syntax. It supports nested placeholders, variable transformations, custom renderers, and reusable engines—ideal for emails, notifications, and dynamic text generation.
Install via Composer:
composer require nicmart/string-template
Pin to ^0.1.3 — it's the first tag that runs clean on PHP 8.1/8.2 (see Gotchas). The public surface is tiny: a StringTemplate value object that you construct from a string, then call render() on with an associative array of substitutions.
use Nicmart\StringTemplate\StringTemplate;
$template = new StringTemplate('Hello {name}, your order #{order_id} ships {ship_date}.');
echo $template->render([
'name' => 'Ada',
'order_id' => 4096,
'ship_date' => '2024-06-01',
]);
// Hello Ada, your order #4096 ships 2024-06-01.
Look first at the StringTemplate class itself (it's small — read it end to end before you build abstractions around it) and the test suite, which is the authoritative documentation of edge cases. The first real use case is user‑facing strings that need named holes: emails, notifications, SMS bodies, log messages, and any sprintf call that has crossed the three‑argument threshold and now needs %1$s-style positional gymnastics.
Sanity-check the install on modern PHP before you build on it:
// tinker
var_dump((new \Nicmart\StringTemplate\StringTemplate('{a}'))->render(['a' => 'ok']));
// Should print "ok" with no deprecation notices in storage/logs/laravel.log
Treat templates as data, not code. Store templates in config, a translations table, or a resources/strings/ directory, and resolve the StringTemplate instance from a service or a small helper:
function render_string(string $template, array $params): string
{
return (new StringTemplate($template))->render($params);
}
Nested lookups for entity data. Instead of flattening your DTOs into user_name, user_email, pass the array and use dot access:
$template = new StringTemplate('Welcome back, {user.profile.first_name}!');
$template->render(['user' => $customer->toArray()]);
This keeps the controller clean and the template readable.
Build a MessageRenderer service for outbound comms. Centralize the pattern once and inject it wherever mail/SMS are sent:
class MessageRenderer
{
public function __construct(private StringTemplate $template) {}
public function render(array $context): string
{
return $this->template->render($context + $this->defaults());
}
}
Pre‑merge defaults, don't fight the template. If a placeholder may be missing, supply it from defaults before calling render():
$params = array_merge(['name' => 'there', 'order_id' => '—'], $params);
Cache the parsed template, not just the string. A StringTemplate instance can be rendered repeatedly with different parameters — instantiate it once per request (or cache the instance in a container keyed by the template string) rather than re-parsing on every call.
Compose with translation lookups. Typical i18n flow: __('emails.welcome') → returns the template string → feed into StringTemplate::render() with the user data. This avoids inventing a second translation syntax.
^0.1.3 on PHP 8.1/8.2. v0.1.3 is a maintenance release whose only change is silencing deprecation notices caused by type changes in PHP 8.1 and 8.2 (PR #19, first-time contributor). No API changes, no new features, no breaking changes — render() behaves identically. If you're on 0.1.2 you'll be emitting deprecation noise into storage/logs/laravel.log on every render, which is exactly the kind of thing that masks a real warning later. Bump the constraint and run composer update nicmart/string-template.render() returns exactly what you put in. If the output goes to a browser, run it through e()/htmlspecialchars() at the output layer, or you will ship an injection bug. This is a text templating engine, not an escaper.{key}, or be replaced with an empty string — then wrap render() in your own helper that enforces that policy, so behaviour is uniform across the app.{ / } (JSON snippets, CSS, some log lines) will be misinterpreted. Escape by doubling, or — for anything with more than a stray brace — store the sensitive fragment in a parameter and interpolate it as {blob}.{user.name} works against array keys. You cannot call {user->getName()}; convert objects with toArray()/json_decode(json_encode($obj), true) first.null, booleans, and arrays will produce surprising output. Normalize scalars at the call site.StringTemplate construction out of the loop.if, it's not a string template anymore — use Blade, Twig, or a match in PHP that selects the template string.render() wrapper thin enough to swap the engine out without touching call sites.{name|upper}), escaping modes, or strict‑missing‑key behaviour is a thin subclass or decorator around StringTemplate in your app, not a fork of the package.How can I help you explore Laravel packages today?