Ditch ColdFusion Evaluate()

The topic of evaluate() recently came up on the CFCDev list. Sean Corfield asserts that evaluate() is only needed for backward compatibility since all variables scopes can be treated as structures as of ColdFusion MX. He argues against its use in any new code siting performance reasons.

Here are some situations in which you might be using evaluate(), but don't need to do so.

1) Getting the value of a Form variable where the field name is (or contains) a variable.

Using evaluate:
<cfset foo = evaluate("Form.bar#i#")>

Without evaluate:
<cfset foo = Form["bar#i#"]>

Since Form is now a structure, you can use structure syntax to get the variable. This will work in other scopes as well (request or URL, for example).

2) Get the value of a column in a query where the column name is (or contains) a variable.

Using evaluate:
<cfset foo = evaluate("qBar.text#lang#")>

Without evaluate:
<cfset foo = qBar["text#lang#"][CurrentRow]>

The important thing to keep in mind with queries, is that while you can use structure syntax, you must indicate the row. The "CurrentRow" variable is handy if you are within a query loop (using the query attribute for the given query in either <cfloop> or <cfoutput>). Otherwise, you will have to pass in the number another way. If you know you want the first row, for example, you could simply pass in 1.

3) Checking the existence of a variable.

This isn't actually an evaluate() issue, but I know that Mr. Corfield also recommends avoiding isDefined() in favor of StructKeyExists(), so I thought I may as well cover that here as well.

Using isDefined()
<cfif isDefined("Form.bar#i#")>

Using StructKeyExists()
<cfif StructKeyExists(Form,"bar#i#")>

The same principle applies here as from the first example. Since "Form" is a structure, it can be treated as such (as can other variable scopes).

If you have any other situations in which you currently use evaluate(), let me know.

Good luck!

UPDATE

Scott Stroz noted that you can use the same approach with queries as well. Since the format doesn't show up correctly from comments, here it is:

The format for that would actually be:

<cfset foo = qBar["column name"][CurrentRow]>

or, with a variable,

<cfset foo = qBar[colvar][CurrentRow]>

Scott's example for queries and columns:

<cfset foo = qBar["A column with spaces"][currentRow] />

<cfset foo["a structKey with spaces"] = 'bar' />


Thanks Scott!

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
You can use this same technique if you have column names in a query, or keys in a struct, that are not allowed in CF, like ones with spaces.



# Posted By Scott Stroz | 6/2/06 6:05 PM
Good point, Scott. I have added that to my entry.

I love your threat level web service, btw (good blog to).
# Posted By | 6/3/06 12:57 PM
Regarding the use of evaluate, how would you do something like this:

{{{

   

}}}
# Posted By Mike Rankin | 8/14/06 3:34 PM
Mike,

I'm not sure I completely understand the example, but would this work?

{{{
   
      
   

}}}
# Posted By | 8/14/06 3:46 PM
Maybe this will help, here's the whole method.

{{{













}}}

This function is in a component that is extended by all of my gateway classes. It basically allows a query to be returned as an array of typed objects. It works pretty good, but if there is a big performance hit because of the evaluate function, I would just as soon get rid of it.

All it's really doing is pasting together the calls to the child component's setter functions. I don't really use it too much, since I find recordsets a little easier to work with.

I think I'd have to do some more work to figure out how to use cfinvoke in this case, but it can probably be done by looping over the cfinvokeargument tag to supply all of the different property values to the init function. I'll have to look into it when I get a little more free time.
# Posted By Mike Rankin | 8/14/06 4:40 PM
Mike,

I think your example is passing in the name of the field instead of its value.

I think you will want to use this as the value:

q{field}{CurrentRow}

(replace curly brackets with square ones)

In any event, I think the cfinvoke syntax I showed should work.

I think it would be interesting to see how much of your performance issue is caused by looping over evaluate() and how much of it is caused by looping over CreateObject(). Object creation in ColdFusion can be somewhat expensive.

You also need to make sure to var "obj" and "field".
# Posted By | 8/14/06 7:03 PM
Thanks for the discussion. Here is what I'm ending up with:
{{{

   
   
   
   
   
   
      
         
            
         

      

      
   

   

}}}

Notice that the approach is a little different. Instead of stitching together a call to each property's mutator, it now just fires the init function. That seems a little cleaner to me.

In this case, I don't really have a performance issue yet, I've just fallen into the habit of using evaluate and couldn't break out of that mode of thinking easily. For what it's worth, I think the performance hit with createObject may be overblown a bit. I have a site now that creates tens of thousands of them on a single call, and while it may take a few moments to complete the request, it's still taking seconds instead of minutes. I almost always seem to get more performance milage out of tuning the db than in tweaking the cf.
# Posted By Mike Rankin | 8/15/06 4:15 PM
Mike,

I like that approach. It looks really clean. Which, I think, is generally more important than performance (though I would expect this to perform pretty well).

As to the efficiency of CreateObject, I think it also depends on how much happens in your component upon object creation.

In general, I like to remember to follow good practices, but only optimize code when you have a performance issue.

Then, I generally look at file size first (usually large images effect load times more than anything). Second, I look at database issues. Third, HTML issues (bloated HTML can add significantly to load time). Finally, I examine the CFML.
# Posted By | 8/15/06 6:53 PM
Thank you
The information was extremely helpful. I had been vexed for a day trying to resolve an isdefined-with-evaluate issue.
# Posted By Richard Carson | 2/9/07 5:01 PM
I know this is old (very old) post but I came across a scenario I couldn't get around w/o using the Evaluation().
Here is what I got,

<cfoutput>
<cfset MainArray_arr = []>
<cfloop from="1" to="2" index=x>
<cfswitch expressing = "x">
<cfcase value="1">
<cfset SubArrayOne_arr = ['One','ThisOne','123456']>
<cfset ArrayAppend(MainArray_arr , 'SubArrayOne_arr')>
</cfcase>
<cfcase value="2">
<cfset SubArrayTwo_arr = ['Two','ThisTwo','987654']>
<cfset ArrayAppend(MainArray_arr , 'SubArrayTwo_arr')>
</cfcase>
</cfswitch>
</cfloop>

<!----Showing/Getting What's in MainArray_arr Array--->
<cfloop array="#MainArray_arr#" index="y">
#Evaluate('#y#[1]')# -- #Evaluate('#y#[2]')# -- #Evaluate('#y#[3]')#
</cfloop>
</cfoutput>

Any help would be greatly appreciated.
Thank you.
# Posted By Madushan M. | 6/14/13 11:45 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.