OUR SITES NetworkRADIUS FreeRADIUS

The foreach Statement

Syntax
foreach [<key-type> <key-name>,] [<value-type>] <value-name> (<reference>) {
    [ statements ]
}

The foreach statement loops over a set of attributes as given by <attribute-reference>. The loop can be exited early by using the break keyword.

There is no limit to how many foreach statements can be nested.

<key-type>

In conjunction with <key-name>, an optional data type for the key or index variable. The data type should be numeric (e.g. uint32) for a <reference> which is a dynamic expansion. The data type should be string for a <reference> which is an attribute reference.

<key-name>

The name of the local variable which is used as the name of key when iterating over the attributes.

For numerical data types, the key value starts off at zero (0), and increases by one every round through the loop. For string data types the key value is the full path to the current attribute.

The <key-type> and <key-name> are optional, and can be omitted.

<value-type>

An optional data type for the <value-name> local variable. When looping over attributes, the data type can be omitted. The data type of the local variable is then taken from the attribute reference.

<value-name>

The name of the local variable which is used as the name of value when iterating over the attributes.

The local variable is created automatically when the foreach loop is entered, and is deleted automatically when the foreach loop exits.

The <value-name> can be modified during the course of the foreach loop. Modifications to the variable are copied back to the referenced attribute when the loop is done. See below for an example.

The only limitation on the <value-name> is that it must be unique.

<reference>

An attribute reference which will will be looped over. The reference can be to one attribute, to an array, a child, or be a subset of attributes.

Alternatively, the <reference> can be a dynamic expansion function, such as %sql("SELECT …​"). When the reference is a dynamic expansion function, a <value-type> must be specified.

Modifying Loop variables

When the <reference> is an attribute, the attribute being looped over can sometimes be modified. When the <reference> is a dynamic expansion, the results cannot be modified, and are discarded when the foreach loop is finished. If it is necessary to save the results, they should be placed into another attribute.

An attribute which is a "leaf" data type (e.g. uint32, and not tlv) will be automatically copied back to the original attribute at the end of each iteration of the foreach loop. That is, the original attribute will still exist, and will be unmodified, during the execution of the loop.

Example of modifying values
&Tmp-Integer-0 := { 1, 3, 5, 11 }

foreach self (&Tmp-Integer-0) {
	&self += 19
}

Once the loop has finished , the &Tmp-Integer-0 attribute will have the following set of values.

&Tmp-Integer-0 := { 20, 22, 24, 30 }
Pseudocode for variable modification
loop over each i in attribute[0..n]
    copy attribute[i] to the key variable, or cast the attribute to the destination type

    run loop body

    if data type of attribute matches the data type of the key
        copy the key variable back to attribute[i]

Keys

Using a key variable allows the loop to determine exactly which attribute is being modified. Or for dynamic expansions, which of 0..n values are being examined.

For attributes, the <key-type> must be string. For dynamic expansions, it must be a numerical type such as uint32.

Key variable with attribute
string total
&Tmp-Integer-0 := { 1, 3, 5, 11 }

foreach string ref, uint32 self (Tmp-Integer-0) {
	total += ref
	total += " = "
	total += (string) self
	ttoal += ", "
}

When the loop is finished, the total variable will have the following value:

"Tmp-Integer-0[0] = 1, "Tmp-Integer-0[1] = 3, "Tmp-Integer-0[2] = 5, "Tmp-Integer-0[3] = 11, "

A dynamic expansion can use a keyed index. If the SELECT statement below returns a list of "a", "b", "c", "d". then we have the following example:

Key variable with expansion
string total

foreach uint32 index, string data (%sql("SELECT ...") {
	total += (string) index
	total += ":"
	total += data
	ttoal += ", "
}

When the loop is finished, the total variable will have the following value:

"0:a, 1:b, 2:c, 3:d, "

Structural Data Types

It is possible to loop over the children of a structural data type, as given in the example below. Since the loop is over the child (i.e. leaf) attributes, the values are copied back.

In this example, we have to explicitly give a data type string. The data type is needed because there may be multiple children of the TLV-Thing attribute, and the children may not all have the same data type.

Example of Looping over children of a structural type.
foreach string child (&TLV-Thing.[*]) {
	&out += &child
	&out += " "
}

When using foreach to loop over multiple structural data types, the values can be examined, but cannot be changed. This is a limitation of the current interpreter, and may be changed in the future.

Example of Looping over children of a structural type.
foreach thing (&Tmp-TLV-0[*]) {
	&out += &thing.c
	&out += " "
}

This example can read the child attribute c, but cannot modify it.