Syntax
5 min read
The results on this page are shown exactly as the variables engine prints them.
Blocks
A block starts with {{ and ends with the matching }}. Spaces and line breaks inside the braces are ignored, so {{glb.name}} and {{ glb.name }} are the same. Text outside the braces is kept exactly as written, including its spaces.
Template | Result |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
A text without {{ is returned untouched and costs nothing.
Grammar
expression := ternary ( '|' filter )*
ternary := or ( '?' or ':' or )?
or := and ( '||' and )*
and := comparison ( '&&' comparison )*
comparison := primary ( ( '==' | '!=' | '>' | '<' | '>=' | '<=' ) primary )?
primary := literal | '(' expression ')' | path | function-call
path := namespace '.' segment ( '.' segment )*
function := 'fn' '.' name '(' ( expression ( ',' expression )* )? ')'
filter := name ( '(' ( expression ( ',' expression )* )? ')' )?Pipes have the lowest precedence, so a ? b : c | upper uppercases the whole ternary result, and (a | upper) == b needs the parentheses.
Literals
Kind | Examples | Notes |
|---|---|---|
String |
| Either quote. Escapes: |
Integer |
|
|
Decimal |
| A dot followed by digits. No exponent form. |
Boolean |
|
|
Null |
| Prints nothing. |
There are no negative number literals. - is not a valid character, so {{ fn.abs(-5) }} and {{ fn.substr('abcdef', -2) }} render empty. Pass the number in from a field instead.
Paths
namespace.segment.segment. The first part is the namespace; what the remaining segments mean depends on the namespace.
{{ glb.phone }}
{{ fg.products.price }}
{{ fg.products.details.size }}
{{ pseo.src.cities.name.2 }}- Names are made of letters, digits and underscores and must start with a letter or underscore. Segments after the first may also be plain integers (
name.2). - Namespace, filter and function names are not case sensitive:
{{ GLB.name }},{{ FN.UPPER('a') }}and{{ 'a' | UPPER }}work. The keys you pass as segments are matched as written. - An unknown namespace, an unknown key or an empty value renders nothing.
- A path may not contain a nested block:
{{ glb.{{ 'name' }} }}renders empty.
To fix an old key, see Troubleshooting.
Filters
A filter transforms the value on its left. Chain as many as you like with |. Arguments go in parentheses; with no arguments the parentheses are optional.
{{ glb.name | upper }} ACME PLUMBING
{{ glb.name | upper() }} ACME PLUMBING
{{ glb.name | lower | slug }} acme-plumbing
{{ glb.price | number(2) }} 1,234.57
{{ glb.price | number(1, ',', '.') }} 1.234,6
{{ glb.tags | split(',') | join(' / ') }} red / green / blueArguments are full expressions ({{ 'abc' | truncate(1 > 0 ? 2 : 1) }} prints ab...). A filter that does not exist passes the value through unchanged ({{ 'a' | nosuchfilter }} prints a). The list is on Filters and functions.
Function calls
Functions are called through the fn namespace: {{ fn.name(arguments) }}. The aliases func, fonk and function are identical to fn. Only these four names call functions; wp.title(1) renders empty. An unknown function renders empty.
{{ fn.concat('a', 'b', 1) }} ab1
{{ fn.if(src.n > 5, 'big', 'small') }} big
{{ fn.coalesce('', null, 'c') }} cOperators
Operator | Meaning | Result |
|---|---|---|
| Equal, not equal (PHP loose comparison) |
|
| Order comparison |
|
| Both truthy |
|
| The left side if it is truthy, otherwise the right side (not a boolean) |
|
| Ternary: condition, value if truthy, value if not |
|
| Grouping |
|
Things that are not in the language:
Not available | Result of trying | Use instead |
|---|---|---|
|
|
|
|
| the |
Chained comparison ( | Only the first comparison is read |
|
Chained ternary ( | Only the first ternary is read |
|
A filter inside a ternary branch ( | Renders empty | Wrap in parentheses or filter the whole result |
Loops, variables, assignments | none | Compute the value in PHP and read it with |
Truthiness follows PHP: an empty string, 0, "0", null, false and an empty array are false.
How a result is printed
Value | Printed as |
|---|---|
String | The string, unescaped |
Number | The number ( |
|
|
| nothing |
List | Items joined with a comma and a space ( |
The text is not HTML-escaped. If a value contains <b>, so does the output. Where the output goes decides the escaping (field output functions escape for you; see Security). To escape inside an expression use the escape, esc_attr or esc_url filters.
Nested blocks
A block can sit inside the arguments of a function or filter. The inner block is resolved first, up to 5 levels deep, and its result is passed to the outer expression as data:
{{ fn.upper({{ glb.name }}) }} ACME PLUMBING
{{ fn.concat('Hi ', '{{ glb.name }}') }} Hi Acme Plumbing
{{ fn.concat('a', {{ glb.price }}) }} a1234.5678Rules:
- A result that is a plain number (
123,1.5) stays a number. Anything else becomes a quoted string. - Inside a string literal, the result is escaped for that quote, so a value cannot close the string and add code. If
glb.badcontainsa') }} {{ fn.option('blogname') }} (', then{{ fn.upper({{ glb.bad }}) }}prints that text in capitals and runs nothing. - Nesting deeper than 5 levels renders empty.
Limits and errors
Limit | Value | What happens past it |
|---|---|---|
Length of one block | 1000 characters | The block renders empty |
Nesting of | 5 levels | Renders empty |
Depth of parentheses and arguments | 32 | Renders empty |
| 100 repeats, 10,000 characters | The result is cut at the cap |
Decimals in | 10 | Clamped to 10 |
Anything that cannot be parsed or evaluated renders as an empty string:
Mistake | Result |
|---|---|
| empty |
| empty (unexpected character) |
| empty (a filter name is missing) |
|
|
|
|
With WP_DEBUG on, each failure is written to the PHP error log (to wp-content/debug.log when WP_DEBUG_LOG is on) as:
[wpmcf] Variable resolution failed — "{{ @ }}" — Unexpected character: @
[wpmcf] Variable resolution failed — "{{ aaaa… }}" — Expression is too long.Nothing is shown to visitors or editors. See Troubleshooting.
Still stuck? Contact support