Adobe ColdFusion 8

Working with arguments and variables in functions

Good argument naming practice

An argument's name should represent its use. For example, the following code is unlikely to result in confusion:

<cfscript>
    function SumN(Addend1,Addend2)
    { return Addend1 + Addend2; }
</cfscript>
<cfset x = 10>
<cfset y = 12>
<cfoutput>#SumN(x,y)#</cfoutput>

The following, similar code is more likely to result in programming errors:

<cfscript>
    function SumN(x,y)
    { return x + y; }
</cfscript>
<cfset x = 10>
<cfset y = 12>
<cfoutput>#SumN(x,y)#<cfoutput>

Passing arguments

ColdFusion passes the following data types to the function by value:

  • Integers
  • Real numbers
  • Strings (including lists)
  • Date-time objects
  • Arrays

As a result, any changes that you make in the function to these arguments do not affect the variable that was used to call the function, even if the calling code is on the same ColdFusion page as the function definition.

ColdFusion passes queries, structures, and external objects such as COM objects into the function by reference. As a result, any changes to these arguments in the function also change the value of the variable in the calling code.

For an example of the effects of passing arguments, see Passing complex data.

Passing complex data

Structures, queries, and complex objects such as COM objects are passed to UDFs by reference, so the function uses the same copy of the data as the caller. Arrays are passed to user-defined functions by value, so the function gets a new copy of the array data and the array in the calling page is unchanged by the function. As a result, you must handle arrays differently from all other complex data types.

Passing structures, queries, and objects

For your function to modify the caller's copy of a structure, query, or object, you must pass the variable as an argument. Because the function gets a reference to the caller's structure, the caller variable reflects all changes in the function. You do not have to return the structure to the caller. After the function returns, the calling page accesses the changed data by using the structure variable that it passed to the function.

If you do not want a function to modify the caller's copy of a structure, query, or object, use the Duplicate function to make a copy and pass the copy to the function.

Passing arrays

If you want your function to modify the caller's copy of the array, the simplest solution is to pass the array to the function and return the changed array to the caller in the function return statement. In the caller, use the same variable name in the function argument and return variable.

The following example shows how to directly pass and return arrays. In this example, the doubleOneDArray function doubles the value of each element in a one-dimensional array.

<cfscript>
//Initialize some variables
//This creates a simple array.
a=ArrayNew(1);
a[1]=2;
a[2]=22;
//Define the function.
function doubleOneDArray(OneDArray) {
    var i = 0;
    for ( i = 1; i LE arrayLen(OneDArray); i = i + 1)
        { OneDArray[i] = OneDArray[i] * 2; }
    return OneDArray;
}
//Call the function.
a = doubleOneDArray(a);
</cfscript>
<cfdump var="#a#">

This solution is simple, but it is not always optimal:

  • This technique requires ColdFusion to copy the entire array twice, once when you call the function and once when the function returns. This is inefficient for large arrays and can reduce performance, particularly if the function is called frequently.
  • You can use the return value for other purposes, such as a status variable.

If you do not use the return statement to return the array to the caller, you can pass the array as an element in a structure and change the array values inside the structure. Then the calling page can access the changed data by using the structure variable it passed to the UDF.

The following code shows how to rewrite the previous example using an array in a structure. It returns True as a status indicator to the calling page and uses the structure to pass the array data back to the calling page.

<cfscript>
//Initialize some variables.
//This creates an simple array as an element in a structure.
arrayStruct=StructNew();
arrayStruct.Array=ArrayNew(1);
arrayStruct.Array[1]=2;
arrayStruct.Array[2]=22;
//Define the function.
function doubleOneDArrayS(OneDArrayStruct) {
    var i = 0;
    for ( i = 1; i LE arrayLen(OneDArrayStruct.Array); i = i + 1)
        { OneDArrayStruct.Array[i] = OneDArrayStruct.Array[i] * 2; }
    return True;
}
//Call the function.
Status = doubleOneDArrayS(arrayStruct);
WriteOutput("Status: " & Status);
</cfscript>
</br>
<cfdump var="#arrayStruct#">

You must use the same structure element name for the array (in this case Array) in the calling page and the function.

About the Arguments scope

All function arguments exist in their own scope, the Arguments scope.

The Arguments scope exists for the life of a function call. When the function returns, the scope and its variables are destroyed.

However, destroying the Argument scope does not destroy variables, such as structures or query objects, that ColdFusion passes to the function by reference. The variables on the calling page that you use as function arguments continue to exist; if the function changes the argument value, the variable in the calling page reflects the changed value.

The Arguments scope is special, in that you can treat the scope as either an array or a structure. This dual nature of the Arguments scope is useful because it makes it easy to use arguments in any of the following circumstances:

  • You define the function using CFScript.
  • You define the function using the cffunction tag.
  • You pass arguments using argument name=value format.
  • You pass arguments as values only.
  • The function takes optional, undeclared arguments.

The following sections describe the general rules for using the Arguments scope as an array and a structure. For more information on using the Arguments scope in functions defined using CFScript, see Using the Arguments scope in CFScript. For more information on using the Arguments scope in functions defined using the cffunction tag, see Using the Arguments scope in cffunction definitions.

The contents of the Arguments scope

The following rules apply to the Arguments scope and its contents:

  • The scope contains all the arguments passed into a function.
  • If you use cffunction to define the function, the scope always contains an entry "slot" for each declared argument, even if you do not pass the argument to the function when you call it. If you do not pass a declared (optional) argument, the scope entry for that argument is empty.

    When you call a function that you defined using CFScript, you must pass the function a value for each argument declared in the function definition. Therefore, the Arguments scope for a CFScript call does not have empty slots.

The following example shows these rules. Assume that you have a function declared, as follows:

<cffunction name="TestFunction">
    <cfargument name="Arg1">
    <cfargument name="Arg2">
</cffunction>    

You can call this function with a single argument, as in the following line:

<cfset TestFunction(1)>

The resulting Arguments scope looks like the following:

As an array

As a structure

Entry

Value

Entry

Value

1

1

Arg1

1

2

undefined

Arg2

undefined

In this example, the following functions return the value 2 because there are two defined arguments:

ArrayLen(Arguments)
StructCount(Arguments)

However, the following tests return the value false, because the contents of the second element in the Arguments scope is undefined.

Isdefined("Arguments.Arg2")
testArg2 = Arguments[2]>
Isdefined("testArg2")

Note: The IsDefined function does not test the existence of array elements. Instead, put any code that might try to access an undefined array element in a try block and use a catch block to handle exceptions that arise if elements do not exist.

Using the Arguments scope as an array

The following rules apply to referencing Arguments scope as an array:

  • If you call the function using unnamed arguments, the array index is the position of the argument in the function call.
  • If you use names to pass the arguments, the array indexes correspond to the order in which the arguments are declared in the function definition.
  • If you use names to pass arguments, and do not pass all the arguments defined in the function, the Arguments array has an empty entry at the index corresponding to the argument that was not passed. This rule applies only to functions created using the cffunction tag.
  • If you use a name to pass an optional argument that is not declared in the function definition, the array index of the argument is the sum of the following:
    1. The number of arguments defined with names in the function.
    2. The position of the optional argument among the arguments passed in that do not have names defined in the function.

    However, using argument names in this manner is not good programming practice because you cannot ensure that you always use the same optional argument names when calling the function.

To demonstrate these rules, define a simple function that displays the contents of its Arguments array and call the function with various argument combinations, as the following example shows:

<cffunction name="TestFunction" >
    <cfargument name="Arg1">
    <cfargument name="Arg2">
    <cfloop index="i" from="1" to="#ArrayLen(Arguments)#">
        <cfoutput>Argument #i#: #Arguments[i]#<br></cfoutput>
    </cfloop>
</cffunction>    

<strong>One Unnamed argument</strong><br>
<cfset TestFunction(1)>
<strong>Two Unnamed arguments</strong><br>
<cfset TestFunction(1, 2)>
<strong>Three Unnamed arguments</strong><br>
<cfset TestFunction(1, 2, 3)>
<strong>Arg1:</strong><br>
<cfset TestFunction(Arg1=8)>
<strong>Arg2:</strong><br>
<cfset TestFunction(Arg2=9)>
<strong>Arg1=8, Arg2=9:</strong><br>
<cfset TestFunction(Arg1=8, Arg2=9)>
<strong>Arg2=6, Arg1=7</strong><br>
<cfset TestFunction(Arg2=6, Arg1=7)>
<strong>Arg1=8, Arg2=9, Arg3=10:</strong><br>
<cfset TestFunction(Arg1=8, Arg2=9, Arg3=10)>
<strong>Arg2=6, Arg3=99, Arg1=7</strong><br>
<cfset TestFunction(Arg2=6, Arg3=99, Arg1=7)>

Note: Although you can use the Arguments scope as an array, the IsArray(Arguments) function always returns false and the cfdump tag displays the scope as a structure.

Using the Arguments scope as a structure

The following rule applies when referencing Arguments scope as a structure:

  • Use the argument names as structure keys. For example, if your function definition includes a Principal argument, refer to the argument as Arguments.Principal.

The following rules are also true, but avoid writing code that uses them. To ensure program clarity, only use the Arguments structure for arguments that you name in the function definition. Use the Arguments scope as an array for optional arguments that you do not declare in the function definition.

  • If the function can take unnamed optional arguments, use array notation to reference the unnamed arguments. For example, if the function declaration includes two named arguments and you call the function with three arguments, refer to the third argument as Arguments[3]. To determine if an unnamed optional argument exists, use the StructKeyExists function; for example, structKeyExists(Arguments,"3").
  • If you do not name an optional argument in the function definition, but do use a name for it in the function call, use the name specified in the function call For example, if you have an unnamed optional argument and call the function using the name myOptArg for the argument, you can refer to the argument as Arguments.myOptArg in the function body. This usage, however, is poor programming practice, as it makes the function definition contents depend on variable names in the code that calls the function.

Using the Arguments scope in CFScript

A function can have optional arguments that you do not have to specify when you call the function. To determine the number of arguments passed to the function, use the following function:

ArrayLen(Arguments)

When you define a function using CFScript, the function must use the Arguments scope to retrieve the optional arguments. For example, the following SumN function adds two or more numbers together. It requires two arguments and supports any number of additional optional arguments. You can refer to the first two, required, arguments as Arg1 and Arg2 or as Arguments[1] and Arguments[2]. You must refer to the third, fourth, and any additional optional arguments as Arguments[3], Arguments[4], and so on.

function SumN(Arg1,Arg2) {
    var arg_count = ArrayLen(Arguments);
    var sum = 0;
    var i = 0;
    for( i = 1 ; i LTE arg_count; i = i + 1 )
    {
        sum = sum + Arguments[i];
    }
    return sum; 
} 

With this function, any of the following function calls are valid:

SumN(Value1, Value2)
SumN(Value1, Value2, Value3)
SumN(Value1, Value2, Value3, Value4)

and so on.

The code never uses the Arg1 and Arg2 argument variables directly, because their values are always the first two elements in the Arguments array and it is simpler to step through the array. Specifying Arg1 and Arg2 in the function definition ensures that ColdFusion generates an error if you pass the function one or no arguments.

Note: Avoid referring to a required argument in the body of a function by both the argument name and its place in the Arguments scope array or structure, as this can be confusing and makes it easier to introduce errors.

For more information on the Arguments scope, see About the Arguments scope.

Using the Arguments scope in cffunction definitions

When you define a function using the cffunction tag, you generally refer to the arguments directly by name if all arguments are named in the cfargument tags. If you do use the Arguments scope identifier, follow the rules listed in About the Arguments scope.