Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lightncandy Laravel Package

zordius/lightncandy

LightnCandy is a fast PHP implementation of Handlebars and Mustache templates. It compiles templates into standalone PHP code, supports helpers, partials, and most Handlebars features, and works without external dependencies.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer:

composer require zordius/lightncandy

The package exposes a single facade, LightnCandy\LightnCandy. Two calls drive everything: compile() turns a template string into a PHP class/closure source, and prepare() turns that source into a callable renderer.

use LightnCandy\LightnCandy;

$php = LightnCandy::compile('Hello {{name}}!', [
    'flags' => LightnCandy::FLAG_ERROR_EXCEPTION,
]);

$render = LightnCandy::prepare($php);
echo $render(['name' => 'World']); // Hello World!

Where to look first:

  • The README flag table (FLAG_ERROR_EXCEPTION, FLAG_JS, FLAG_MUSTACHE, FLAG_RUNTIME_LAMBDA, FLAG_STANDALONEPHP, etc.) — nearly every behavior difference is a flag.
  • LightnCandy::getLog() for compile-time diagnostics.
  • The helpers, partials, basedir, fileext, runtime, and renderer options, which are the main extension points.

First real use case: rendering an HTML email or a user-editable template fragment where you want Handlebars/Mustache syntax in PHP without a JS runtime. Compile once at build/deploy time, cache the generated PHP, and call the prepared closure during requests.

Implementation Patterns

Compile-then-cache is the canonical workflow. Compilation is the expensive part; rendering is cheap. Persist the generated PHP to disk, keyed by a hash of the template source, options, and helper signature, then include and render.

$key  = hash('xxh128', $template . $optionsFingerprint);
$file = __DIR__ . "/cache/{$key}.php";
if (!is_file($file)) {
    $php = LightnCandy::compile($template, $options);
    $tmp = $file . '.' . uniqid('', true);
    file_put_contents($tmp, $php);
    rename($tmp, $file); // atomic; keep opcache warm
}
$render = require $file;
echo $render($data);

Precompile at deploy/build time. For static templates (emails, PDFs, notification bodies), run a small script or Artisan command in CI that compiles all templates into a cache directory. Never compile inside a hot request path unless you're certain about cache hit rates.

Register helpers for app-specific logic. Helpers are plain PHP callables (closures, invokables, [Class::class, 'method']) supplied in the helpers option:

'helpers' => [
    'asset'      => fn(string $path) => '/assets/' . ltrim($path, '/') . '?v=42',
    'formatDate' => fn($ts, $opts) => date($opts['hash']['fmt'] ?? 'Y-m-d', $ts),
    'raw'        => fn($html) => new \LightnCandy\SafeString($html),
],

Block helpers receive $options['fn'] (the inner section) and $options['inverse'] (the {{else}} branch), plus $options['hash'], $options['name'], and $options['context']:

'helpers' => [
    'eq' => function ($a, $b, $opts) {
        return $a === $b ? $opts['fn']() : $opts['inverse']();
    },
],
// {{#eq role "admin"}}...{{else}}...{{/eq}}

Use partials for layout and reuse. Partials can be inline strings or resolved from disk when basedir and fileext are set:

'partials' => ['header' => $headerHtml],
// or
'basedir'  => __DIR__ . '/views/',
'fileext'  => '.hbs',   // {{> header}} loads views/header.hbs

Reach for context variables deliberately. @index, @key, @first, @last, @root, and ../ parent access are all supported, with FLAG_PARENT required for parent navigation. Pass shared configuration (site URL, locale) via @root instead of injecting it into every scope.

Migrating from Mustache. Start with FLAG_MUSTACHE (or FLAG_MUSTACHELOOKUP) to preserve {{name}} semantics, then progressively enable Handlebars features. FLAG_JS emulates Handlebars.js behavior more closely; pick one and document it, because escaping and lookup rules differ.

Coexisting with Blade. Use Blade for application pages and reserve LightnCandy for content you cannot trust Blade to own: user-supplied templates, CMS fragments, or email bodies stored in the database. Wrap the compile/prepare cycle in a small service so it is mockable in tests.

Return SafeString from helpers that emit HTML. Helper output is escaped by default (same as {{ }}). Wrap trusted markup in new \LightnCandy\SafeString($html) so you don't have to use triple-stache everywhere.

Gotchas and Tips

  • Silent compile failure. By default compile() returns false on error. Always add FLAG_ERROR_EXCEPTION and/or FLAG_ERROR_LOG, and surface LightnCandy::getLog() in development. A false returned from prepare() then blows up on $render(...) with a cryptic callable error.
  • Escaping surprises. {{ }} HTML-escapes; {{{ }}} / {{& }} do not. Don't reach for FLAG_NOESCAPE to "fix" output — use SafeString on individual values. Handlebars escaping of ', `, and = differs from htmlspecialchars() defaults.
  • Runtime lambdas require a flag. If you pass closures as data values (Mustache lambdas) at render time, you need FLAG_RUNTIME_LAMBDA. Otherwise lambdas must be known at compile time, and changing them invalidates the cached PHP.
  • Helpers are baked into the cache. The helpers and partials arrays influence generated code. Include them in your cache key (e.g., hash helper names plus a version constant) or helpers silently won't update after a deploy.
  • Partials resolve at compile time when file-backed. With basedir/fileext, editing header.hbs does not affect an already-cached compiled template. Either recompile on file mtime or keep partials inline.
  • ../ needs FLAG_PARENT. Parent context navigation and @root behavior both depend on flags; missing them produces empty output rather than errors.
  • Compiled output is PHP source, not a result. Store it as a .php file and include/require it — never echo the string. Compiled files may reference runtime classes unless you enable FLAG_STANDALONEPHP.
  • Whitespace control. Use {{~ ... ~}} to trim surrounding whitespace; this is the usual culprit behind stray blank lines in email HTML.
  • Debugging tip. When output looks wrong, dump the generated $php and read it — helper calls, escaping, and context lookups are all visible as plain PHP and usually make the bug obvious.
  • Package is stable but dormant (last release 2021). Pin the version in composer.json, and test against your target PHP version — PHP 8.x works for typical use but expect deprecation notices if you lean on lambdas or reflection-heavy helpers.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json