Accessibility
 
Home / Developer Center / ColdFusion MX Application Developer Center /

ColdFusion Article

Icon or Spacer Icon or Spacer Icon or Spacer
Ray Camden
 
 
Writing user-defined functions in ColdFusion MX


For those of you who develop, or use, user-defined functions (UDFs) in Macromedia ColdFusion 5, you will be happy to know that ColdFusion MX has some very exciting changes in store for you. Don't worry, your existing UDFs will work just fine; in fact, you can continue to write or use UDFs just as you have in the past. ColdFusion MX introduces changes to UDFs that make them not only easier to use, but far more powerful as well.

Writing UDFs in ColdFusion 5
In case you're wondering what this is all about, let me quickly review how you write UDFs in ColdFusion 5 (which is identical to how you can write them in ColdFusion MX). Listing 1 shows a simple example of a UDF that prints the "Hello World!" message.

Download the components Download (2K) the sample code for this tutorial.

See listing1.cfm in the sample code:


Listing 1

<cfscript>
function helloWorld() {
   var greeting = "Hello World!";
   return greeting;
}
</cfscript>

<cfoutput>#helloWorld()#</cfoutput>


All UDFs in ColdFusion 5 must be written in CFScript, so your code example must begin with the cfscript tag. Begin the UDF declaration with the "function" keyword. This is followed by the name of the UDF—in this case, helloWorld. Next comes a set of parentheses. If your UDF accepts any arguments, you enumerate them here. Like an "if" statement, the body of your UDF appears within the set of curly brackets. The body of my UDF above only contains two lines. The first line uses the "var" keyword to declare a local variable, called greeting. This variable is simply set to the string "Hello World!". Next, you use the "return" keyword to return the string. Essentially all I did above was create a string (the welcome message) and return it to the caller.

After creating a UDF, you call it just like any other built-in function. (Some developers refer to a built-in function as a "BIF" or a "normal function" that is part of the ColdFusion server.) You simply take the name of your UDF and surround it with cfoutput tags and pound (#) signs. In the case above, this calls my UDF and displays the "Hello World!" message onscreen when run in the browser.

There are a few salient points about UDFs in ColdFusion 5 that I want to point out. First, as I stated above, you must use CFScript to write UDFs in ColdFusion 5. While CFScript has a good syntax, especially if you are used to scripting languages, for those ColdFusion developers who don't feel comfortable with this type of coding environment, it may be a bit hard to work with CFScript.

Secondly, CFScript can only contain expressions, conditionals and functions. In other words, you can only do things like this:


x = 2;
y = x+2;
z = len("This is a string");
if(x gte 1) {
   writeOutput("Hello");
}


You can't call ColdFusion tags from CFScript. This means you can't perform a database call, use CFHTTP, or any other ColdFusion tag. Of course, this also means you can't do these kinds of operations with UDFs. (There are workarounds, like using Java or COM objects, but they aren't nearly as easy to use as the native ColdFusion tag equivalents.) It would certainly be nice if ColdFusion MX offered you an alternative! It just so happens that it does.

Writing UDFs in ColdFusion MX
As I said earlier, writing UDFs in ColdFusion MX is much easier than it is in ColdFusion 5. The cffunction tag allows you to write your UDFs using tag syntax. Before getting into the details of the tag, let's just take a quick look at a sample UDF. Listing 2 shows the code from Listing 1 rewritten using tag syntax.


See listing2.cfm in the sample code:


<cffunction name="helloWorld">
   <cfset greeting = "Hello World!">
   <cfreturn greeting>
</cffunction>

<cfoutput>#helloWorld()#</cfoutput>


Even though the above code sample contains two tags you've never seen before, you can probably take guess how they work. The new cffunction tag defines a function. You have a beginning and closing tag that wraps the body of the UDF. The cfreturn tag returns a value. (Rocket science, right?) Once you have declared the UDF with your cffunction tags, you can call it like any "old style" UDF.

Listing 3 shows you a slightly more advanced UDF.


See listing3.cfm in the sample code:


<cffunction name="httpGet">
   <cfargument name="urlToGet">
   <cfhttp url="#urlToGet#">
   <cfreturn cfhttp.filecontent>
</cffunction>

<cfset macromedia = httpGet("http://www.macromedia.com")>
<cfoutput>#htmlCodeFormat(macromedia)#</cfoutput>


This UDF, called httpGet, performs an HTTP GET on a URL. Pass it any URL and it retrieves the source of the web page found at that location. All I've done is simply wrap up a call to the cfhttp  tag, but as you can see I've made it easier to use, as well as making it possible to use the cfhttp tag from CFScript if I choose to. I also introduced another new tag in this listing, called the cfargument tag, which lets you define what arguments a UDF will take. The cfargument tag takes various attributes. You can specify what the type should be by using the type attribute. You can also specify whether the argument is required or not.

One nice thing about UDFs written with the cffunction tag compared to CFScript is that required and optional arguments are much easier to work with. Let's look at another comparison. Listing 4 shows a simple UDF that uses both required and optional arguments.


See listing4.cfm in the sample code:


<cfscript>
function highLight(str,word) {
   var front = "<span style=""background-color: yellow;"">";
   var back = "</span>";
   var matchCase = false;
   if(ArrayLen(arguments) GTE 3) front = arguments[3];
   if(ArrayLen(arguments) GTE 4) back = arguments[4];
   if(ArrayLen(arguments) GTE 5) matchCase = arguments[5];
   if(NOT matchCase) return REReplaceNoCase(str,"(#word#)"
,"#front#\1#back#","ALL"); else return REReplace(str,"(#word#)","#front#\1#back#","ALL"); } </CFSCRIPT> <cfsavecontent variable="test"> This is a test string. If it contained important information, I'd have to make sure I use the highlight function. As it stands, the information is rather trivial. </cfsavecontent> <cfoutput>#highLight(test,"information")#</cfoutput>

This UDF takes a string and applies a highlight to matching words. For example, if you were displaying text that resulted from a search, you might want to use this UDF to highlight the words the user searched for. Notice that that UDF only declares two arguments, str and word. These are the required arguments. For the optional arguments, you must check the length of the Arguments array. This UDF takes three optional arguments, so it performs three different checks. All in all, there are six lines of code to handle processing of the arguments. At first glance, you may not realize that this UDF takes a total of five arguments. Because it's not obvious, that's one reason to use UDFDoc formatting.


For more information on UDFs and UDFDoc, check out the ColdFusion Common Function Library Project (CFLib.org), which contains libraries of UDFs for ColdFusion 5. For this article, I have removed the UDFDoc header for the sake of brevity. They aren't required, except for documentation purposes.

Let's take a look at this same UDF written in the cffunction tag, as shown in Listing 5. I think the differences, and the ease of reading, will be quick to see.


See listing5.cfm in the sample code:


<cffunction name="highLight" returnType="string">
   <cfargument name="str" type="string" required="true">
   <cfargument name="word" type="string" required="true">
   <cfargument name="front" type="string" 
        required="false" default="<span style=""
		   background-color: yellow;"">">
   <cfargument name="back" type="string" required="false" 
default="</span>"> <cfargument name="matchCase" type="boolean" required="false"
default="false"> <cfif not MatchCase> <cfreturn REReplaceNoCase(str,"(#word#)","#front#\1#back#","ALL")> <cfelse> <cfreturn REReplace(str,"(#word#)","#front#\1#back#","ALL")> </cfif> </cffunction> <cfsavecontent variable="test"> This is a test string. If it contained important information, I'd have to make sure I use the highlight function. As it stands, the information is rather trivial. </cfsavecontent> <cfoutput>#highLight(test,"information")#</cfoutput>


I used a few new attributes here. First, I specified a returnType in the cffunction tag. This ensures that the function returns the correct type of data. I also added a type attribute to all of the attributes. Notice that the third, fourth and fifth cfargument tags all show required="false" as well as default values.

Imagine a coworker who has to work with your UDF. Now imagine them looking at this version compared to the earlier one. It's much easier to see not only what the function returns, but what arguments are required and what type of values they should be. So now we have multiple reasons to use cffunction syntax for our UDFs! As an extra bonus, you will see a nicely formatted description of the UDF if you do this:

<cfdump var="#highLight#">

Below is a screen shot of the highLight UDF being CFDUMPed:


highLight UDF


To be honest, I've only scratched the surface of what these new tags can do. Download ColdFusion MX and play around with it yourself. If you want to see examples of UDFs in action, check out the ColdFusion Common Function Library Project http://www.cflib.org. This is a site I maintain along with fellow Team Macromedia member Rob Brooks-Bilson, who wrote Programming ColdFusion (O'Reilly & Associates, 2001). We currently have over 400 UDFs that are open-source and free for anyone to use. We also have UDFs that work specifically under ColdFusion MX. If you create any MX UDFs (or any UDFs for that matter), be sure to submit them so that we can share them with the world.


About the author
Raymond Camden is a senior software engineer for Mindseye, Inc. A long time ColdFusion user, Raymond is a co-author of the Mastering ColdFusion series published by Sybex Inc, as well as the ColdFusion MX Developer's Handbook. He also presents at numerous conferences and contributes to online webzines. He is a contributor and technical editor of the ColdFusion Developer's Journal. He and Rob Brooks-Bilson created and run the Common Function Library Project, an open source repository of ColdFusion UDFs. You can reach him at ray@camdenfamily.com.