What does it take, to take advantage of the performance gain from ColdFusion 2016's new setting searchImplicitScopes="false":
... bypasses searching for a variable in the implicit scopes thus
finding the variables defined in the application faster.
Should we start scoping Variables & This scopes inside a CFC with searchImplicitScopes="false" in ColdFusion 2016?
I cannot find any documentation on what are considered implicit scopes in CF2016. I am pretty sure local and arguments scopes are fine inside a function, but what about frequently used scopes like variables and this in a CFC?
Scope all the things!!!
The variables scope inside a CFC is global to all functions inside the CFC.
The this scope inside a CFC is global to all functions inside the CFC and can also be referenced from the caller of the CFC.
If you do not scope a variable inside a CFC, it defaults to the variables scope.
<--- this_test.cfc --->
<cfcomponent>
<cfset variables.foo = "This is my CFC global variable." />
<cfset this.bar = "This variable is global to my CFC and can be referenced externally." />
<cfset narf = "Global variable! Point!" />
<cffunction name="getNarf" access="public">
<cfreturn narf />
</cffunction>
</cfcomponent>
Test call:
<cfset test = new this_test() />
<cfoutput>
<li>#test.foo#</li>
<li>#test.bar#</li>
<li>#test.narf#</li>
<li>#test.getNarf()#</li>
</cfoutput>
foo is in the variables scope, you'll get an error: Element FOO is undefined in TEST.
bar is in the this scope, so you get the output for test.bar.
narf is in the variables scope, you'll get an error: Element NARF is undefined in TEST.
The only way to get the value of narf is to have a function return it.
Update
That searchImplicitScopes can turn off CF's scope search capability. So if you don't scope something, it won't look up the hierarchy of scopes. Question is, does an un-scoped variable end up in the variables scope by default? I would say, yes, as that's been the default since CFCs debuted.
Regardless of using that setting, I still say scope everything. The whole reason they created the implicit local scope was because:
developers weren't used to var scoping function local variables
developers that were, tended to do var local = structNew(), so they only had to var scope one variable.
2 also gave them the ability to return the collection of function local variables.
As of CF 9, when var a = 0 became the same as local.a = 0, you could remove all instances of var local = structNew(), so long as you also "scoped" and referenced those private variables as local.a.
The bottom line
is that there is some performance overhead if CF has to look up variable scopes on every request. If you turned off that lookup using searchImplicitScopes=false, you should get some performance boost. But then, that should really rely on your application and average request load.
Just installed CF 2016 Express and I can conclude that with searchImplicitScopes="false", the Variables scope is still being searched when a variable is not scoped, but not the This scope.
Related
I have been reading about the CF Scopes, and am comfortable with the CFC scopes and their implications (detailed here), however, whenever I search for CF scopes it almost always references in the context of a CFC - So I was hoping for some clarification around scopes in CFM pages. I am working with CF 9/10 so only really interested in how the scopes behave in these releases.
What scopes are available on a CFM page - do CFM pages have the same concurrency problems as can happen elsewhere, or are the variables scoped on a CFM page bound to the scope of that specific request?
If I include the line <cfset myVar = 10 /> in a CFM page , which scope will it be included in? is there any risk of either other users on the same page accessing the variable or other cfm pages accessing it?
Thanks
Almost all the scopes except 'THIS' are available in the CFM pages.
unscoped variables declared in CFM page can be called directly or can be called with VARIABLES scope prefix.
eg:
<cfset varA = 'someValue'/>
can also be written as
<cfset VARIABLES.varA = 'something' />
To my Knowledge, unless you create a singleton (only possible for CFC's) and put it in Application scope, you never risk sharing variables with other users. This is also valid if one is not careful about scoping the local variables properly in the CFC functions.
On a CFM page, each user request has its own processing thread and never gets crossed with other user request. So, the variables are bound only to the scope of that specific request.
if you want a variables to be used by all the users requesting a page, you can put that in the APPLICATION scope.
I did not quite understand your second question. if you can elaborate on it, may be i can add more to my answer.
Update
This code will help you answer the 2 questions.
<cfscript>
function a(){
_a = 20;
WriteOutput("Inside function:"&variables['_a']);
WriteOutput("Inside function:"&variables['_b']);
}
_b = 30;
a();
WriteOutput('outside function:'&variables['_a']);
</cfscript>
Output
Inside function:20Inside function:30outside function:20
This page, gives a good explanation of the available scopes.
If you look hard enough, you will find more information about what happens if you don't scope your variables. The gist of it is that your code will run successfully, but less efficiently. The reason is that ColdFusion will attempt to find the correct scope. It checks certain scopes, in a specified order. That order is somwhere, I just couldn't find it quickly.
For your second question,
<cfset myVar = 10>
puts the myVar variable into the variables scope.
Regarding one user changing variables that affect other users, I believe the only scope that is at risk is the application scope. However, with modern browsers, it is possible for a single user to mess up his own session variables. I've seen it done.
Another way that variables might be inadvertently changed is with functions. If you want to keep your variables local to the function, you have to use the var keyword when you instantiate them. In later versions of CF, there is a local scope that accomplishes the same thing.
Personally, I scope all my variables except for the variables scope.
I have this line of code:
<cfset variable.currentCategory = 0>
I want to know what the `variable. part is. Does this have something to do with session variables? I have seen 'session.' before in CF, but not this. I am working with a huge amount of code and CF is not my wheelhouse. I can't tell if this is something with ColdFusion, or something defined elsewhere in the code that I just haven't found yet. Due to the nature of the question, it has made it very hard to look for an answer on google (try googling coldfusion variable lol). Thanks.
Edit
This line is specifically 'variable' and not the 'variables' scope. I think it has something to do with scopes, but I don't know what.
It has nothing to do with the variables scope or the session scope. It is a variable names "variable" that contains a struct. It is possibly a typo, or possibly a very poorly named variable.
There are several scopes available in ColdFusion, variables and session being just two of them. Every variable you create in CF exists in a scope. The variables scope is the default scope used. So if you do <cfset foo = "bar" /> this is the same as <cfset variables.foo = "bar" />.
You can find more about ColdFusion scopes in the ColdFusion Documentation: ColdFusion 9, ColdFusion 10
You can define values in the session scope like you said. You can also define them in the variables scope, which is probably what they were intending to do here. What they've really done is create a new structure called variable. So if below that line you do <cfdump var="#variable#"> it will dump out that entire structure. My guess is they messed up and meant to type variables.
What is be the best way to define method level variables scope?
Some ColdFusion documents say it should be done like this:
<cfset Var testVariable = "this is a local variable">
But others, do it like this:
<cfset LOCAL = StructNew() />
<cfset LOCAL.testVariable = StructNew() />
Which way is better?
Either will work.
In CF9 (and I assume 10) var local = structNew() is unnecessary (though harmless and makes your code backward compatible) as there is within each function a local scope that contains any locally scoped values. Also with CF8 and earlier these variables will need to be the first thing declared in a function.
So to specifically answer your question I prefer the var local = structNew() (or just var local={}) because:
It means that locally scoped variables are obviously such (var
scoping issues can be a pain to debug).
You can dump or examine the
local "scope" in cf 8.
The scope evaluation order is well known/documented when using variables. However I can't find any information about the scope evaluation order when setting a variable.
One would presume it's the same list, but there appears to be a few caveats as demonstrated here:
<cfset qryChain = queryNew("id,next")>
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
<cfset Next = StructNew()>
<cfset Next.id = qryChain.next>
</cfloop>
The above code is trying to reuse a variable name that it shouldn't, but fails in an unexpected way.
Since the cfsets are inside a query loop, item 4 of the scope evaluation order should be used for both. Instead Next is being evaluated as Variables.Next (item 6), and then Next.id is being evaluated as Variables.qryChain.next.id (item 4) and fails.
Is this documented anywhere? Is it simply items 1-6 of the "using" list above with a few caveats? Are these caveats intentional, or bugs? What other caveats are there?
I think I understand what is going on here. The behavior you are seeing is from scope searching when accessing variables in order to set them. When you set a variable without scoping it, ColdFusion will search the scopes to see if that variable exists anywhere first, and if it does, it will set it there.
In your first example:
<cfset qryChain = queryNew("id,next")>
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
<cfset Next = StructNew()>
<cfset Next.id = qryChain.next>
</cfloop>
When you are creating your "Next" variable it is actually placing that variable into the VARIABLES scope, you can prove that if you dump the variables scope at any time during the looping process. You will see a "Next variable" with an empty struct.
The problem is in the next line. When you try to ACCESS the Next variable to set a new key into it, ColdFusion is finding the Next variable that exists in the Query result first, because while looping a query the Query scope (not really a scope, but it works like one in this case) has a higher precedence than the Variables scope. That variable does not contain a struct, so you will get an error about how you are referencing it.
Scope searching is happening, but it is not really while setting, it is while accessing in order to set.
Here is a working example to demonstrate this.
<cfset qryChain = queryNew("id,next")>
<cfset queryAddRow(qryChain, 3) />
<cfdump var="#qryChain#">
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
<cfset Next = StructNew()>
<cfdump var="#variables#">
<cfset Next.id = qryChain.next>
<cfdump var="#qryChain#">
</cfloop>
in this example, I show that after you create the next variable it does exist in the variables scope, but when you immediately try to set a key into it without scoping that access you will instead get the NEXT variable from the current record in the query. This is only happening because the query happens to have record with a column name that happens to match the variable you are trying to use.
So why doesn't ColdFusion try to set the StructNew() into the Query scope (pseudo-scope)? The query cannot be manipulated using dot notation. Again, it is not really a scope. So in this sense it is read-only and is being skipped over
To manipulate a query resultset in CF you must use query functionsVARIABLES scope. Because unscoped variables are always placed in the VARIABLES scope. In the line where you try to set the id however, there is another phase going on in the background where it needs to access the variable in a read capacity first, and then try to perform the set. In this case, it DOES find the NEXT variable in the query because scope searching will occur first to determine in NEXT exists to set that key into and then when you try to set something to it, it fails.
As for your second set of examples, this is expected behavior and is easily explained.
In the first example, you are varing your variable (which places it in the local scope). You are then setting the value of that variable. When you set a value to a variable (without scoping it) ColdFusion checks to see if that variable already exists anywhere (so it does a scope search, again this is accessing at this point, not setting) it will find it in the local scope and then set the value there. After that you set the value again, this time properly scoping it, so no searching is done.
In the second example, you are not varing the variable when it is initially set, it does not exist anywhere, and so it is set to the variables scope. If it had already existed in the local scope, then ColdFusion would have found it and set it there (as in your first example), but since it did not exist, and it was not var'd it was set to the variables scope.
Finally, in your last example, you explicitly scope your variable and so it will be set in the local scope. You then set it again without scoping it. ColdFusion will find it in the local scope and overwrite it.
The moral of the story is, scope your variables. I tis important to get expected behavior. Scope-searching was never a good idea, but unfortunately, it is here to stay. I don't see anything here that I would call a bug, or even unpredictable behavior if you understand the way scope searching works.
Scope evaluation during assigment
I am aware of two different methods of scope evaluation when creating variables in ColdFusion. I haven't tested every possible instance, but this is how it should work.
The first instance uses the full list of scopes in evaluating unscoped variables. This is used by cfparam when creating variables. If ColdFusion does not find a variable with the given name then it will create it in the Variables scope.
The second instance uses the first 6 scopes in evaluating unscoped variables and then if unsuccessful will also create the variable in the Variables scope. This is employed by cfset and any other tags that creates variables, such as cfhttp with the result attribute, and cfsavecontents variable attribute.
As you observed there is the odd "sometimes ignore" the query scope issue. I would classify that as a bug, but someone may yet be able to provide a reason why the exception needs to be made.
Hoisting
Although ColdFusion was designed to copy JavaScript in a lot of ways (specifically cfscript) there is a subtle deviation which I haven't seen documented. With regard to functions (both script and tag), JavaScript employs hoisting, whereas ColdFusion does not.
Hoisting is the process of automatically moving the declaration of a variable to the top of the function, while maintaining the code placement of the assignment of the variable. The means that the scope of a variable will not change in JavaScript, but it can in ColdFusion.
Prior to CF9 the var keyword had to be used at the top of the function, essentially negating the need for hoisting. This is different from JavaScript, where var can be used anywhere in a function and employs hoisting. With CF9 ColdFusion adopted the declare anywhere philosophy, but neglected to implement hoisting.
In both the following examples JavaScript would only be dealing with a single scope, with x being function local.
<!--- sets variables in 1 scope --->
<cfscript>
var x = 0;
x = 1;
local.x = 7;
</cfscript>
Compared to:
<!--- sets variables in 2 scopes --->
<cfscript>
x = 1;
var x = 0;
local.x = 7;
</cfscript>
To avoid the potential pitfalls created by the lack of hoisting you can either var only at the top of the function, or do things as many have prior to CF9 and declare a var structure at the top of the function and prefix all variables with that (remembering to not name it local). e.g.
<cfset var localVars = StructNew()>
<cfset localVars.x = 7>
<cfset localVars.y = 1>
var vs local
In functions var appears to be a second class citizen to the local scope. If you try to set a local variable to the same name as an argument using var you will receive an error message saying Use local to define a local variable with same name. despite var and local supposedly being equivalent.
More
There may be additional caveats and bugs, however I'm not aware of there being any documented cases.
You're getting this error, presumably (since it's the error I received):
You have attempted to dereference a scalar variable of type class coldfusion.sql.QueryColumn as a structure with members.
This is because when you are within a cfloop for a query, the query scope takes precedence over the variables scope when evaluating a scope-less variable. However, when you are assigning scope-less variables (either within a cfloop query context or not) then you use the variables scope. So when you assign
<cfset Next = StructNew()>
You are setting the "Next" variable to the variables. scope. However, when you attempt to evaluate Next (by referring to its key, .id), like so:
<cfset Next.id = qryChain.next>
Now you are getting Next from the query scope (and since Next in the query scope is not a structure, you get an error).
So, to try to answer your main question - when you are assigning a variable with no scope specified, CF will put the variable in the variables scope. When you attempt to evaluate a variable with no scope specified, then it will use the normal scope-precedence rules to find a match (in this case, finding one in the query scope).
In a ColdFusion Component (CFC), is it necessary to use fully qualified names for variables-scoped variables?
Am I going to get myself into trouble if I change this:
<cfcomponent>
<cfset variables.foo = "a private instance variable">
<cffunction name = "doSomething">
<cfset var bar = "a function local variable">
<cfreturn "I have #variables.foo# and #bar#.">
</cffunction>
</cfcomponent>
to this?
<cfcomponent>
<cfset foo = "a private instance variable">
<cffunction name = "doSomething">
<cfset var bar = "a function local variable">
<cfreturn "I have #foo# and #bar#.">
</cffunction>
</cfcomponent>
It won't matter to specify "variables" when you create the variable, because foo will be placed in the variables scope by default; but it will matter when you access the variable.
<cfcomponent>
<cfset foo = "a private instance variable">
<cffunction name="doSomething">
<cfargument name="foo" required="yes"/>
<cfset var bar = "a function local variable">
<cfreturn "I have #foo# and #bar#.">
</cffunction>
<cffunction name="doAnotherThing">
<cfargument name="foo" required="yes"/>
<cfset var bar = "a function local variable">
<cfreturn "I have #variables.foo# and #bar#.">
</cffunction>
</cfcomponent>
doSomething("args") returns "I have args and a function local variable"
doAnotherThing("args") returns "I have a private instance of a variable and a function local variable."
Especially in CFCs, proper scoping is important. The extra 'verbosity' is worth the clarity.
Having variables slip out of their indended scope will cause severe problems and very hard to diagnose.
Verbosity isn't always a bad thing. We name our functions and methods in descriptive manners like getAuthenticatedUser(), rather than gau(). Database columns and tables are best left descriptive like EmployeePayroll rather than empprl. Thus, being terse might be 'easier' when your short term memory is full of the project details, but being descriptive shows your intent and is helpful during the maintenance phase of an application, long after your short term memory has been filled with other stuff.
I'll say Yes. Is it explicitly necessary? Nope. Can you get away with not doing it? Sure. Are you asking for trouble? Absolutely. If you have the following inside a cffunction:
<cfset foo = "bar" />
That will not place that variable in the function local var scope, it will place it in the CFC's global VARIABLES scope, meaning that it is available to every method of that CFC. There are times when you may want to do this, but most of the time you'd be asking for a race condition.
When any variable is being read by the server, if that variable is not explicity declared as part of a scope (REQUEST., SESSION., etc.) then ColdFusion will run ScopeCheck() to determine which scope the variable is in. Not only is this placing unnecessary overhead on your application server, it also introduces the ability for hijacking, whereby your variable is in one scope, but ScopeCheck() has found a variable of the same name higher in the precedence order.
Always, always, ALWAYS, scope all variables. No matter how trivial. Even things like query names and looping indexes. Save yourself, and those that come behind you, from the pain.
The short answer to your question is that no, you will probably not run into trouble attempting to do that. Outside the context of a UDF (even still inside a CFC), an un-scoped set statement implies the variables scope.
In addition, in a CFC, the Variables scope is available to all of its functions; it is sort of the global scope within that CFC -- similar to the "this" scope, except variables scope is akin to "private" variables, whereas the this scope is akin to public variables.
To test this, create test.cfc:
<cfcomponent>
<cfset foo = "bar" />
<cffunction name="dumpit" output="true">
<cfdump var="#variables#" label="cfc variables scope">
<cfdump var="#this#" label="cfc this scope">
</cffunction>
</cfcomponent>
and a page to test it, test.cfm:
<cfset createObject("component", "test").dumpit() />
And the results will be:
Now, to address another problem I see in your example code...
In CF, all User Defined Functions have a special un-named scope commonly referred to as the "var" scope. If you do the following inside a UDF:
<cfset foo = "bar" />
Then you are telling CF to put that variable into the var scope.
To compound things a bit, you can run into problems (variable values changing when you weren't expecting them to) when you are not using the var scope in your inline UDFs.
So the rule of thumb is to always, Always, ALWAYS, ALWAYS var-scope your function-internal variables (including query names). There is a tool called varScoper that will assist you in finding variables that need to be var-scoped. Last I checked it wasn't perfect, but it's definitely a start.
However, it is a bad idea to reference (display/use) variables without a scope (obviously excepting var-scoped variables, as you can't specify the scope to read from) in CFCs or even on your standard CFM pages. As of CF7, there were 9 scopes that were checked in a specific order when you read a variable without specifying the scope, first match wins. With CF8, there could be more scopes in that list, I haven't checked. When you do this, you run the risk of getting a value from one scope when you are expecting it from another; which is a nightmare to debug... I assure you. ;)
So in short: implying a variable's scope (on set) is not a terrible idea (though I usually specify it anyway); but inferring variable's scope (on read) is asking for trouble.
Not explicitly scoping in the variables scope may work, but it's not a good idea, and honestly the only reason not to is out of laziness IMO. If you explicitly scope everything 1) you avoid potential issues, and 2) it makes the code easier to read because there's no question which scope things are in.
To me it doesn't make the code more verbose (and certainly not unnecessarily verbose)--it's actually easier to read, avoids confusion, and avoids weird side effects that may crop up if you don't explicitly scope.
The simple answer to your question is:
"NO, it isn't necessary"
However, I think best practices would suggest that you do, in fact, use the variables indentifier when accessing those variables. In my opinion anyone who comes upon your code in the future, and is looking in the middle of a function, will instantly know the scoping of the variable without having to scan the top of the function the local functions.
In fact, I add a little extra verbosity to my CFC UDFs by creating one local struct:
<cfset var local = structNew() />
Then I put all my local vars in that struct and reference them that way so my code will look something like this:
<cfset local.foo = variables.bar + 10 />
After reading your answers here's what I'm thinking:
Yes, it's safe. In general, it's not necessary or useful to explicitly specify the variables scope. It just adds clutter to an already verbose language.
Granted, there is one minor exception, as Soldarnal pointed out, where qualifying a variables-scoped variable is required. That is if you have a function local variable with the same name. (But you probably shouldn't do that anyway.)
Best practices aside, i believe it could also depend on how your going to access your cfc's i have not had any problems leaving them out when creating objects and accessing them from coldfusion. However i think it might be needed when accessing and/or mapping them remotely via actionscript in flex/flash.
Here's a very good CFC scope reference from Raymond Camden.
Personally, I prefer to make a 'self' hash to avoid all confusion (notice I don't use the 'variables' scope in the functions):
<cfcomponent>
<cfset variables.self = structNew()>
<cfscript>
structInsert(variables.self, <key>, <value>);
...
</cfscript>
<cffunction name="foo">
self.<key> = <value>
<cfreturn self.<key> />
</cffunction>
...