Guide 02 · Developer docs

Template languages: Smarty and Twig

Core dna renders server-side sites with Smarty or with Twig, one engine per site, and treats the two as peers. This guide is the one place both are documented: what they share, each engine's language and the platform's additions and limits, the two side by side, and translation.

L1–L2
Unit 02 · Smarty

The template security policy

Every Smarty template on Core dna compiles under a policy the site cannot change: 98 callable PHP functions, seven PHP functions usable as modifiers, no super-globals, and files only from the template folders. Why it exists, the complete lists, the errors it produces, and what to use for what it refuses.

Who this is for. The developer on a Smarty site whose template failed with “not allowed by security setting”, or who wants to know before writing a call whether it will compile. Afterwards you can check any function or modifier against the lists and find the allowed way to do what a refused call would have done.

What the policy is, and why

Smarty can compile almost any PHP expression into a template. The platform turns that off. A template is closer to content than to application code: some of it is typed into admin fields, by people who should not be able to run arbitrary PHP on the server, and all of it is rendered on servers that host other sites. The policy lets a template call only the PHP functions on a list and refuses everything else when the template compiles.

It is set by the platform for every site, and a theme can neither switch it off nor add to it. It decides four things:

  • which PHP functions a template may call;
  • which PHP functions may be used as modifiers, a separate and much shorter list;
  • that super-globals such as $_GET are out of reach;
  • that templates load only from the template folders.

A template that breaks the policy does not render partly. It fails to compile, and the error names the call: PHP function 'sprintf' not allowed by security setting or modifier 'trim' not allowed by security setting. Search for that text when a page suddenly fails.

The PHP functions a template may call

The list has 101 names, read from the platform's policy for the current release. Three of them cannot be called (below), so 98 work.

KindFunctions
Arrays (35)array_change_key_case, array_column, array_combine, array_diff, array_diff_key, array_filter, array_flip, array_intersect, array_intersect_key, array_key_exists, array_key_first, array_keys, array_merge, array_pop, array_rand, array_replace, array_reverse, array_shift, array_slice, array_unique, array_values, arsort, asort, count, end, explode, implode, in_array, join, krsort, ksort, max, min, shuffle, sort
Strings (21)htmlspecialchars, htmlspecialchars_decode, lcfirst, ltrim, number_format, rtrim, str_getcsv, str_pad, str_replace, strip_tags, stripslashes, strlen, strpos, strrpos, strstr, strtolower, strtoupper, substr, trim, ucfirst, ucwords
Type checks (13)empty, filter_var, gettype, intval, is_array, is_bool, is_float, is_int, is_null, is_numeric, is_object, is_string, isset
Hashing and encoding (11)base64_decode, base64_encode, bin2hex, hash, hash_hmac, http_build_query, json_decode, json_encode, openssl_random_pseudo_bytes, openssl_sign, urlencode
Regular expressions (8)preg_filter, preg_grep, preg_last_error, preg_match, preg_match_all, preg_quote, preg_replace, preg_split
Dates and time (6)date, date_create, microtime, time, timezone_offset_get, timezone_open
Numbers (3)ceil, floor, round
URLs (1)parse_url
On the list, not callable (3)__, error, split

strtolower and strtoupper mangle accented letters; Smarty's |lower and |upper handle them. Functions that sort in place, such as sort(), change the variable you pass and print their return value, so assign it: {$sorted = sort($names)}.

Modifiers: three sources, three rules

A modifier can come from three places, and the policy treats them differently:

  • Smarty's own modifiers always pass: capitalize, cat, count, count_characters, count_paragraphs, count_sentences, count_words, date_format, debug_print_var, default, escape, explode, from_charset, implode, indent, json_encode, lower, mb_wordwrap, nl2br, noprint, number_format, regex_replace, replace, round, spacify, str_repeat, string_format, strip, strip_tags, strlen, substr, to_charset, truncate, unescape, upper, wordwrap.
  • The platform's modifiers always pass: _, format, json_encode, intval, strval, base64url_decode, webix_table (What the platform adds to Smarty).
  • Any other PHP function used as a modifier passes only if it is one of seven (below), even when the same function is allowed in function form.

So a PHP helper usually works when you call it and fails when you pipe to it:

Template code Smarty only
{trim($path, '/')}                    {* compiles: trim() is on the function list *}
{$path|trim:'/'}                      {* fails: modifier 'trim' not allowed by security setting *}

{str_replace('-', ' ', $slug)}        {* compiles *}
{$slug|str_replace:'-':' '}           {* fails *}
{$slug|replace:'-':' '}               {* compiles: replace is Smarty's own modifier *}

{nl2br($text)}                        {* fails: nl2br is not on the function list *}
{$text|escape|nl2br}                  {* compiles: nl2br is one of the seven modifiers *}

When an expression that looks harmless fails to compile, this is almost always why. Either call the function, or use Smarty's modifier for the same job.

The PHP functions allowed as modifiers

count, dd, intval, json_encode, nl2br, strval, substr.

Most duplicate a Smarty or platform modifier and are harmless. dd is not: {$page|dd} dumps the value, sends a 500 status and ends the request on the spot, on production as much as on a development environment. Useful for a minute while debugging locally; never commit it.

On the list but not callable

__, error and split are on the function list but do not exist as PHP functions on the platform, so calling one fails to compile with unknown function, not with a policy error. split was removed from PHP in version 7: use explode(). The other two share their names with the platform's {__} block and {error} tag, which are what you want.

What is missing, and what to use instead

Anything that reads files, opens a network connection, runs code from a string or prints PHP's own debug output is deliberately absent. So are some everyday helpers; most have a replacement that passes the policy:

Instead ofUse
sprintf, printf{$n|string_format:'%05d'}
str_contains, str_starts_with{if strpos($path, '/docs') !== false}; === 0 for starts-with
mb_substr, mb_strlen, mb_strtolower|truncate, |count_characters, |lower, which handle multibyte text; |regex_replace for anything more specific
strtotime|date_format, which also accepts a date written as text: {'2026-09-23'|date_format:'j F Y'}. For a record's own dates, |format
array_map, array_sum, array_pushA {foreach}; {$list[] = $item} appends
array_searcharray_keys($list, $value) returns the keys holding a value
range{for $i = 1 to 5}…{/for}
md5, sha1hash('md5', $value), hash('sha1', $value)
rawurlencodeurlencode() or |escape:'url'; Smarty's |unescape:'url' decodes
print_r, var_dump{debug_vars} on a development environment
A slug from a title$_string->slugify($title)

Super-globals are off

{$smarty.get.page}, {$smarty.post}, {$smarty.server}, {$smarty.session}, {$smarty.cookies} and their siblings fail to compile with (secure mode) super globals not permitted; {$_GET} is an undefined variable and prints nothing. The request reaches a template through the platform's helpers:

You wantedUse
A query-string parameter$_request->getParameter('page')
All query-string parameters$_request->getParameters()
The path, the method, the hostgetPath(), getMethod(), getHostname(), getSchemeAndHost() on $_request
A cookie$_request->getCookie('consent', '')
Who is signed in$_currentUser
Posted form fieldsNot available to a template: a form posts to a platform controller, which handles the fields (Submissions)

The full set of helpers is on Global template variables.

Templates load only from the template folders

An {include} or {extends} may load a file only from the three template folders: the site's templates/, the platform's, and the site's virtual files. That keeps a template from reading any other file on the server, such as configuration or logs. A path that leaves those folders fails with Smarty Security: not trusted file path; a relative path in template code typed into the admin fails with cannot be relative to template of resource type 'string'.

When you need something the policy refuses

Never reach around the policy. Three legitimate routes, in the order to try them:

  1. A helper or record method that already does it. The underscore helpers cover the request, URLs, settings, site data, menus and queries; records cover their own data and relations (Response models).
  2. The browser. For data a page can load after it renders, call the API from JavaScript or a custom endpoint built as a job (Custom endpoints with synchronous jobs). Older sites used a widget ($_widgets->get('identifier')) for this; widgets are deprecated, and a template reads records with the query helper $_query instead.
  3. A platform change. Ask for the function or the record method, saying what the template needs it for. The policy is the same for every site, so a function is added for everyone or not at all.
Go deeper

Was this page helpful?

Your answer goes to the docs team.