CFML 101 - Rookie Mistakes Part 2

{ Posted By : Eric Cobb on December 17, 2009 }
1949 Views
Related Categories: CFML 101, CFML

Just as in Rookie Mistakes Part 1, this post will cover mistakes that we've all made at one point or another. These are things that seasoned developers instinctively know to do, but beginners may not realize are better or more efficient. Some of this post is based loosely on Adobe's Coding Best Practices for ColdFusion Performance, with a few of my own personal favorites thrown in there.


1) Application Multiplication

NOTE: This section is a rewrite of my original "Application Multiplication" section. My original post seemed to have some misinformation in. (see comments below) Actually, it wasn't misinformation...what I originally posted was flat out wrong. Actually, it wasn't just flat out wrong...what I originally posted was the exact opposite of how Applicaiton.cfm really works! Thanks go to David Boyer and Larry Lyons for straightening me out! And, yes, I do feel stupid. Thank you for asking. ;^)

One thing that those new to CFML sometimes don't realize is that the Application.cfm file is run on every .cfm page request, before any other CFML code gets executed. The same thing applies to OnRequestEnd.cfm. Application.cfm only gets called once per request, so if you have an index.cfm page that has 2 cfincludes (both including a .cfm page) inside of it, Application.cfm still only gets called once. This is important to note because I originally thought that Application.cfm got called once for every .cfm page (including the includes) instead of once per request. But, as it turns out, that is not the case (see http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec22c24-7d6f.html).

Since Application.cfm gets called every time you make a request to a .cfm page, that means any variables that are getting set in Application.cfm, and any queries that are being run in Applicaiton.cfm, and any other code in your Application.cfm file is being run over and over again unless you have some conditional logic preventing it.

For example, let's say we're setting our datasource to an application variable in our Application.cfm file:

<cfset application.dsn = "myDb">

This code pretty much defeats the purpose of an application variable. It is constantly resetting the application variable, whereas you generally just want to set an application variable once and leave it alone unless it changes. If you're going to reset the variable every time a .cfm page is called, there's really not much point in putting it in the application scope.

The proper way to do this is:

<cfif not StructKeyExists(application,"dsn")>
    <cflock scope="Application" throwontimeout="true" timeout="10" type="exclusive">
        <cfset application.dsn = "myDb">
    </cflock>
</cfif>

Now we're telling Application.cfm not to bother setting our variable unless it doesn't exist. So, Application.cfm will set the variable once, when your application first starts, and not worry about it any more. It's also important to remember to use cflock whenever you're writing to a shared scope (such as application or session).

As a side note, Application.cfc came out in 2005 with ColdFusion MX 7 as a replacement of the traditional Application.cfm and OnRequestEnd.cfm application templates. Application.cfc gives you much more detailed control over how and when things are called than Application.cfm does. If you're not yet using Application.cfc instead of Application.cfm, I would highly recommend that you try to do so if at all possible.


2) Fun with Nested Functions

Another thing that CFML newcomers may not realize is that you can nest functions together. So, instead of having multiple cfset statements, you can combine your functions into one statement.

For example, instead of:

<cfset variables.moneyVar = "$500.00">

<cfset variables.moneyVar = Replace(variables.moneyVar,"$","")>
<cfset variables.moneyVar = Trim(variables.moneyVar)>

You could have:

<cfset variables.moneyVar = "$500.00">

<cfset variables.moneyVar = Trim(Replace(variables.moneyVar,"$",""))>

This works great when you don't have many functions. However the more functions you nest together, the more convoluted it gets. Take the following example:

<cfset variables.moneyVar = "$1,500.00">

<cfset variables.moneyVar = Replace(variables.moneyVar,"$","")>
<cfset variables.moneyVar = Replace(variables.moneyVar,",","")>
<cfset variables.moneyVar = ListFirst(variables.moneyVar,".")>
<cfset variables.moneyVar = Trim(variables.moneyVar)>

If you tried to nest these together into one cfset statement, you would have:

<cfset variables.moneyVar = "$1,500.00">

<cfset variables.moneyVar = Trim(ListFirst(Replace(Replace(variables.moneyVar,"$",""),",",""),"."))>

While this would work fine as far as processing the code goes, it's getting a little jumbled up and hard to read. When it comes to using nested functions, there's really no "best practices" rule that I know of. It can go either way. They're fine if you just have a few of them together, but it's probably better to break them up if you're going to have several of them. There's not really much of a performance hit either way, so it really just comes down to how readable you want your code to be, and how much you like to type as to whether or not you use them.


3) You'd better be Query Paramin'!

Always, always, always use cfqueryparam. There is no valid reason not to (ok, there are 1 or 2 valid reasons, but they are rare!). If you don't currently use cfqueryparam stop what you're doing right now, go to the nearest whiteboard and write this 100 times: "I will always, always, always use cfqueryparam because it's the right thing to do, and I am not a slacker. I love my applications and want to code them in a way that will not make other CFML coders point and laugh at me. I will always, always, always use cfqueryparam." Go ahead, I'll wait.

Seriously, though, cfqueryparam is so very important and should be used at all times. It's not hard to use and it has several benefits, such as increased performance and added security against SQL injection attacks. One of my favorite features of cfqueryparam is its ability to handle dates. That's actually what got me started using cfqueryparam several years ago. I was working on an Intranet app that was connecting to both SQL Server and Oracle databases. I was always having to format my dates with createOBDCdate() or to_date() until I found out that cfqueryparam handles that for you. So now all I have to do is put my date variables in a cfqueryparam tag and it takes care of the rest.

I'm not going to get into all of the details of cfqueryparam and how to use it. Here are a couple of good (old) articles that already do that. I'm just going to say that if you don't use it, the day will come when you regret it. For me, that time was at the office at 10:30 one night after I'd spent all day trying to recover from a malicious attack. My boss at my first IT job once told me "It's not a matter of 'if' you get attacked, but 'when'". Turns out, 8 years later, he was right. So always, always, always use cfqueryparam. (I mean, come on...it came out with ColdFusion 4 in the '90s! Get with the program!)

This post is part of a continuing series of CFML 101 articles. My intent is to produce a blog series aimed at the beginning CFML developer, one which helps to explain basic techniques and concepts to those new to the CF world. The topics and examples covered in this series focus on the CFML programming language in general, not a specific application server. So whether you're using ColdFusion, Railo, or Blue Dragon (referred to as CF/R/BD) to run your CFML applications, these concepts still apply.

Related Blog Entries

Comments
larry c. lyons's Gravatar You'll actually be creating more problems if you set an application variable like this:

<cfif not StructKeyExists(application,"dsn")>
<cfset application.dsn = "myDb">
</cfif>

A better way is to setup up a lock and the if and then do another if statement around that as in

<cfif not StructKeyExists(application,"dsn")>
<cflock scope="Application" throwontimeout="true" timeout="10" type="exclusive">
<cfif not StructKeyExists(application,"dsn")>
<cfset application.dsn = "myDb">
</cfif>
</cflock>
</cfif>

regards,

larry
# Posted By larry c. lyons | 12/18/09 2:55 PM
Eric Cobb's Gravatar @Larry - D'oh! I complete forgot to add locking! Thanks for pointing that out. Although, I don't see the point in having to run the same cfif twice outside and inside the lock. Can you elaborate?
# Posted By Eric Cobb | 12/18/09 4:19 PM
larry c. lyons's Gravatar This hearkens back to a discussion online with several people from a few years ago.

Barney Boisver gives a much better explanation than I can,
http://www.barneyb.com/barneyblog/index.php?s=cflo...

It a method of preventing race conditions and keeping simultaneous from interfering with each other.

regards,
larry
# Posted By larry c. lyons | 12/18/09 7:36 PM
David Boyer's Gravatar @Eric,

Are you sure about point one?

"If you have 11 cfincludes, Application.cfm gets called 12 times"

I was quite surprised to hear this so I had to try it out. I created a new folder, application.cfm creates an array (application.test) if it doesn't exist. It also added app.cfm to the array. I then have index.cfm (adds its name to the array), include.cfm (adds its name and is included by index.cfm 10 times) and onrequestend.cfm which does the same and dumps the array.

I'm only seeing one application.cfm and onrequestend.cfm in the array, plus the expected amount of 1 index.cfm and 10 include.cfm's.

I'm pretty sure cfinclude doesn't fire off application.cfm and onrequestend.cfm.
# Posted By David Boyer | 12/21/09 3:35 AM
larry c. lyons's Gravatar BTW cfincludes are simply an insert of the template's code into the calling page at the cfinclude's location on the calling page. Thus Application.cfm is only called once in your example.
# Posted By larry c. lyons | 12/21/09 7:13 AM
Eric Cobb's Gravatar @David - I'm almost positive that it's getting executed on each .cfm page call. I'm running FireFox with FireBug and ColdFire. When I view the CF debugging output in ColdFire, I can view the debugging output for each template that was called during a request. I put a couple of queries in my application.cfm file, and as I view the debugging for each included template, it shows each of those queries being run every time an include is called.

I may be completely wrong here. I may be misreading what the debugging output is telling me, but it's always been my understanding that App.cfm fires on every .cfm page request. I'll try to do some digging today and find out for sure.

Thanks for bringing this up!
# Posted By Eric Cobb | 12/21/09 8:14 AM
Eric Cobb's Gravatar Well, you guys were right! According to Adobe (http://help.adobe.com/en_US/ColdFusion/9.0/Develop...)

"ColdFusion processes only one Application.cfc or Application.cfm page for each request. If a ColdFusion page has a cfinclude tag pointing to an additional ColdFusion page, ColdFusion does not search for an Application.cfc or Application.cfm page when it includes the additional page. "

That tells me 1 of 2 things. Either 1) there's something really screwy going on in my code, or 2) I don't have a clue how to use FireBug/ColdFire debugging.

Lesson learned: don't rely solely on your debugging output when writing a blog article, sometimes you need to RTFM too! ;)

I'm removing the original entry until I can get it rewritten correctly.

Thanks guys!
# Posted By Eric Cobb | 12/21/09 8:25 AM
Eric Cobb's Gravatar I've corrected the entry so that it's a little more...um...accurate. Thanks for calling me out on this! I would hate to have left a blog post full of bad info out there for people to use!
# Posted By Eric Cobb | 12/21/09 3:29 PM