As a member of Team Macromedia (formerly Team Allaire), I spend a lot of time on the ColdFusion Support Forum answering technical questions about programming in ColdFusion. Here are ten programming tips derived from the most frequently asked questions presented on the forum:
Know the rules for naming variables.
The golden rule concerning naming variables in your ColdFusion applications is simple:
Variable names must begin with a letter and can contain only letters, numbers, and the underscore character.
In addition to these two rules, there are several additional guidelines that you should follow to minimize potential problems in your applications:
Variable names are not case sensitive. In the interest of good style and readability, however, you should keep the case of your variable names consistent.
Always try to use descriptive terms for your variables. It might seem like a pain, but you will be grateful when it comes time to debug or add a new feature later on down the road.
Avoid using variable names that may be reserved words in SQL. Words such as Time, Date, and Order may cause errors when querying databases.
Avoid using variable names that are the same as ColdFusion variable scopes. Names such as Application, Attribute, Caller, CGI, Client, Cookie, Form, Variable, Request, Server, Session, URL, and Query.
Avoid choosing variable names that end in _date, _eurodate, _float, _integer, _range, _required, or _time. These are reserved suffixes for server-side form validation variables and can cause naming conflicts.
Use ColdFusion variable names that match the corresponding fields in the database. If your application interacts with a database, this makes your code clearer.
Scope your variable.
ColdFusion supports a number of different variable scopes, where scope refers to the context in which the variable exists within an application. The scope encompasses where the variable came from (such as a form field, a URL, etc.), how it can be used, and how long it persists. When you refer to a variable in your code, you can refer to it using just the variable's simple name (MyVar) or by its fully scoped name (Scope.MyVar).
Visit web.oreilly.com for a complete list of O'Reilly's books about Web and Internet technologies.
Because ColdFusion supports different variable scopes, the potential exists for having like-named variables of different scopes within an application. ColdFusion allows you to deal with this potential conflict in two ways.
One way to handle potential variable conflicts is to always provide the variable scope when referencing a variable. For example, a URL variable should be referenced as URL.MyVariable, while a form variable should be referenced as Form.MyVariable. Using the variable scope has two additional benefits. First, it makes your code more readable, by identifying the variable scope right along with the variable. That way, when you look through your code, you know in exactly what context a particular variable is used. The second benefit has to do with performance. When ColdFusion encounters a scoped variable, it is able to process the code faster because it does not have to take time to determine the variable's scope.
The second way to deal with potential variable conflicts is to let ColdFusion handle them. When the ColdFusion server encounters an unscoped variable, it attempts to evaluate it in a specific order. Because application, server, session, attribute, caller, and request variables must always be scoped, they are not included in the order of evaluation, which is as follows:
As you might imagine, allowing ColdFusion to resolve potential conflicts can lead to unexpected results. For example, you might refer to a variable thinking that you are getting a URL variable, but ColdFusion resolves it to a local variable that has the same name. Of course, you can avoid this problem by choosing your variable names more carefully. But to make things even clearer, I recommend that you always scope your variables.
Lock all reads/writes of application, session, and server variables.
Because ColdFusion is a multithreaded application server, it is possible for multiple threads to attempt to access the same variable at the same time. For application, session, and server variables, this is an issue. Because each of these persistent variable types is stored in the ColdFusion server's RAM, the potential exists for the memory to become corrupted as multiple threads attempt to access (read or write) the same variable concurrently and end up colliding.
When a collision occurs, all sorts of problems can result. I've heard of everything from users receiving other users' data, to server instability, to crashing the ColdFusion application server. Because memory space is involved, the results of a collision are, at best, unpredictable.
Fortunately, ColdFusion has a mechanism for managing concurrent access to specific variables or chunks of code know as locking. Locking can be broken down into two types, exclusive and read-only. Exclusive locking means that ColdFusion single-threads access to a particular variable or chunk of code:
<CFLOCK SCOPE="Session" TYPE="Exclusive" TIMEOUT="30" THROWONTIMEOUT="Yes"> <CFSET Session.Username="pmoney"> <CFSET Session.AccessLevel=5> </CFLOCK>
This means only one thread at a time is allowed to access code that has been exclusively locked. Any other threads that attempt to access an exclusively locked block of code are queued until the initial request completes. Exclusive locks must be used (notice I say must, and not should) when writing to application, session, and server variables. Because exclusive locks single-thread concurrent requests, they have a negative impact on performance. For this reason, it is important to use them sparingly.
The other type of lock you can use is a read-only lock. When you place a read-only lock around a particular piece of code, ColdFusion does not automatically single-thread access to that code:
<CFLOCK SCOPE="Session" TYPE="ReadOnly" TIMEOUT="30" THROWONTIMEOUT="Yes"> <CFOUTPUT> Username: #Session.Username#<BR> Access Level: #Session.AccessLevel# </CFOUTPUT> </CFLOCK>
What it does do is prevent an exclusive lock from being placed on the code while it is being read from. In other words, if you have a read-only lock placed around a chunk of code that reads a shared persistent variable, multiple threads can read the variable's value, but a concurrent request to write to the variable will not be processed until the read operations complete. Conversely, if an exclusive lock is already in effect, a read-only lock will wait until the exclusive lock is released before proceeding. Because of this, read-only locks do not generally result in degraded performance. Read-only locks should be used anytime you read data from a shared persistent variable.
There is a lot more to locking than presented in this tip. Although I've covered the basics here, a complete discussion can be found in my book Programming ColdFusion (O'Reilly).
Alternate row colors in dynamically generated HTML tables.
On of the most frequently asked questions by those new to ColdFusion development is how to alternate the row colors for dynamically generated HTML tables. There are several ways to do this, however, the easiest is to use the IIF() and DE() functions as shown in the following example:
<CFQUERY NAME="GetEmployeeInfo" DATASOURCE="ProgrammingCF"> SELECT Name, Title, Department, Email, PhoneExt FROM EmployeeDirectory </CFQUERY> <TABLE CELLPADDING="3" CELLSPACING="0"> <TR BGCOLOR="#888888"> <TH>Name</TH> <TH>Title</TH> <TH>Department</TH> <TH>E-mail</TH> <TH>Phone Extension</TH> </TR> <CFOUTPUT QUERY="GetEmployeeInfo"> <TR BGCOLOR="###IIF(GetEmployeeInfo.currentrow MOD 2, DE('E6E6E6'), DE('C0C0C0'))#"> <TD>#Name#</TD> <TD>#Title#</TD> <TD>#Department#</TD> <TD><A HREF="Mailto:#Email#">#Email#</A></TD> <TD>#PhoneExt#</TD> </TR> </CFOUTPUT> </TABLE>
The row color is alternated by using the IIF() and DE() functions along with the MOD operator to determine whether or not the row number for the current record being output is odd or even. Depending on the outcome of the evaluation, one color or the other (in this case, two shades of gray) is used as the background color for the current row. Because hex color codes are supposed to begin with a pound sign (#), we have to create an escape sequence before we call the IIF() function. This is done by doubling up on the first pound sign.
Obtaining a list of form fields and their values.
One question I see posted over and over again in the ColdFusion Support Forum asks how to obtain a list of all form fields and their associated values submitted by an HTML form using the POST method. This technique is often used to work with dynamically created form fields, in situations where you do not necessarily know the names of the form fields being passed.
There are two ways to obtain a list of form field names and their values:
The first method uses a special ColdFusion structure, named Form (introduced in v4.5) that contains each form field name and its associated value. To obtain a list of all form variables within the Form structure, you could use code like this:
<TABLE> <TR> <TH>Variable Name</TH> <TH>Value</TH> </TR> <!--- loop over the Form structure and output all of the variable names and their associated values ---> <CFLOOP COLLECTION="#Form#" ITEM="VarName"> <CFOUTPUT> <TR> <TD>#VarName#</TD> <TD>#Form[VarName]#</TD> </TR> </CFOUTPUT> </CFLOOP> </TABLE>
The code uses a collection loop to loop over the Form structure. Each variable name and its associated value are output in an HTML table.
The second method for obtaining a list of every form variable passed to a template involves a special form variable called Form.FieldNames. This variable is automatically available to any ColdFusion template and contains a comma-delimited list of form field names that have been posted to the current template.
<CFOUTPUT> <B>Field Names:</B> #Form.FieldNames# <P> <B>Field Values:</B><BR> <CFLOOP INDEX="TheField" list="#Form.FieldNames#"> #TheField# = #Evaluate(TheField)#<BR> </CFLOOP> </CFOUTPUT>
In this example, the list of field names is output on a single line. Next, a list loop is used to loop over the list of field names and output each one along with its associated value. The value for each form field is obtained using the Evaluate() function. Note that the special validation form fields (i.e., ones that have names that end with _date, _time, etc.) are not present in the Form.FieldNames variable. They are, however, present in the Form structure we discussed in the first method.
Avoid redirection with CFLOCATION when using cookies.
Due to the way ColdFusion assembles dynamic pages, you should not attempt to use the CFLOCATION tag within a template after a cookie variable has been set. Setting a cookie variable and using CFLOCATION afterward results in the cookie not being set. If you need to redirect to a different template after setting a cookie, consider using the CFHEADER tag instead, as in:
<CFCOOKIE NAME="MyCookie" VALUE="Hey, look at me!"> <CFHEADER NAME="Refresh" VALUE="0; URL=http://www.example.com/mytemplate.cfm">
The CFHEADER tag generates a custom HTTP header with a Refresh element that contains the number of seconds to wait before refreshing the page as well as the URL of the page to retrieve when the refresh occurs.
Use stored procedures for database queries whenever possible.
Most enterprise level databases (MS SQL Server, DB2, Oracle, Informix, Sybase) support creating special programs within the database called stored procedures.
Stored procedures allow you to encapsulate SQL and other database-specific functions in a wrapper that can be called from external applications. There are several reasons why you should use stored procedures whenever possible in your applications:
Stored procedures execute faster than identical code passed using the CFQUERY tag because they are precompiled on the database server.
Stored procedures support code reuse. A single procedure only needs to be created once and can be accessed by any number of templates--even different applications and those written in other languages.
Stored procedures allow you to encapsulate complex database manipulation routines--often utilizing database specific functions.
Security is enhanced by keeping all database operations encapsulated within the stored procedure. Because ColdFusion only passes parameters to the stored procedure, there is no way to execute arbitrary SQL commands.
Many enterprise level databases support the return of more than one record set through stored procedures. This simply isn't possible using the CFQUERY tag.
Avoid assigning user-defined functions (UDFs) to persistent variable scopes.
The code for user-defined functions can be written inline, at the beginning of a template, or more commonly, contained in a separate file that is included at the beginning of the template via the CFINCLUDE tag. Advanced developers new to UDFs may be tempted to assign frequently used functions to one of the persistent scopes (Application, Session and Server scope) in an attempt to improve performance. Although this sounds tempting, it should be avoided.
The first problem with this technique has to do with locking. Because reads and writes to persistent variables must always be locked, any time you want to reference a UDF in your code, you'll need to include two extra lines of code to lock the variable. This can get to be a real pain with frequently used functions.
The second reason to avoid this technique is that it wastes RAM. Each function you store in a persisitent variable takes RAM that could probably be put to better use elsewhere in your application. The Server scope is a particularly bad place to put UDFs as Server variables persist until the ColdFusion Application Server is rebooted.
It makes much more sense to keep all of your frequently used UDFs in a single "function library" template that can be included via CFINCLUDE only in the templates where the functions are needed. If you find you use functions from your library throughout your application, you have the option of placing a single CFINCLUDE in your Application.cfm template. The minor overhead of having to include the function library template easily offsets the inconvenience of locking all function calls and wasted server resources of storing the function in a memory resident variable.
Detecting WAP- and WML-enabled devices.
ColdFusion is increasingly being used to deliver content to WAP (Wireless Application Protocol) enabled devices such as cell phones and PDAs. One question I'm frequently asked is how to detect when a request to the server is made by a WAP- and WML- (Wireless Markup Language) enabled device, so that WML content can be delivered to the user instead of HTML. The answer is to use the CGI variable HTTP_ACCEPT to see if the user's browser is capable of receiving content with the MIME type text/vnd.wap.wml. The following code should be placed at the top of any page you want the check to occur on:
<!--- if the user is using a WAP enabled device, send them to the WAP version of the site. ---> <CFIF CGI.HTTP_ACCEPT CONTAINS "text/vnd.wap.wml"> <CFLOCATION URL="/wap/index.cfm"> </CFIF>
If the user's browser does accept the MIME type, we know the user is coming to the site with a WAP- and WML-enabled device, and he or she is rerouted to an appropriate page that generates WML content.
Take advantage of the Verity K2 server in ColdFusion 5.0.
Since version 2.0, ColdFusion has included advanced indexing and searching capabilities using a bundled version of Verity's popular search technology via the VDK (Verity Developer's Kit). In addition to the VDK, ColdFusion 5.0 comes with a restricted version of Verity's enterprise level K2 server. K2 server offers features that appeal to clustered and large-scale sites, such as simultaneous searching of distributed collections, concurrent queries, and an overall performance gain over the VDK engine. Setting up and administering the K2 server takes a bit of work, but is well worth the results. For more information, consult the Advanced ColdFusion Administration book that comes with the official ColdFusion documentation.
O'Reilly & Associates will soon release (August 2001) Programming ColdFusion.
Sample Chapter 11, Advanced Database Techniques, is available free online.
For more information, or to order the book, click here.
Copyright © 2009 O'Reilly Media, Inc.