Chris asked:
Recently I had to outsource some CF work as I was under the gun and buried in deadlines. I noticed something in the programmer's code that I had never tried before and never new was possible. What I am looking for is the validity in actually doing it.
The page a was a simple search results form. It took in the search query from either a form using the post method or a url link. However the programmer used code such as this ..
<cfparam name="town" default="">
<cfparam name="city" default="">
and then queried using a simple SELECT * WHERE town='#town#' OR city='#city#'
My question is this - what are the good bads and uglies of using the variable as is and not going through the usual
<cfparam name="url.town" default="">
<cfparam name="url.city" default="">
<cfparam name="form.town" default="">
<cfparam name="form.city" default="">
blah blah blah.
Should I use the seemingly easier way? I like it for sure. But will it bring the dark side down upon my site?
There's a couple of things going on here but I'd like to begin by pointing one thing out. It's a bit off topic from the rest of the conversation but it's probably more important. In your example query you used:
SELECT * WHERE town='#town#' OR city='#city#'
This may have just been Chris writing something quick in an email, but if it really is the code being used then the 'bare' variables need to replaced with cfqueryparam tags as soon as possible. Again - I'm hoping that was just for the email and not the real code - but every time I see SQL like this I speak out. Ok, so let's move on.
What you are seeing is 100% documented expected ColdFusion behavior. When ColdFusion encounters a variable and the scope is not defined, it checks a predefined list of scopes in order to find the variable. From the documentation:
If you use a variable name without a scope prefix, ColdFusion checks the scopes in the following order to find the variable:
Local (function-local, UDFs and CFCs only)
Arguments
Thread local (inside threads only)
Query (not a true scope; variables in query loops)
Thread
Variables
CGI
Cffile
URL
Form
Cookie
Client
And then immediately after the docs go on to say:
Because ColdFusion must search for variables when you do not specify the scope, you can improve performance by specifying the scope for all variables.
I think right there you will find the main reason people don't use unscoped variables. However, I'd probably say the performance penalty is not going to matter for 99.99% of us. In fact, I typically leave the scope off of Variables scoped variables.
I'd say the other reason folks avoid non-scoping (is that even a word/phrase?) is for security reasons. Imagine the following example. (Note - I'm writing this off the top of my head so pardon any typos.)
<h2>Search Page</h2>
<cfif isDefined("search")>
You searched for <cfoutput>#search#</cfoutput>
</cfif>
<cfset results = mycfc.search(search)>
<cfoutput query="results">
etc
</cfoutput>
So note the search variable. You expect this to be form or URL. ColdFusion checks the URL scope first. I could create a link that includes HTML and possibly script elements and send that link to you so that when you click it, you see my 'bad stuff' within the context of the page itself. Changing the code to a more specific form.search or url.search can help prevent this type of issue.
I hope this helps. By the way, while looking for the docs on the order of scope look up, I noticed these two pages that I thought were darn good examples of ColdFusion scopes. I recommend reviewing these or sharing them with new coders. They do a great job of enumerating and describing all of the scopes.
Archived Comments
Where does the Application scope fall in to the search order - First or Last?
It doesn't. :)
Hit submit too early. If you look at the -very- last link, the second column in the table says if the prefix is required. it is for Application.
Nice explanation Ray, thanks.
I never used to scope variables, didn't realize it was an issue. I forced myself to start doing it recently and now I scope everything out of habit as I go. Thought it'd be a nuisance, but it's just become part of the process.
Good post... basic CF knowledge, but very important to know (and be reminded of, in my case).
In such case whenever I need to use URL and FORM scope. I always append FORM variables to URL using structAppend(URL,FORM) at top of the page and in rest of page I will use URL scope variable.
Till now it work fine for me.
Recently had an example come up at work where scoping improved a bit of code by an incredible magnitude--can't remember the precise metrics off the top of my head, but it made the difference between an unacceptably slow page and something that now zips along like nobody's business.
Definitely a good habit to adopt, for new and old devs alike :)
I've done this before, which I kinda like... I used it when I have a page that uses URL variables, but also handles the same variables in the Form scope:
<cfparam name="URL.param1" default="">
<cfparam name="URL.param2" default="">
<cfif IsDefined ("Form")>
<cfset StructAppend (URL, Form, true)>
</cfif>
... then I continue writing the code using the URL scope.
Pritesh - didn't mean to steal your thunder!!! I missed your post on my first read. Glad to know this technique is used by others.
I prefer using Request Scope.
In my App.cfc i usualy do:
public boolean function onRequestStart(){
structAppend(request, url, false);
structAppend(request, form, true);
some other stuff
}
So i can use request.search no matter if search was send in by form or url.
Great to know this as some of our old code does not use this scoping. However if i had a custom tag returning a 'caller.var' variables how would I reference it in my calling page? as 'var' or do I do <cfset variables.var = var />?
Thanks
Not knowing where your variables are coming from is very bad! There are security issues with every method described above. The php community has delt with this in the appropriate manner.
http://php.net/manual/en/se...
They have removed the ability to not scope the variables.
@Richard: In a custom tag, caller always refers to the immediate parent's Variables scope. So caller.x == variables.x in the parent.
Hi Ray,
I'm planning on creating a summary that shoukld address _all_ the issues above as per my blog post: http://www.niallodoherty.co...
Sometimes it's good to "Get back to the Basics" for a refresher ;-)
It appears then that the following scopes do not get checked:
Application
Session
Request
I blogged about "cfparam with unscoped variables" a few weeks ago http://henke.ws/post.cfm/cf...
I've written a couple of blog posts about variable scoping.
http://www.cfgears.com/inde...
http://www.cfgears.com/inde...
Not properly scoping variables is a pet peeve of mine. (and yes, Ray, I mean the variables scope too!) ;)
I'm currently maintaining a legacy site that does not have any scoped variables. Its maddening to debug as variables are named the same across the different scopes and any scoping I add often breaks functionality as the code is dependent on CF's ability to find the currently defined version of the variable in the various scopes. Its a fight not to re-write the site from scratch as it goes against every best practice I know.
@Josen
AFAIK - When ColdFusion is evaluating a variable - it doesn't include Application, Server, Session, Attribute, Caller and Request as these must _always_ be scoped.
Scoping vars makes code easier to read as well.
A project I was on a few years ago used a legacy, home-grown framework that RELIED on unscoped variables. It would, literally, break pages if I used "variables.foo" instead of "foo". To provide myself a way to make copies of variables and properly scope them while not brekaing the "framework", I ended up writing a function that basically worked like this:
variables.myFoo = getVariable( "foo", "default" )
that would search the different scopes looking for a "foo" and if it couldn't find one, it would use my default value. it also had optional parameters to check other things like a query that might also contain the variable I needed, and a "force override" that let me do things like "if foo happens to live in multiple scopes, then use the one in some specific scope, regardless of the order that CF checks things.
...it was either that, or force myself to leave EVERYthing un-scoped, lest the framework break...and i just couldn't bring myself to do that. :)