Jason asks:
I know it is important to ALWAYS var scope your variables in your CFCs my question is, is there a difference between these two cfset statements?
<cfset var myVar = "" />
<cfset variables.myVar = "" />
It is indeed very important, and there is a world of difference between those two lines of code. I cover this more in my CFC Scope Reference, but the basic difference is that your first line of code creates a variable that will only exist for the execution of the method. You use this for any variable you create in the method, and be sure to not forget things like loop counters, query names, etc.
The second line creates a global variable the CFC. Anything in the variables scope is available to every method of the CFC. I'll typically use the variables scope for configuration information, like a DSN, so all my queries will use datasource="#variables.dsn#". You would definitely not want to use it for things you intend to just be in the local method.
Archived Comments
Ray,
Thanks, that answered my question perfectly. And now that it is explained, it makes perfect sense. i can;t believe i didn;t see it before.
Jason
Don't feel bad. CFC variables scopes is something that can be tricky to developers just getting into it.
Another good one:
<cfset var myVar = "" />
and
<cfset var.myVar = "" />
Are these two statements the same within a cfc?
Nope, unfortunately, unlike the other scopes, the Var scope isn't a "named" scope, so you can't do var.x, and you can't structKeyList it, etc. Some people use something like this:
cfset var local = structNew()
and then use local.x, local.y, etc. This means less var scoping and makes it a bit more obvious that a variable is a local var, but personally I don't care for it.
What scope are variables that are created by tags like cfloop index="somevariable"?
Can Should you
<cfset var intx = 0>
<cfloop from="1" to="#q.recordcount#" var=intx>
so that it's in the local scope, or doesn't it matter?
Yes, you need to var scope loop iterators. _Anything_ that makes a variable needs to be var scoped. For example, cfquery name="foo", that will end up creating a query named foo, so you need to var scope it.
I know Adobe needs to keep this style around for backwards compatibility, but I wish they could introduce new scope names that were easier to read and remember for this purpose.
...Then again, maybe they don't do that for some reason that I'm not aware of. So until then, just one of those things I must keep memorized.
Do you need to var scope queries that do not return values (insert, update, delete)?
Var scoping them just seems kind of... silly.
Akira, if you don't use a name for your cfquery (did you know names were optional?) then don't var scope it.
var scoping may be silly, but it's used in other languages as well.
Definitely var scope your index variables in loops. I was getting an intermittent error for a while that I finally tracked down to just exactly that issue.
some other easy ones to forget are cfsavecontent and cffile variables. It's good to be able to manually look at your cfc functions and fix ones you forgot, but I like to also run http://varscoper.riaforge.org/ fairly often to make sure I didn't miss anything.
Also, when you have a cffunction with a lot going on in it, it's sometime preferable to do cfset Var local = StructNew() right below your arguments, and then prepend all your function-"local" variables with "local." to ensure you don't miss anything. Usually, your functions should be small enough that it doesn't get that complicated, but the real word isn't always so simple... ;)
Ray, no I did not know. I learned it that way and I should read the documentation more carefully. :)
What I meant by silly is that everything should be local unless specifically made global... it just bugs me a little.
Ok, so I checked the documentation again, and it says that name is required for cfquery. However, when I tried a query without a name, it did not break.
Thanks for the tip Ray.
Is there a way to var scope a dynamic variable name? I thought this should work, but doesn't (cfmx7):
<cfset var "#arguments.passedInVariableName#" = "" />
or
<cfset var setVariable(arguments.passedInVariableName,"") />
No, but just use a struct.
cfset var local = structnew()
cfset local[anythinggoes] = "foo"
Thanks Ray. I really don't use dynamic names much, but just ran into a situation where I was using someone else's cfc that did this and was getting all kinds of fun variable "sharing" going on.
I've since just statically renamed these variables, but your suggestion would have been just as well, if not more elegant.
After posting the above I realized that the local struct idea would not have worked in my case (unless I'm mistaken). The dynamic variable names in this case are used for cfquery calls within the function (i.e. <cfquery name="#anythinggoes#"...>).
If you use local[anythinggoes], (i.e. <cfquery name="local[#anythinggoes#]"...> ) CF throws an error because the name local[theactualvalue] "is not a valid ColdFusion variable name".
I may be wrong, but if you do <cfquery name="#local[anythinggoes]#"...> the query results would not be locally scoped since #local[anythinggoes]# would just be a placeholder for the real variable name.
Like I mentioned before I'm now just using a static name for the query and that is fine (and probably the only way to do it). In this case, the dynamic naming of queries is not for referencing the query object itself (inside or outside) of the function, but is simply so that debugging output displays the descriptive name for the query. For instance:
<cfscript>
myQuery = queryCFC.read(sql="select * from orders", name="getAllOrders");
</cfscript>
The debugging output would show the query name as "getAllOrders". petty I know.
Why not just do
cfquery name="mydata"
local[dynamicnamehere] = mydata
That defeats the purpose. The goal was to name the query for debugging output purposes. I'm better off just keeping the static name rather than passing back the query to the local struct.
Ah, I missed that. Why not use the result= attribute to get the debug info? :)
Good idea, but I'm looking at the CF debugging output, which still shows the actual query object variable name and not the result attribute value. Also, since I var scope the query, I cannot see the result struct outside of the function, and would then have to put a cfdump (our output) inside the function and turn output="true" on the function. I don't like the taste of that.
Maybe there's something I'm missing?
Nope, you aren't. If you want the debugging then you are kinda screwed there.
That's usually my case :) This ones not a biggie, but I'm glad to have my sanity confirmed by a Jedi.
Why don't you care for it Raymond? Just interested. I use var local = StructNew() or var local = {}. It is a great time saver and provides a descriptive way of telling other developers that the variable is local to its method...
Um... I don't know. 8 years ago was a lifetime ago. ;) As it stands, "local" is a real scope now that equals the var scope. CF should have done it this way to start off. Now I'd have no problem using local.x, y, etc since it is just the var scope.
<cfquery name="local.query" datasource="#variables.dsn#">
Works perfectly. I use it all the time...
Err... yes. Because local equals var in newer versions of ColdFusion. Please make note of the age of this post. :)
Sorry. I just noticed this post is 8 years old. I guess if you use, var local = StructNew(); It protects against backward compatibility and just produces a harmless empty structure in CF9+
Point taken. I only just realised how old this post was...
I'm being picky, but my understanding is that var local = structnew() in 9+ won't do anything, it is basically ignored, since local is a scope and is itself the var scope. So technically it isn't making a new struct.
As I said - picky - and I could be wrong.
You are probably right. I guess I am thinking if a client wants to use codebase from a CF9+ project on an older application server, then creating these benign structures might come in useful. But this is pretty obscure.