I'm a big fan of sharing mistakes as I figure any mistake I make I'm sure others have as well. If you read my blog or have attended any presentation I do on ColdFusion Components, you know I get pretty anal about var scoping. One of the things I mention is how if you don't var scope - some day - some where - a bug is going to bite you in the rear and it may takes days to track it down. Well guess what? It happened to me.
For about two months now I've gotten daily bug emails from RIAForge. The bug always looked a bit like this:
The NAME parameter to the setNAME function is required but was not passed in.
I was never able to reproduce the bug, and as far as I knew, it wasn't a bug that could exist. Here is the code in question:
<cfquery name="getit" datasource="#dsn#">
select stuff,changed,here
from users
where id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.id#">
</cfquery>
<cfif getit.recordCount>
<cfloop index="col" list="#getit.columnlist#">
<cfinvoke component="#bean#" method="set#col#">
<cfinvokeargument name="#col#" value="#getit[col][1]#">
</cfinvoke>
</cfloop>
</cfif>
This code is in my DAO CFC in the read method. I use the fact that my database columns has the same names as my bean. This lets me easily set all the values in the bean by just looping over the column list from the query.
Looking at this code, it seems impossible for me to run setX and not pass X. As you can plainly see, the method name and argument share the exact same value.
The only thing I figured was that maybe the value in the query was null, and ColdFusion was saying, "Yea, you passed me X, but it was null, so you didn't really pass X."
Turns out - I had forgotten a simple var scope on "col". Under load, it was possible for the col values not to match up and then throw my error.
So - just consider this a warning!
Edit: I've had a number of people get confused by the code block above. The code block above was merely meant to show the area throwing the bug. The fix (adding a var scope line) was shown because it was just a var scope. But to make it more clear, I've added the entire function below. Again, the only change was to add the var scope line.
p.s. For folks wanting to see the entire RIAForge site, I promise, soon.
<cffunction name="read" access="public" returnType="UserBean" output="false">
<cfargument name="id" type="numeric" required="true">
<cfset var bean = createObject("component","UserBean")>
<cfset var getit = "">
<cfset var col = "">
<cfquery name="getit" datasource="#dsn#">
select cols,changed,for,security,reasons
from users
where id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.id#">
</cfquery>
<cfif getit.recordCount>
<cfloop index="col" list="#getit.columnlist#">
<cfinvoke component="#bean#" method="set#col#">
<cfinvokeargument name="#col#" value="#getit[col][1]#">
</cfinvoke>
</cfloop>
</cfif>
<cfreturn bean>
</cffunction>
Archived Comments
Just a note that the variable "getit" would also need to be var'ed.
And #dsn# and #bean#.
Why Adobe consider this a feature I've no idea.
Oh, sorry if it wasn't clear. The code above is a subset of the entire method. Everything else was properly var scoped.
Is the code snippet the fixed code?
If not, can you post the fixed code as an example?
Josen
I didn't fix the code like a syntax error, I just added the missing var scope. Since this is confusing folks though, I'll add it to the blog entry. Reload in 5 minutes.
Tom,
Adobe did not do anything wrong. That is th proper behavior. If you do not scope a variable in a CFC, ColdFusion assumes it should be in the variables scope. The variables scope is like the this scope in Java.
David Fekke.
Although in Java you have to explicitly declare your variables. I suppose the combo of implicit declaration of variables and a function scope (the var scope) does indeed lend itself to this common coding mistake.
Coldfusion should default scope variables in methods to the var scope, period.
I was hoping CF8 was going to fix this major annoyance.
At least give us an option to tell Coldfusion what is the default scope inside functions.
Common sense lost at Adobe? If I create a variable inside a function, I think according to common sense that variable should be defaulted to being in the var scope unless given a scope.
In my opinion, Adobe did get this wrong...
At first your comment made sense to me. But then I started thinking about other scopes. Should custom tags default to the ThisTag scope? The Attributes scope? Or are they right to default to the Variables scope? What about references to variables in Application.cfc/.cfm? It would get confusing really fast if you had "context-based scope".
Not attacking you, it's just a thought.