How Do You Use The ColdFusion CFParam Tag?
Posted March 23, 2007 at 9:17 AM
Yesterday, I wrote a post on how I felt the CFParam tag could be improved (through the addition of a "Catch" attribute). In the comments to this blog posts, it appears that perhaps I am not using the CFParam tag in the way it aught to be used? I use it in ONLY two places: Paraming FORM field values and ColdFusion custom tag attributes.
This is an example of what I would have at the top of a FORM action page:
Launch code in new window » Download code as text file »
- <!--- Param form values. --->
- <cfparam name="FORM.first_name" type="string" default="" />
- <cfparam name="FORM.last_name" type="string" default="" />
- <cfparam name="FORM.phone" type="string" default="" />
- <cfparam name="FORM.email" type="string" default="" />
- <cftry>
- <cfparam name="FORM.user_type_id" type="numeric" default="0" />
- <cftcatch>
- <cfset FORM.user_type_id = 0 />
- </cfcatch>
- </cftry>
I am basically making sure the FORM values exist whether or not I have submitted the form. The CFTry / CFCatch block is there to validate the fact that user_type_id must be a number (as it is most likely a foreign key to the user_type database table).
Similarly, I would have something like this my ColdFusion custom tags:
Launch code in new window » Download code as text file »
- <!--- Param tag attributes. --->
- <cfparam name="ATTRIBUTES.Name" type="string" />
- <cfparam name="ATTRIBUTES.Value" type="string" />
However, it seems that perhaps I am standing alone here? On my other post, people started suggesting using IsDefined(), StructKeyExists(), and IsValid() as all work-arounds to using the CFParam tag. Is that how people are doing things?
How you do you guys use the CFParam tag? Is there a best practices for it?
Download Code Snippet ZIP File
Post Comment | Ask Ben | Permalink | Print Page
Newer Post
Guess The Coin Amount, Win Cool Prizes
Older Post
A Better CFParam Tag With A Catch Attribute (Proof Of Concept)
Reader Comments
I basically do the same thing.
I also use it to combine the url and form scopes together so I can just reference the form scope in my templates. This is something that all the frameworks out there do for you, but since I don't use one, I do it myself
<cfparam name="url.myvar" default="tony">
<cfparam name="form.myvar" default="#url.myvar#">
<cfoutput>
<h1>Hi my name is #form.myvar#</h1>
</cfoutput>
@Tony,
I do something similar on FORM pages that might have an overriding URL value.
Ben,
I use <cfparam> the same way you do, however I never use the type attribute, I will have to start looking into that. I also use them when my pages are looking for certain URL variables like so:
<cfparam name="url.entry_ID" default="0" />
That way if for some reason entry_ID doesn't exist in the URL it will get defaulted to 0 and the user will not get a nasty CF message.
Tony,
I really like your idea! I'll have to look into that as well. I've often wondered how frame works do that. Why do you combine them into the form scope though? Why not just use the variables scope?
Ryan
If you're combining form and url vars, put something like this in your Application.cfm or Application.cfc
<cfset structAppend(form, url, false)>
Then there's no need to use two cfparam tags. Simply use:
<cfparam name="form.email" default="">
I've always liked the way Fusebox combined the url and form scopes into the ATTRIBUTES scope, so that you can also invoke templates via cfmodule or custom tag-style calls, without needing to fiddle with caller scopes.
I basically use the CFPARAM tag as sparingly as possible, ever since discovering way back in (i think) CF 4.0 that it led to potentially huge slowdowns as it searched through all the possible scopes, and I always try to use
[cfscript]
// replace attributes with whatever scope you prefer
if( not structKeyExists( attributes, "varname" ) ) {
// whatever
}
[/cfscript]
instead.
Yeah, I create a REQUEST.Attributes object for that purpose.
I'll sometimes use it to allow parameters from multiple scopes with over-riding capabilities. e.g. in a custom tag:
<cfparam name="URL.PostID" default="0">
<cfparam name="Attributes.PostID" default="#URL.PostID#">
<cfquery name="q">
SELECT * FROM Something WHERE PostID = #Arguments.PostID#
</cfquery>
... allows the consumer of the custom tag to hard-code the postid if they want, otherwise it looks in for it in the url, or else defaults to a default value.
I almost never use it for type validation. I prefer to check that myself, and make a decision based on it, rather than throw an error.
Since you asked... :)
In your example, you have the following:
<cftry>
<cfparam name="FORM.user_type_id" type="numeric" default="0" />
<cftcatch>
<cfset FORM.user_type_id = 0 />
</cfcatch>
</cftry>
If I were attempting the same situation, I would most likely do this:
<cfparam name="FORM.user_type_id" default="0" />
<cfif NOT IsNumeric(FORM.user_type_id) >
<cfset Form.user_type_id = 0 />
</cfif>
This just makes more intuitive sense to me, and I don't have to fire off an error handler. Just my $0.02.
@ryan,
The reason is because I'm already using two scopes on the page by using URL and FROM. Why throw a third into the mix by using the variables scope. Let's hypothetically look at what that code would look like:
<cfparam name="url.myvar" default="tony">
<cfparam name="form.myvar" default="#url.myvar#">
<cfset variables.myvar = form.myvar
<cfoutput>
<h1>Hi my name is #variables.myvar#</h1>
</cfoutput>
When you look at the code above, did I gain anything by using the variables scope? Personally I don't think so, plus I added one more line of code to my template and another layer of WTF?!? is going on :)
@brad
I don't think your idea would work without adding addition security to the template which CFPARAM already does for me. The security I'm talking about is making sure the variable is defined. What you propose just takes care of overloading the form scope with any url scope values. This wouldn't check to make sure that form.myvar exists and if not, sets a value to it.
To use your approach I would have to add to my template:
<cfif NOT StructKeyExists(form.myvar)>
<cfset form.myvar = "">
</cfif>
to make sure that the form variable exists.
Does anyone else agree with this, or am I completely wrong assuming that this would need to be done and in that case I owe Brad an apology.
@Brad,
Forget what I wrote, I TOTALLY missed the end of your comment. Guess my eyes are going bad and everything is starting to run together. Sorry.
I use cfparam all the time. It seems nicer than to isDefined() 20 variables. However, someone mentioned that cfparam is much slower than isDefined().
Is there a comparison chart of isDefined(), cfparam and structKeyExists?
From what I have seen in the past, StructKeyExists() is faster than IsDefined(). But i have never read anything about CFParam performance other than in this thread and my previous one.
I use it completely differently, i create what are local scoped variables, that will either capture url or form data.
<cfparam name="local_username" default="">
if i am tricky, i will use coldfusion to create my cfparam's :)
for example if i have a query, and i want to create a cfparam for every column
<cfloop list="#cfquery.columnlist#" delimiters="," index=column>
<cfoutput>
<cfparam name="local_#column" default=""><br>
</cfoutput>
</cfloop>
i run that before any other code, view source, and i have all my cfparam's, get rid of the extra line break's and there's my cfparam short cut.
@Dmitriy
Readability and Maintainability trumps speed and performance any day in my book. If speed was such an importance than why is everyone moving to write applications in OO and not staying with procedural code?
The amount of performance gain you get by doing StructKeyExists or IsDefined can possibly be measured in milliseconds if that. To me, I'd write 20 lines of code and give up 2 milliseconds then write 60 lines of code.
@Dmitriy
Dave Shuck had a couple good posts on his site about testing isDefined() vs structKeyExists(). Below is the link.
http://daveshuck.instantspot.com/blog/index.cfm/2006/1/30/Benchmarks-revisited
Ryan
You got me thinking...so I wrote my own post about how I use cfparam and why I use it that way. I was curious whether I could actually explain the "why" part. It's not something I've thought about much.
http://musetracks.instantspot.com/blog/index.cfm/2007/3/23/How-I-Use-cfparam
@Ryan
Thanks for the link. Very nice test. Would be nice to see how it's compared to cfparams.
Dmitriy
@Rob,
Nice explanation of your usage.... actually, that got me thinking :)
Data validation is really a worst-case scenario in my case. I use the CFTry / CFCatch around my numeric params because those are usually the only ones that will fail. However, why do they fail? They fail really only if people hack the HTML or mess with the URL values. Otherwise, my systems consistently pass valid values.
So what does this mean? It means the CFParam tag will ONLY throw an exception error IF the user is doing something malicious. At this point, is performance really even a concern? I should say not (why save the user from his own boobery).
Now, this means that 99.99% of my CFParams will NEVER thrown an exception. I wonder if this is performant with IsValid() type logic.
Interesting....
Does this all apply to CF7? Is it any different for CF 6?
Dmitriy
I think this is the same for CFMX 6. I think the only difference in MX7 is that the are more "types" for the CFParam tag (and maybe the min/max/pattern stuff).
One of the FB3 applications that I support uses <cfparam> quite a bit, and in the case of expecting a numeric value I will typically cast the incoming string to a numeric value. For example:
<cfparam name="attributes.myvar" type="string" default="0">
<!--- somewhere in code --->
<cfset somethingelse = val(attributes.myvar)>
I find that this approach works for most situations when the default value is zero. Empty strings and non numeric values will evaluate to zero and in the case of numbers followed by letters (possibly fat fingered) like '123e', will evaluate to the number portion of the string.
I use the <cfparam> tag on my forms so that I can do error checking and keep form values:
<cfparam name="FORM.FirstName" default="">
Then I check the form field for an entry:
<cfif Len(Trim(FORM.FirstName)) LT 1>
<cfset strError = strError & "Please enter an First Name!<br>">
</cfif>
And finally it keeps the form value if submitted, or just leaves it empty if not:
<cfinput type="text" name="FirstName" id="FirstName" value="#FORM.FirstName#" size="32" required="yes" message="Please enter a First Name!" />
i'm trying to work out if I should be using <cfparam> or structKeyExists(..) + validation to check incoming URLS have not been tampered with
I read that in Coldfusion 8 <cfparam> has been dramatically improved - it is now 35 times faster than previous versions
http://www.forta.com/blog/index.cfm/2007/6/27/ColdFusion-8-Performance-Numbers
Does this mean that it is now the best choice to validate user submitted formdata AND to check URL values have not been tampered with
@Paul,
It depends on what exactly you want to do when / if the data was altered. I use CFParam with try/catch for things like numeric data values and pattern-matching data values. If the data was corrupted, my CFParam then throws an exception, at which point I catch it and set default values. But, you might not want to catch the error - you might want it to bubble up to the Application's error handling.
On the other hand, you might not want to deal with exceptions; you might want to use a combo of StructKeyExists() and IsValid(). The question is, if one of those fails, what do you do? Throw an exception? Set default values? Display an error message?
I personally am very happy with CFParam and I have never found performance to be an issue on such a small scale.
I like the explicitness that cfparam provides. Use of isDefined() usually makes maintenance a nightmare as obscure uses of isDefined() essentially make the page nondeterministic. Unfortuately, cfparam is poorly implemented and a big performance hit. For example, the default expression is *always* evaluated, even if it's not used. Thanks for wasting tons of CPU cycles Adobe.
@Chris,
I believe that in ColdFusion 8, the CFParam tag is something insane like *70* times faster. So, at least that is wicked cool.
@tony,
I agree that a lot of people underestimate the value of code readability and maintainability. BUT, there are cases where speed is an issue and you need every millisecond you can get. I usually err on the side of readability, but it all depends on your needs for the app :)




