Posting this little example I’d like to share and I came across in my own (bad) coding that explains why setting variables with explicit scope names is important in cfml, mainly because of lexical scoping and the way cfml gets the holding values from unscoped varibales.
There are already many examples around in blogs, but usually they show security concerns because of the cfml engine searching for unscoped variables in each available scope and using the first hit they find (see more about it in Lucee Docs about Scopes and Lucee’s recommendation about scopes.
This is a simple example that shows how variables can hold and return wrong values when lexical scoping takes effect and variables are not explicitly scoped in CFML.
Take this simple example that uses one variable named and uninitialized variable named `result’ inside a function that isn’t resinitialized.
<cfscript>
public struct function testFunc(
string someArgumentVar required
){
result["someFilename"] = arguments.someArgumentVar ;
return result;
}
filename="abc";
result1 = testFunc( filename );
dump(result1); //dumps abc
filename="cde";
result2 = testFunc( filename ); //dumps cde
dump(result2);
dump([result1, result2]); //dumps wrongly cde, cde
</cfscript>
A better way would be using explicit local scope in functions:
<cfscript>
public struct function testFunc(
string someArgumentVar required
){
local.result={};
local.result["someFilename"] = arguments.someArgumentVar ;
return local.result;
}
filename="abc";
result1 = testFunc( filename );
dump(result1); //dumps abc
filename="cde";
result2 = testFunc( filename ); //dumps cde
dump(result2);
dump([result1, result2]); //dumps correctly abc, cde
</cfscript>
Or alternatively set local scope by using the ‘var’ keyword as mentioned by @Roberto_Marzialetti in his posts below, e.g:
<cfscript>
public struct function testFunc(
string someArgumentVar required
){
var result={};
result["someFilename"] = arguments.someArgumentVar ;
return result;
}
filename="abc";
result1 = testFunc( filename );
dump(result1); //dumps abc
filename="cde";
result2 = testFunc( filename ); //dumps cde
dump(result2);
dump([result1, result2]); //dumps correctly abc, cde
</cfscript>
3 Likes
Thanks for this sharing @andreas!
I will make it read to all the (few ) people who start programming in CF.
PS - I am fond of “var” and I always use it instead of “local.”
2 Likes
Yeah @Roberto_Marzialetti, that’s totally fine because it sets the variable to the local function scope.
2 Likes
You can always use a stricter scope cascading setting, it’s a bit of work initially, testing and finding bugs in perhaps your own code and 3rd party code, but it’s worth it.
Plus it’s also faster
4 Likes
@andreas
Buddy, your worst “bad coding” is 100 times better than my best “used in production” code.
My devotion to tags, and pure CFMX Bible standards is literally 15+ years old
I still cant seem to wrap my head around CFSCRIPT nor Cfbeans.
Oh well, luckily I still have <Cfblink>
1 Like
@Terry_Whitney hahaha, I doubt it , I still have prod (legacy) code using a lot of isdefined() and also Application.cfm all over the place. I think I’m part of the Top 10 Spaghetti Coders Hall Of Fame. But we are getting better every day then, am I right Terry? That’s why I’m sharing all this stuff in my journey to better coding Thx for joining!
yeah, do this in 11 minutes with another programming language!
5 Likes
lol Thats far above my head…
Most of my code is written off examples found in The Coldfusion MX Bible.
lots of tag soup mixed, cfincludes implicitly called vs CFC files, and who needs this MVC stuff, top 100-2000K lines all just quries and then the rest is cfoutput.
I appreciated the cfscript.me site for quickly taking the familiar tag syntax and converting to script when first jumping in. You can’t throw those 2k line pages at it (in my experience) but smaller snippets work well.
2 Likes
Seriously,
What’s wrong with isDefined();
I use it all the time!
I guess have something more to learn : COOL!
Gavin.
1 Like
Always a good reminder of why scoping is important, and I understand the example is contrived to make the point about scoping specifically… but this tangentially related to scoping so will post this here too.
If your function can return a result without creating any variables or processing additional functions you will save on system resources (memory and processing cycles) which is better for concurrency.
Using Andreas example, you could achieve the same results without worrying about scope by directly returning what you need:
<cfscript>
public struct function testFunc(
string someArgumentVar required
){
return {
'someFileName' = arguments.someArgumentVar
};
}
filename="abc";
result1 = testFunc( filename );
dump(result1); //dumps abc
filename="cde";
result2 = testFunc( filename ); //dumps cde
dump(result2);
dump([result1, result2]); //dumps correctly abc, cde
</cfscript>
A more elaborate example (and something I see often) involves formatting data. In this contrived example, both of these functions do the same thing, one with less resources utilized.
<cfscript>
// this
public string function testFunc(
struct someArgumentVar required
){
return serializeJson( [ arguments.someArgumentVar ] );
}
// instead of this
public string function testFunc(
struct someArgumentVar required
) {
var someArr = [];
someArr.append( arguments.someArgumentVar );
var result = serializeJson( someArr );
return result;
}
</cfscript>
If you never have to worry about concurrency then I envy you and want your job lol. But if you do, always try to do the most you can with the least amount of resources while, of course, still being readable and maintainable.
HTH
– Denny
5 Likes