Dale asks about var scoped variables:
I think the answer is no, but it seems strange. All variables go into a scope somehow.But if I declare 5 variables as var within a function, coldfusion knows that they are only for that function, but how can I get to them? Is it possible to dump all var variables for a given function? Where are they hiding, are they a struct withing some other scope.
If I dump all the scopes, variables, this, sesssion etc would they simply not exist.
I normally use <cfset var local = structNew() />
Which gets around this, but really wondering where the variables live.
You are correct. Var scoped variables are "special" and currently there is no way to enumerate them in ColdFusion. That is exactly why some people use a local struct as you have described above. Yes, ColdFusion does know about them, and yes, it is a bit odd that they don't have a proper scope, unlike every single other variable out there.
Personally I don't like using a struct for my var scoped variables. It just seems like too much to type. Of course the flip side is that I'm typing more var scopes. I also kind of like seeing the var scope lines as it helps remind me what is going on inside my function.
Archived Comments
I like using a "scope" for vars and use private (local is a keyword in query-of-queries) so start most functions with:
<cfset var private = {}> (replace {} with StructNew() for pre-8)
and then use the private structure for all my var variables. To me this is a) consistent with how CF works and b) leads to less chance of having variables in cfcs that are not in the var scope and c) means I can create a new var variable half way through a function.
You can dump the var scope with this:
<cfdump var="#getPageContext().getVariableScope()#">
I wouldn't consider the dump you used as valid though since it isn't actually documented. I'm sure it works - but I'd be wary of pushing production code with anything that wasn't _supposed_ to work (ie, documented). That being said, I love your private = {}. I obviously new about short hand notation for structs in CF8, but it never occurred to me use them for _blank_ arrays or structs. Nice!
@Sam, I really wouldn't have CF create a struct for each and every function and for each and every invocation of that function. You realize a struct is a heap-based java object that has to be created and released each time, correct?
Further, I don't see how you have "less chance" of a variable not being in VAR scope. Forget to VAR a cfquery name and your query will still hit the cfc's scope, added struct or not.
From a cumulative performance standpoint, this is a bad thing.
Yeah, I agree with your thoughts about undocumented features and it being valid. Though, really cfdump should not be used in production IMO.
The private var scope idea I read about in the comments of this entry (see Matthew Lesko's and Todd Sharp's comments):
http://www.firemoss.com/blo...
I use cfdump in production - but only for error emails. It is handy to have a dump of all the data for the current request.
@Michael -- My tests show there is no real time difference when using a stucture (I'll post the test on my blog tomorrow). Still, I do only use a private structure when I need it.
As for less chance if you have a big function, say, 50 lines plus, and you're half way through it and working on a loop, it is much easier to add cfloop index="private.i" than it is to do index="i" and go to the top of the function and do cfset var i = 0. Thats all.
@sam, Still don't buy the "less chance" argument. Yes, you just saved yourself ten seconds... but at the expense of a minor performance hit server-side each and every time the function is called from now on. Those hits are small, but implement them as a pattern throughout the system and sooner or later they start to add up. And usually just when things start to scale.
And I take it back about saving ten seconds, as you now need to type "private." in front of every variable reference. Not to mention the fact that if you now FORGET to type "private." in front of some variable name you've now blown the VAR scope again, but for a different reason.
Just a bad coding practice in my book.
@Ray: Ah, yeah, good point. I do that as well. Very handy.
@Michael
I don't use the private or local convention myself, but I've tempted to consider adopting it, precisely because it allows for all of your variables "looking" scoped. If you have any variable that has less than 2 words, you know it wasn't scoped correctly.
And I think you've posted on another blog about some other programming concern where someone was using structs, and you warned about performance.
First: most people are not scaling to the point where performance (outside of really poorly designed algorithms) really matters. If the struct helps to design cleaner code, eliminating it for performance is premature optimization.
Second: adding a struct really doesn't add that much in way of processing. I ran a very basic test (I know -- looping is a bad testing mechanism). But creating a million structs seems to take around 11 seconds or less on average. So, that's around 0.01 milliseconds per struct. Only Google, Amazon, or Ebay really needs to worry about shaving off that kind of time.
Michael --> It may seem like a bad practice but it's to deal with the bass ackwards way CF approaches scoping for variables (local, global; not CF's "scoping" which isn't scope but something else). CF screwed it up and now we're just trying to deal with the mess. I also disagree with the idea that it's a performance hit that actually matters. It's like the old overly academic argument that you should only use CFOUTPUT around exactly what needs to be generated. I mean, come on, CF variable scopes are just structures in themself. It's doing it all the time. This time it just limits that struct to the method call. What's the diff between 5 freestanding variables and a struct with 5 keys? If there actually is a difference in perforumance I can't imagine it as anything other than nearly negligible. And, to step back from it, OO is well know as creating more overhead... so if you're that worried about performance just stick to the old school procedural CF stuff then. I've written apps with a few thousand concurrent users that, for example, have trim() EVERYWHERE and it didn't cause any issues. I don't see how a few extra structs should be any different than an extra 5, 10, 30 trims.
---> "VAR a cfquery name" : Does anyone have an example of how to do this? I haven't seen it before. Thanks.
@Allen - its simple. If the query is named foo, you just do var foo = "".
Thanks! I was off in la-la land trying to find an attribute in the cfquery tag and trying <cfquery name="var qName"... Just set it right before the query and you're set. Sweet.
There is an undocumented feature to gain access and even mutate local scopes without the "var local" method. It uses getPageContext.getActiveFunctionLocalScope(), getPageContext.popActiveFunctionLocalScope(), and getPageContext.pushNewFunctionLocalScope(). These are undocumented, but with function names like these, do you really need documentation?* I've even written a function that allows var to be used anywhere in a function (well, not exactly, but you can type for (var("i",1); i LTE someval; ++i) and it creates a local instance. I posted it to Ben Nadel's blog here:
http://www.bennadel.com/blo...
*There is one caveat described in the blog. If you are poping FLSs, then you have to be careful of errors or use a try catch block around possible errors so you can restore the popped FLSs before allowing the error to be rethrown. The stack trace pops FLSs to get its information, and if one is missing, you don't get a stack trace, but just an error pointing at the top level function call.
This blog entry was written before CF9 which now has an explicit Local scope. Now you can just introspect that.
Yes, I am aware of CF9's local scope, but at the moment, I'm still unable to upgrade for several reasons. Anyway, there is another use I have used these functions for that I'm not sure CF9 allows. Maybe you could test it for me and see?
I have written a cfargument function which is similar, but slightly different from the regular cfargument tag. This one has the same options, but it can be used anywhere in a function to allow for overloading a function call. Say you have a getInput function that takes one known parameter (input type) and switches based on this type to determine what variables should have been passed in under which names or at which positions within Arguments. Under the hood it does type checking and handles everything similar to the cfargument tag, but can overload the definition at run-time.
Anyway, the way it works is it pops the FLS to get at the caller's FLS without the need to pass in a reference to it. Is there a way to do this with CF9, such as getCallerLocalScope()?
Um - think you went a bit above me here. ;) I normally just treat the arguments scope as a struct, so if I have to do anything dynamic with it I just use struct functions.