Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

How Do You Use The ColdFusion CFParam Tag?

By Ben Nadel on
Tags: ColdFusion

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:

  • <!--- 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:

  • <!--- 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?

Tweet This Deep thoughts by @BenNadel - How Do You Use The ColdFusion CFParam Tag? Thanks my man — you rock the party that rocks the body!



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>

Reply to this Comment

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

Reply to this Comment

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="">

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

@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 :)

Reply to this Comment

@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.

Reply to this Comment

@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.

Reply to this Comment

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?

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

@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.

Reply to this Comment

@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....

Reply to this Comment

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).

Reply to this Comment

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.

Reply to this Comment

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!" />

Reply to this Comment

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

Reply to this Comment

@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.

Reply to this Comment

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.

Reply to this Comment

@Chris,

I believe that in ColdFusion 8, the CFParam tag is something insane like *70* times faster. So, at least that is wicked cool.

Reply to this Comment

@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 :)

Reply to this Comment

Hi Ben,

Would you happen to know what the CFSCRIPT equivalent of CFPARAM is for CF8? Google only gives me hints to lots of UDF's en CFC's that work around the problem (mainly if not isdefined then do this else do this). In the livedocs for CF8 (which I'm on) I cannot find anything either...

Thanx up front ;-)

Reply to this Comment

@Sebastiaan,

I don't think CF8 has a script-equivlanet to that, other than using structKeyExists() and an IF statement. The CFScript version was added in CF9.

Reply to this Comment

cfparam doesn't seem to work with the session scope (using CF7)... it will test that it exists but not carry a default value

Reply to this Comment

@Shaun,

That's odd; I can't think of a reason why it wouldn't? Doing this:

<cfparam name="session.val" type="string" default="hello" />

... should default session.val == "hello". Does it throw an error?

Reply to this Comment

@Shaun,

Just to make sure we are on the same page, you understand that the default value only gets set if NO value exists already? For example, if you have:

<cfset session.foo = "bar" />
<cfparam name="session.foo" default="blam" />

... session.foo will equal "bar", NOT "blam".

Just want to make sure we are having the same conversation.

Reply to this Comment

@Ben Nadel, yes I understand that.. however I guess I wasn't clearing all the session data when testing and it had previously been set to "" duh! .. after clearing the session data and refreshing it works... thanks for you time

Reply to this Comment

Your information on the <cfparam> tag has been extremely helpful. Have been converting all of our form validation from Adobe Developer Toolbox. Recently ran across a problem with <cfparam> on a textarea and reaching out for help if anyone has run across this.

Have a text area that is a description of a tour offered by our company. For validation, it's set as:
<cfparam name="FORM.tourdescription" type="string" default="#getdesc.tourdescription#"/>

The data is stored in the database as: <p>This is a test</p>

In outputing #form.tourdescription# in the body of the page, the data appears as: <p>This is a test</p>

This is causing a problem with the rich text editor on the field. Have used the replace function, but not working.

Anyone have thoughts?

Reply to this Comment

@Alex,

I am not sure why the text would be causing the rich editor to error. Perhaps the data needs to be escaped. Try using the htmlEditFormat():

#htmlEditFormat( form.tourDescription )#

That will escape the open/close brackets. See if that helps the rich editor.

Reply to this Comment

Thought I'd throw in my 2 cents on what's been puzzling me for several days on using cfparam...

This pertains to a form posting to an action page where you would normally use form.url but I thought I'd try and use cfparam and cfset to try and make the form.variable persistant.

The form itself has a dynamically loaded select with number values. The form posts to an action page which contains a cfquery that is an inner join of two tables. The form select value is inserted into the query in the Where clause which determines the fields to pull from the database. No problem so far...

Now. Here's where the problem starts. I want to only show 40 (database fields) rows on the page and dynamically build Next and Previous pages. Catching my drift on this now?

The action page displays just fine when it first loads - 40 rows of the correct fields and the CF NextN builds the url link to the next 40 rows...

Since the form select variable(s) only persists when the action page first loads - when the Next 40 url is clicked CF cannot find the form.variable... The Next 40 url is basically just a page refresh, so, there is no form.variable to load in the query...

The action page is set up as follows (disregard not having query security for the moment - just trying to experiment with basic code):

**********

<!--- Start displaying with record 1 if not specified via url --->
<CFPARAM name="start" default="1">

<!--- Number of records to display on a page --->
<CFPARAM name="disp" default="40">

<!--- Value of param default for form select value--->
<CFPARAM name="form.MyDropdown01" default="">
<CFPARAM name="form.ThisNumber" default="">

<cfif isdefined ("form.MyDropdown01")>
<cfset form.ThisNumber ="#form.MyDropdown01#">
</cfif>

<cfoutput>
<cfquery DATASOURCE="#application.dsn#" name="data">
SELECT p_table.PersonalID, p_table.FacilityNumber, p_table.LastName, p_table.FirstName, p_table.ParticipationLevel, s_table.FacilityNumber, s_table.FacilityName, s_table.City, s_table.State, s_table.ParticipationLevel, s_table.ParticipationYear
FROM p_table INNER JOIN s_table ON p_table.FacilityNumber = s_table.FacilityNumber
WHERE (((p_table.FacilityNumber)=#form.ThisNumber#) AND ((p_table.ParticipationLevel)='active') AND ((s_table.FacilityNumber)=#form.ThisNumber#) AND ((s_table.ParticipationLevel)='active') AND ((s_table.ParticipationYear)='2010'))
ORDER BY p_table.LastName;
</cfquery>
</cfoutput>

**********

When the page 'refreshes' to load the Next 40 rows CF is reading the default cfparam value of "nothing". Of course, if I (hard code) the default ThisNumber in the cfparam everything would work fine but, this form.variable is chosen by the user. You can see why the above code doesn't work. I thought I could make the form.variable persistant by cfsetting the cfparam for the query to a different name but, alas, no dice.

I do have an Application.cfm and have read that I could cfset a session.form.variable here that could be inserted in the action page. I'm not sure how to do this since the "FacilityNumber" field will grow dynamically as a new facility is set up in the database and, I'm assuming, this could mean many lines of code in cfsetting each FacilityNumber value on the Application page...

All of this may be a moot point for me anyhow since the above will be run on https secure pages with 'admin privilages only' login and, I guess I could safely enough use form.url...

But, at least I've learned a bit more about cfparam by experimenting with this NextN action page.

Just my 2 cents on cfparam for this action page. (Wondering if anyone else has tried to do this with a Next/Previous setup).

- ed

Reply to this Comment

@Ed,

All you have to do is make sure that parameters are passed through on each page refresh. On the first page, it might come from a Form; but, as the user clicks on the Prev/Next pages, y0ou need to pass the parameters through with the links as well.

To handle this, sometimes people will create an "Attributes" collection or an "Event Collection"; something that combines both the URL and FORM scope into a shared scope:

<cfset eventCollection = duplicate( url ) />
<cfset structAppend( eventCollection, form ) />

This merges both the form and url scopes into "eventCollection". The idea with this is that when you are unsure as to where a variable is coming from (form vs. url), you can always refer to it in the eventCollection.

Then, you can param your variables on the event collection:

<cfparam name="eventCollection.MyDropdown01" />

... rather than on the FORM scope.

In fact, in your first two params, by not including a scope, you are sort of doing the same thing. When you don't include a scope, ColdFusion will search *both* the URL and FORM scopes for the given variable.

So, you just need to pass the variables back with each link.

Reply to this Comment

Thanks so much, Ben, for pointing me in the right direction.

I've never used attributes collection and structappend but am anxious to get a handle on this. It's opening up a lot of possibilities in my mind...

I've messed with my code on this today and, as before, the action page loads fine with the first 40 rows. I can see that I need to put the eventCollection.MyDropDown01 within the Next/Previous urls (or either cfset the eventCollection.MyDropDown01 to some other name for insertion in the urls).

For the life of me, after trying numerous ways to get this in the urls so that they work, I've yet to figure it out.

The code I've set up is as follows:

**********

<!--- Start displaying with record 1 if not specified via url --->
<CFPARAM name="start" default="1">

<!--- Number of records to display on a page --->
<CFPARAM name="disp" default="40">

<CFSET eventCollection = duplicate( url ) />
<CFSET structAppend( eventCollection, form ) />

<CFPARAM name="eventCollection.MyDropdown01" />

<cfif isdefined ("eventCollection.MyDropdown01")>
<cfset ThisNumber ="#eventCollection.MyDropdown01#">
</cfif>

<cfoutput>
<cfquery DATASOURCE="#application.dsn#" name="data">
SELECT p_table.PersonalID, p_table.FacilityNumber, p_table.LastName, p_table.FirstName, p_table.ParticipationLevel, s_table.FacilityNumber, s_table.FacilityName, s_table.City, s_table.State, s_table.ParticipationLevel, s_table.ParticipationYear
FROM p_table INNER JOIN s_table ON p_table.FacilityNumber = s_table.FacilityNumber
WHERE (((p_table.FacilityNumber)=#ThisNumber#) AND ((p_table.ParticipationLevel)='active') AND ((s_table.FacilityNumber)=#ThisNumber#) AND ((s_table.ParticipationLevel)='active') AND ((s_table.ParticipationYear)='2010'))
ORDER BY p_table.LastName;
</cfquery>
</cfoutput>

**********

The Next/Previous urls themselves:

ThisActionPage.cfm?start=#prevrec#"><<< Previous #prev# Rows

ThisActionPage.cfm?#Evaluate("start + disp")#">Next #next# Rows >>>

**********

I'm sure this is a simple fix and do appreciate your counsel. I do want to understand this so that I can start to use this in all my code development. Thanks so much again...

- ed

Reply to this Comment

I just discovered what I believe to be a bug with the CFPARAM tag.

Put the following into a CFM document:

  • <CFOUTPUT>
  • #MyVar#<br>
  • <CFPARAM NAME=" URL.MyVar " DEFAULT="Interrupting Cow">
  • #MyVar#<br>
  • </CFOUTPUT>

My first hunch was that the CFPARAM name would be invalid because of the spaces. It's not -- executes just fine. Go ahead and run the script and pass it a MyVar value through the URL.

Your URL variable will be overwritten with the CFPARAM value.

Reproducible in CF9 and below.

This was discovered because we were getting data loss due to an errant space in a CFPARAM tag (the same thing happens with StructKeyExists, btw). The value WAS correctly passed in but then it was overwritten by the CFPARAM value. I don't see how this can NOT be considered a CF bug worthy of their notification. Can anybody give me an explanation why I shouldn't send it along to Adobe?

Reply to this Comment

I realize this is an older post, but I'm struggling with a cfparam type issue that I can't figure out.

If I use a type='integer' in my cfparam and pass it a list of values in the URL, it doesn't throw an error.

index.cfm?id=325,345

  • <cftry>
  • <cfparam name="url.id" type="integer" default="0" />
  • <cfcatch>
  • <cfset url.id = 0 />
  • </cfcatch>
  • </cftry>

If I use type='numeric' it does throw the error. Why doesn't type='integer' work in this scenario?

Reply to this Comment

It's failing because url.id is a string "325,345". You'd need to split it into a list and check the items in the list.

I'm chasing a silly problem where I'm splitting a list and trying to use <cfparam> with the first item in the list as a default numeric value. Even though it passes IsNumeric() the param tag is refusing it!

Reply to this Comment

@Ken,

We use code very similar to this with all Id values to assure they contain a valid numeric.

Our update came in response to attempted SQL injection attacks on our site where Id fields were submitted containing the offending script code.

Checks like this catch and eliminate that potential problem, firming up the security of the overall system.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.