|
Comments
|
Today's Top SOA Links
BF on CF User Defined Functions - Round 2
User Defined Functions - Round 2
By: Ben Forta
Jul. 31, 2002 12:00 AM
The introduction of user-defined functions (UDFs for short) was the most requested and anticipated event in the ColdFusion 5 era - developers desperately wanted to be able to extend CFML, using not just tags, but functions as well. I first introduced UDFs a year or so ago (CFDJ, Vol. 3, issue 5), but now it's time to revisit the subject. Why? Because ColdFusion MX provides us with a whole new way to write UDFs, this time using tags.
Why UDFs?
Prior to CF5 we had no way to write our own functions. For example, ColdFusion provides a whole range of list-manipulation functions - List-First(), ListGetAt(), ListSort(), and so on - but there was no function to get the smallest or greatest value in a list - ListMin() and ListMax(). To get these values you'd have to loop through the list, doing the comparisons yourself. There were two ways to do this: (1) inline, right in the middle of your code; or (2) by creating a custom tag. Neither option was ideal. The former wasn't reusable; each time we wanted to obtain these values we had to copy and paste the block of code. The latter was inefficient (custom tags execute more slowly than functions do) and clumsy (because custom tags have no mechanism by which to return data). The right solution is a user-defined function, which became possible in CF5.
CF5 UDFs
Here's a simple example of a <CFSCRIPT>-based UDF (this is the example I used a year ago). Suppose you need to use yesterday's or today's date repeatedly within your code. The CFML DateAdd() function can be used to add or subtract a day. To refer to yesterday you could use DateAdd("d", -1, Now()); to refer to tomorrow, DateAdd("d", 1, Now()). These function calls have to be repeated every time they're needed. Or you could create a couple of UDFs. CFML already has a function, Now(), that returns today's date (and time). Why not Yesterday() and Tomorrow() functions to complement it? Here's the CF5 UDF code that creates these two new functions: <CFSCRIPT>As you can see, the UDFs are created using CFML scripting between <CFSCRIPT> and </CFSCRIPT> tags. This <CFSCRIPT> block defines two functions, Yesterday and Tomorrow. Each function is preceded by the keyword function so <CFSCRIPT> knows we're about to define a function. These functions are very simple. All they do is return the result of a DateAdd() function: one adds 1 to Now(), the other subtracts 1 from Now(). The value returned must be preceded by the keyword return, and whatever follows return is returned by the function to the caller. You can return functions (as we did here), variables, or any other valid CFML expression. Once defined, these user-defined functions can be used just like any other CFML functions. As long as the UDFs appear on the same CFM page, or are included in your code via <CFINCLUDE>, they can be used like this: <CFOUTPUT>As you can see, the new Yesterday() and Tomorrow() functions are used just like the built-in Now() function, and all three functions are passed to DateFormat(). Once defined, your own UDFs can be used like any other ColdFusion functions - there's no difference whatsoever.
UDFs Revisited
So CFMX introduced tag-based UDFs - the ability to create user-defined functions using CFML tags. The tags used are:
<!--- Get yesterday's date --->They're pretty much self-explanatory. Each function is created using a <CFFUNCTION> tag and ends with a matching </CFFUNCTION> tag. The code between the tags makes up the function itself. These particular functions have no processing, just a return value specified using <CFRETURN> (as in the <CFSCRIPT> versions). So how would you use these functions? Just as you would the <CFSCRIPT> versions: <CFOUTPUT>It's worth noting that there's one difference between the <CFSCRIPT>- and tag-based versions of these functions: the latter define a RETURNTYPE. This optional attribute is used to define the type of data returned by a UDF. This value is used in validating return values as well as where functions can and can't be used. If RETURNTYPE isn't specified, any type will be allowed, just as in <CFSCRIPT>-based functions. As a rule, you should always define return types.
Accepting Parameters
Tag-based UDFs (like <CFSCRIPT>-based UDFs) can accept parameters (technically called arguments). All you have to do is define them in the order that they're expected by using the <CFARGUMENT> tag in one of two ways: <CFARGUMENT NAME="n"That line of code defines a required parameter. If it isn't provided, an error is thrown. Additional validation can be specified to check the type of passed data: - <CFARGUMENT NAME="n"In this example the passed value must be numeric. If it isn't specified at all, or if a nonnumeric value is specified, an error will be thrown. <CFARGUMENT> can also be used to define optional parameters, like this: <CFARGUMENT NAME="date"In this example a date may be specified, but it isn't required. In practice a default value should be used in this situation, like this: <CFARGUMENT NAME="date"Here the date is optional. If it's specified, it had better be a valid date; if it isn't specified, then today's date will be used as the default. Now that you know how to use <CFARGUMENT>, here's a complete (albeit simple) example. Cube(), as its name suggests, returns the cube of a specified value (pass it 3 and it will return 27). Here's the tag-based Cube() UDF: <!--- Get the cube of a number --->Cube() accepts a single parameter. To accept additional parameters all you do is insert additional <CFARGUMENT> tags. Simple as that.
Using Local Variables
Let's look at an example. The function in Listing 1 verifies a datasource name to make sure it's working (in the past this was doable only from within the ColdFusion Administrator). This particular UDF uses some advanced CFMX features that I'll explain in detail next month. VerifyDSN() does just that: pass it the name of a datasource and it'll return TRUE if the datasource can be verified (as usable) or FALSE if not. VerifyDSN() accepts one required parameter, the name of the datasource to be verified. The actual work is done by a Java call to a class named coldfusion.server.ServiceFactory and objects within it (that's what I'll cover next month). But there's a big potential problem here. Within VerifyDSN() two variables are used, dsService and result. As already stated, all scopes are visible within UDFs. So what if a variable named result already exists outside the UDF? The <CFSET> calls will overwrite it (a real likelihood with a variable named result). Any variables created within a UDF will be of type VARIABLES if no explicit scope is specified. So how can you create local variables (local only to the UDF)? Look at this code snippet (taken from the VerifyDSN() function): <!--- Init local variables --->These two <CFSET> tags define local variables; what makes them local is the VAR keyword. As local variables named dsService and result have been defined (because VAR was specified), they'll be used within the UDF (and not any variable of the same name in other scopes). As a rule, every variable used within a UDF should be defined this way so as to prevent accidental overwrites.
Things to Keep in Mind
UDFs can be written when needed (right within your CFM page), or in libraries (files containing nothing but lists of UDFs), where they can be included as needed. There's no limit to the number of UDFs that can be written and used, and there's no limit to how many UDF libraries you can include in your page. Note: Visit www.cflib.org to access the best public collection of ColdFusion UDFs.
There you have it. UDFs are useful, they're fast, and they're fun. And now they're easier to write and more powerful than ever before. Reader Feedback: Page 1 of 1
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
|||||||||||||||||||||||||||