Creating user-defined functions

You can use tags or CFScript to create a UDF. Each technique has advantages and disadvantages.

Creating functions using CFScript

You use the function statement to define the function in CFScript. CFScript function definitions have the following features and limitations:

The following is a CFScript definition for a function that returns a power of 2:

<cfscript>
function twoPower(exponent)
{
    return 2^exponent; 
}
</cfscript>

For more information on how to use CFScript to define a function, see "Defining functions in CFScript".

Creating functions using tags

You use the cffunction tag to define a UDF in CFML. The cffunction tag syntax has the following features and limitations:

The following code uses the cffunction tag to define the exponentiation function:

<cffunction name="twoPower" output=True>
  <cfargument name="exponent">
  <cfreturn 2^exponent>
</cffunction>

For more information on how to use the cffunction tag to define a function, see "Defining functions using the cffunction tag".

Rules for function definitions

The following rules apply to functions that you define using CFScript or the cffunction tag:

You can define a function in the following places:

For recommendations on selecting where you define functions, see the sections "Using Application.cfm and function include files" and "Specifying the scope of a 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:

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 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. To test whether an array index contains data, copy the array element to a simple variable and use the IsDefined function to test the existence of the copy.

Using the Arguments scope as an array

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

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 shown in the following example:

<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:

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.

Function-only variables

In addition to the Arguments scope, each function can have a number of variables that exist only inside the function, and are not saved between times the function gets called. As soon as the function exits, all the variables in this scope are removed.

In CFScript, you create function-only variables with the var statement. Unlike other variables, you never prefix function-only variables with a scope name.

For more information on using function-only variables, see "Using function-only variables".

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>

Defining functions in CFScript

You define functions using CFScript in a manner similar to defining JavaScript functions. You can define multiple functions in a single CFScript block.

Note:   For more information on using CFScript, see Chapter 6, "Extending ColdFusion Pages with CFML Scripting".

CFScript function definition syntax

A CFScript function definition has the following syntax.

function functionName( [argName1[, argName2...]] )
{
  CFScript Statements
}

The following table describes the function variables:
Function variable
Description
functionName
The name of the function. You cannot use the name of a standard ColdFusion function or any name that starts with "cf". You cannot use the same name for two different function definitions. Function names cannot include periods.
argName1...
Names of the arguments required by the function. The number of arguments passed into the function must equal or exceed the number of arguments in the parentheses at the start of the function definition. If the calling page omits any of the required arguments, ColdFusion generates a mismatched argument count error.

The body of the function definition must consist of one or more valid CFScript statements. The body must be in curly braces, even if it is a single statement.

The following two statements are allowed only in function definitions:
Statement
Description
var variableName = expression;
Creates and initializes a variable that is local to the function (function variable). This variable has meaning only inside the function and is not saved between calls to the function. It has precedence in the function body over any variables with the same name that exist in any other scopes. You never prefix a function variable with a scope identifier, and the name cannot include periods. The initial value of the variable is the result of evaluating the expression. The expression can be any valid ColdFusion expression, including a constant or even another UDF.
All var statements must be at the top of the function declaration, before any other statements. You must initialize all variables when you declare them. You cannot use the same name for a function variable and an argument.
Each var statement can initialize only one variable.
You should use the var statement to initialize all function-only variables, including loop counters and temporary variables.
return expression;
Evaluates expression (which can be a variable), returns its value to the page that called the function, and exits the function. You can return any ColdFusion variable type.

A simple CFScript example

The following example function adds the two arguments and returns the result:

<cfscript>
function Sum(a,b)
{      
    var sum = a + b;  
    return sum; 
} 
</cfscript>

In this example, a single line declares the function variable and uses an expression to set it to the value to be returned. This function can be simplified so that it does not use a function variable, as follows:

function MySum(a,b) {Return a + b;} 

You must always use curly braces around the function definition body, even if it is a single statement.

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".

Defining functions using the cffunction tag

The cffunction and cfargument tags let you define functions in CFML without using CFScript.

Note:   This chapter describes how to use the cffunction tag to define a function that is not part of a ColdFusion component. For information on ColdFusion components, see Chapter 11, "Building and Using ColdFusion Components". For more information on the cffunction tag, see CFML Reference.

The cffunction tag function definition format

A cffunction tag function definition has the following format:

<cffunction name="functionName" [returnType="type" roles="roleList" 
    access="accessType" output="Boolean"]>
  <cfargument name="argumentName" [Type="type" required="Boolean"
    default="defaultValue">]
  .
  .
  Function body code
  .
  .
  <cfreturn expression>
</cffunction>

where square brackets ([]) indicate optional arguments. You can have any number of cfargument tags.

The cffunction tag specifies the name you use when you call the function. You can optionally specify other function characteristics, as described in the following table:
Attribute
Description
name
The function name.
returnType
(Optional) The type of data that the function returns. The valid standard type names are: any, array, binary, boolean, date, guid, numeric, query, string, struct, uuid, variableName and void. If you specify any other name ColdFusion requires the argument to be a ColdFusion component with that name.
ColdFusion throws an error if you specify this attribute and the function tries to return data with a type that ColdFusion cannot automatically convert to the one you specified. For example, if the function returns the result of a numeric calculation, a returnType attribute of string or numeric is valid, but array is not.
roles
(Optional) A comma-delimited list of security roles that can invoke this method. If you omit this attribute, ColdFusion does not restrict user access to the function.
If you use this attribute, the function executes only if the current user is logged in using the cfloginuser tag and is a member of one or more of the roles specified in the attribute. Otherwise, ColdFusion throws an unauthorized access exception. For more information on user security, see Chapter 16, "Securing Applications".
output
(Optional) Determines how ColdFusion processes displayable output in the function body.
If you do not specify this option, ColdFusion treats the body of the function as normal CFML. As a result, text and the result of any cfoutput tags in the function definition body are displayed each time the function executes.
If you specify True or "yes", the body of the function is processed as if it were in a cfoutput tag. ColdFusion displays variable values and expression results if you surround the variables and expressions with pound signs.
If you specify False or "no" the function is processed as if it were in a cfsilent tag. The function does not display any output. The code that calls the function is responsible for displaying any function results.

You must use cfargument tags for required function arguments and named optional arguments. All cfargument tags must precede any other CFML code in cffunction tag body. Therefore, put the cfargument tags immediately following the cffunction opening tag. The cfargument tag takes the following attributes:
Attribute
Description
name
The argument name.
type
(Optional) The data type of the argument. The type of data that is passed to the function. The valid standard type names are any, array, binary, boolean, date, guid, numeric, query, string, struct, uuid, and variableName. If you specify any other name, ColdFusion requires the argument to be a ColdFusion component with that name.
ColdFusion throws an error if you specify this attribute and the function is called with data of a type that ColdFusion cannot automatically convert to the one you specified. For example, if the argument type attribute is numeric, you cannot call the function with an array.
required
(Optional) A Boolean value specifying whether the argument is required, If set to True and the argument is omitted from the function call, ColdFusion throws an error. The default if False.
Because you do not identify arguments when you call a function, all cfargument tags that specify required arguments must precede any cfargument tags that specify optional arguments in the cffunction definition.
default
(Optional) The default value for an optional argument if no argument value is passed.
If you specify this attribute, an error occurs if you specify this attribute and set the required attribute to True.

Note:   The cfargument tag is not required for optional arguments. This feature is useful if a functions can take an indeterminate number of arguments. If you do not use the cfargument tag for an optional argument, reference it using its position in the Arguments scope array. For more information see "Using the Arguments scope as an array".

Using a CFML tag in a user-defined function

The most important advantage of using the cffunction tag over defining a function in CFScript is that you can include CFML tags in the function. Thus, UDFs can encapsulate activities, such as database lookups, that require ColdFusion tags. Also, you can use the cfoutput tag to display output on the calling page with minimal coding.

The following example function looks up and returns an employee's department ID. It takes one argument, the employee ID, and looks up the corresponding department ID in the CompanyInfo Employee table:

<cffunction name="getDeptID" >
  <cfargument name="empID" required="true" type="numeric">
  <cfquery dataSource="CompanyInfo" name="deptID">
    SELECT Dept_ID
    FROM Employee
    WHERE Emp_ID = #empID#
  </cfquery>
  <cfreturn deptID.Dept_ID>
</cffunction>

Note:   The cfquery tag automatically puts the query result in the Variables scope, so you cannot limit its result to the This 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".

Comments