Expressions

The sequence of operator precedence is outlined from the lowest-precedence operators to the highest: ?: (ternary operator), b-and, b-xor, b-or, or, and, ==, !=, <=>, <, >, >=, <=, in, matches, starts with, ends with, .., +, -, ~, *, /, //, %, is (tests), **, ??, | (filters), [], and ..

{% set greeting = 'Hello ' %}
{% set name = 'World' %}

{{ greeting ~ name|lower }}   {# Hello World #}

{# use parenthesis to change precedence #}
{{ (greeting ~ name)|lower }} {# hello World #}

Literals

Literals are the most straightforward form of expressions. Literals are representations for types such as strings, numbers, and arrays. Here are the existing literals:

  • String: "Hello World" - Any content between two double or single quotes is considered a string. Strings are essential when a template requires a text value, whether as function arguments, filters, or for template extension or inclusion. A delimiter inside a string can exist if preceded by a backslash, as in 'It\'s good'. To escape a string containing a backslash (like 'c:\Program Files'), double the backslash, e.g., 'c:\\Program Files'.

  • Numbers: 42 / 42.23 - Writing down a number creates integers and floating-point numbers. If a dot appears, the number is a float; otherwise, it's an integer.

  • Arrays: ["foo", "bar"] - Arrays consist of a series of expressions separated by commas and enclosed in square brackets.

  • Hashes: {"foo": "bar"} - Hashes are formed by a list of keys and values separated by commas and wrapped in curly braces:

    {# keys as string #} { 'foo': 'foo', 'bar': 'bar' }
    {# keys as names (equivalent to the previous hash) #} { foo: 'foo', bar: 'bar' }
    {# keys as integer #} { 2: 'foo', 4: 'bar' }
    {# omitting keys when it matches the variable name #} { foo } {# this is the same as: #} { 'foo': foo }
    {# keys as expressions (enclose the expression in parentheses) #}
    { (foo): 'foo', (1 + 1): 'bar', (foo ~ 'b'): 'baz' } 
  • Boolean: true / false - The value true signifies true, while false denotes false.

  • Null: null - This symbolizes no specific value and is the outcome when a variable is absent. none is synonymous with null.

Arrays and hashes can also be nested:

{% set foo = [1, {"foo": "bar"}] %}

Tip: Only double-quoted strings support string interpolation.

Math

The template engine empowers you to carry out mathematical operations within templates. Below are the supported mathematical operators:

  • Addition (+): Adds two numbers together. Both operands are converted to numbers.

    {{ 1 + 1 }}  {# returns 2 #}
  • Subtraction (-): Deducts the second number from the first.

    {{ 3 - 2 }}  {# returns 1 #}
  • Division (/): Divides two numbers, producing a floating-point result.

    {{ 1 / 2 }}  {# returns 0.5 #}
  • Modulus (%): Provides the remainder from an integer division.

    {{ 11 % 7 }}  {# returns 4 #}
  • Floor Division (//): Divides two numbers and furnishes the floored integer outcome.

    {{ 20 // 7 }}  {# returns 2 #}
    {{ -20  // 7 }}  {# returns -3 #}

    This is essentially a shorthand for the round filter.

  • Multiplication (*): Multiplies the operands.

    {{ 2 * 2 }}  {# returns 4 #}
  • Power (**): Elevates the left operand by the right operand's power.

    {{ 2 ** 3 }}  {# returns 8 #}

Logic

You have the flexibility to combine multiple expressions utilizing the following operators:

  • and: Validates as true only if both the left and right operands are true.

    {% if var1 and var2 %} ... {% endif %}
  • or: Affirms as true if either the left or right operand is true.

    {% if var1 or var2 %} ... {% endif %}
  • not: Functions to negate a statement.

    {% if not var1 %} ... {% endif %}
  • (expr): Facilitates in grouping an expression.

    {% if (var1 and var2) or var3 %} ... {% endif %

Note: Bitwise operators like b-and, b-xor, and b-or are also supported

Important: Operators are case-sensitive.

Comparisons

The template engine supports a range of comparison operators in any given expression, including: ==, !=, <, >, >=, and <=.

You can evaluate if a string commences or concludes with a certain string:

{% if 'Hello' starts with 'H' %}
    ...
{% endif %}

{% if 'Hello' ends with 'o' %}
    ...
{% endif %}

For asserting that one string encompasses another, you can utilize the containment operator (explained in the following section).

Note: For intricate string comparisons, the matches operator provides the capability to apply regular expressions:

{% if phone matches '/^[\\d\\.]+$/' %}
    ...
{% endif %}

Containment Operator

The in operator assesses containment. It renders as true if the left operand is found within the right:

{# This returns true #}
{{ 1 in [1, 2, 3] }}
{{ 'cd' in 'abcde' }}

Tip: You can leverage this filter for containment verification on strings, arrays and hashes

For negated containment tests, utilize the not in operator:

{% if 1 not in [1, 2, 3] %}
    ...
{% endif %}

{# This is synonymous to #}
{% if not (1 in [1, 2, 3]) %}
    ...
{% endif %}

Test Operator

The is operator facilitates tests. These tests allow a variable to be compared against standard expressions. The test's name serves as the right operand:

{# Testing if a variable is odd #}
{{ name is odd }}

Tests can also accept arguments:

{% if variable is divisible by(3)  %}
    ...
{% endif %}

To negate tests, employ the is not operator:

{% if variable is not divisible by(3) %}
    ...
{% endif %}

{# This is synonymous to #}
{% if not (variable is divisible by(3)) %}
    ...
{% endif %}

Go to the tests page to delve deeper into the inherent tests.

Other Operators

There are several operators that don't fit neatly into other categories:

  • |: Used to apply a filter.

  • ..: Generates a sequence based on the preceding and following operand. Essentially, it's a shortcut for the range function. If you wish to combine this with the filter operator, ensure to use parentheses due to operator precedence: (1..5)|join(', ')

    {{ 1..5 }}
    
    {# equivalent to #}
    {{ range(1, 5) }}
  • ~: Transforms all operands to strings and joins them. So, {{ "Hello " ~ name ~ "!" }}, assuming name is 'John', will yield Hello John!.

  • ., []: Fetches an attribute of a variable.

  • ?:: The ternary operator:

    {{ foo ? 'yes' : 'no' }}
    {{ foo ?: 'no' }} {# This is the same as {{ foo ? foo : 'no' }} #}
    {{ foo ? 'yes' }} {# This equates to {{ foo ? 'yes' : '' }} #}
  • ??: The null-coalescing operator:

    {# Yields the value of foo if it's defined and not null, otherwise 'no' #}
    {{ foo ?? 'no' }}

String Interpolation

String interpolation (#{expression}) lets you embed any valid expression within a double-quoted string. The outcome of the evaluated expression is then inserted into the string:

{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}

When rendered, if bar is, for instance, 3, the first line will yield foo 3 baz. The second line will produce foo 3 baz since the arithmetic operation 1+21+2 is evaluated as 3.

Last updated