Troubleshooting¶
Symptoms first, causes second — scan the headings for what you're seeing.
Template edits don't show up after a deploy¶
Smarty compiles .tpl files into PHP under smarty.compile_path and, with compile_check on (the default), recompiles whenever the source file is newer than the compiled output. Two deploy patterns break that assumption:
compile_check => false(the production performance setting) skips the mtime check entirely. Compiled output is trusted until you delete it — runphp artisan smarty:clear-compiled(orsmarty:optimize --force) as a deploy step.- Deploys that rewind mtimes (rsync with
--timesfrom a build box, Docker image layers, some atomic-symlink strategies) can leave the new source file older than the compiled output, so evencompile_checksees nothing to do. Same fix: clear or force on deploy.
If you use Smarty's output cache, pair it with php artisan smarty:clear-cache — cached pages are not invalidated by template changes, only by lifetime expiry.
ReservedTemplateVariable exception¶
auth, request, session, route and errors are auto-shared wrapper objects, assigned on every render. Passing one of those names via view data (view('foo', ['session' => …]), View::share('route', …), or a composer's $view->with('auth', …)) would silently shadow the wrapper, so the engine throws instead. Rename the colliding key.
The one exception: Laravel's stock ShareErrorsFromSession middleware shares errors as a ViewErrorBag on every web request — the engine recognises that exact case and swaps in its own $errors wrapper around the same bag.
Output is escaped twice (< in the page)¶
escape_html is on by default, so don't combine it with manual escaping:
- Drop
|escapefrom templates — under auto-escaping the plain form is a no-op anyway, and|escape:'force'-style variants double-escape. - Custom plugins that return markup should either build it from escaped parts (function-plugin output is not auto-escaped) or be documented for
nofilteruse. |jsonand|markdownhandle this themselves — their output renders correctly withoutnofilter.
Raw HTML where you wanted it escaped¶
Function-plugin output bypasses the escape_html pass by design (that's how {csrf_field} can emit an <input>). The package's own plugins escape anything user-coupled ({old}, {lang}, …) — apply the same rule to plugins you write: htmlspecialchars() anything that originates from user input before returning it.
"unknown modifier" / "tag … disabled by security setting" under a policy¶
The Strict policy switches modifiers to an allow-list and bans the state-reaching tags ({config}, {service}, {session}, {dump}, {dd}). If a template legitimately needs a modifier that isn't allow-listed, subclass the policy and append to $allowed_modifiers. Also note: switching the security config does not invalidate already-compiled templates — php artisan smarty:clear-compiled after changing it.
Octane / Swoole / RoadRunner notes¶
Workers are long-lived processes, so anything static survives across requests. The package is built for that, and the guarantees are worth knowing:
- Block-state frames ({auth}'s
$user, {error}'s$message) are reset in afinallyaround every render, so an exception mid-block can't leak a binding into the next request's render. - Wrapper objects are re-assigned per render from the live container, so
$auth,$requestetc. always reflect the current request, not the worker's first one. - Plugin discovery memoises per process — after a code change to a plugin class, the file-mtime fingerprint invalidates the on-disk cache, but reload the workers (
octane:reload) like you would for any code change.
Smarty's output cache serves stale request data¶
It shouldn't: every request- or locale-coupled built-in registers nocache (see the caching row in Configuration). If a custom plugin's output is being baked into cached pages, register it with cacheable=false — for class-backed plugins, #[SmartyPlugin(type: '…', name: '…', cacheable: false)]. Note Smarty only honours the flag for function and block plugins; a custom modifier's output follows the cacheability of the expression it appears in.
A view renders the wrong template with the same filename¶
Fixed in 0.21.0 — older versions resolved templates by basename, so dashboard.tpl and admin/dashboard.tpl could shadow each other. Upgrade, then clear the compile and cache dirs (file names changed): php artisan smarty:clear-compiled && php artisan smarty:clear-cache.