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 the jQuery Conference 2010 (Boston, MA) with:

Strange ColdFusion URLDecode() and GetEncoding() Behavior

By Ben Nadel on
Tags: ColdFusion

I ran into a really strange ColdFusion behavior this morning. I don't deal with character encoding very much, so I am not sure if this is a bug or not. It sure feels like a bug to me. Take a look at this example:

  • <!--- Dump out the URL encoding. --->
  • <cffunction name="Test">
  • <cfargument
  • name="URL"
  • type="string"
  • required="false"
  • default=""
  • />
  •  
  • <cfdump var="#UrlDecode( 'http%3A%2F%2Fwww.bennadel.com' )#" />
  • <cfabort />
  • </cffunction>
  •  
  • <!--- Trigger test method. --->
  • <cfset Test() />

This is a really paired down example of the problem code; but, essentially, I needed to URLDecode() a value that was returned from a remote API. However, when we run the above code, we get the following ColdFusion error:

java.lang.String cannot be cast to coldfusion.filter.UrlScope null

As I started to take away parts of the code in an effort to debug, I found that this code also broke:

  • <!--- Dump out the URL encoding. --->
  • <cffunction name="Test">
  • <cfargument
  • name="URL"
  • type="string"
  • required="false"
  • default=""
  • />
  •  
  • <cfdump var="#GetEncoding( 'URL' )#" />
  • <cfabort />
  • </cffunction>
  •  
  • <!--- Trigger test method. --->
  • <cfset Test() />

In fact, this code throws the same error:

java.lang.String cannot be cast to coldfusion.filter.UrlScope null

When I saw that GetEncoding() was throwing the same error as the UrlDecode() method, I started to suspect that there was some sort of name conflict going on, perhaps with the "URL" argument being passed to the UDF method. To test this theory, I renamed the URL argument of the user defined function and ran the code again:

  • <!--- Dump out the URL encoding. --->
  • <cffunction name="Test">
  • <cfargument
  • name="TargetURL"
  • type="string"
  • required="false"
  • default=""
  • />
  •  
  • <cfdump var="#GetEncoding( 'URL' )#" />
  • <cfabort />
  • </cffunction>
  •  
  • <!--- Trigger test method. --->
  • <cfset Test() />

This time, with the TargetURL argument rather than the URL argument, the GetEncoding() method run without error, outputting the value:

UTF-8

Obviously, I am not going to rename my CFArgument value because the name "URL" makes total sense for the given context. So, what can I do about it? I tried to manually set the encoding before calling UrlDecode():

  • <!--- Dump out the URL encoding. --->
  • <cffunction name="Test">
  • <cfargument
  • name="URL"
  • type="string"
  • required="false"
  • default=""
  • />
  •  
  • <!--- Set the URL encoding. --->
  • <cfset SetEncoding( "url", "utf-8" ) />
  •  
  • <cfdump var="#UrlDecode( 'http%3A%2F%2Fwww.bennadel.com' )#" />
  • <cfabort />
  • </cffunction>
  •  
  • <!--- Trigger test method. --->
  • <cfset Test() />

... But this didn't help at all (same error). Finally, after some more playing around for like 10 minutes, I found that adding the CharSet optional argument to the UrlDecode() method did the trick:

  • <!--- Dump out the URL encoding. --->
  • <cffunction name="Test">
  • <cfargument
  • name="URL"
  • type="string"
  • required="false"
  • default=""
  • />
  •  
  • <cfdump var="#UrlDecode( 'http%3A%2F%2Fwww.bennadel.com', 'utf-8' )#" />
  • <cfabort />
  • </cffunction>
  •  
  • <!--- Trigger test method. --->
  • <cfset Test() />

This code runs without error and outputs the value:

http://www.bennadel.com

Again, I don't play around with encodings very much, but this definitely feels like a bug. Anyone have any thoughts or see where I'm missing a big piece of logic?



Reader Comments

@Jeff,

The name of the CFArgument should never matter as long as your references to the argument are scoped (making sure ColdFusion doesn't have to try to decide before which one you are talking about). When you define arguments this way, they are simply keys in an argument struct and there are no limitations on what values you can use for struct keys (as far as I know).

I'm not entirely sure whether to conclude that it's a bug or the nature of it being a naming conflict, or something else.

What I did observe from running a few little tests was that if you remove the cfargument entirely from all of your examples, everything works as expected.

If you keep the cfargument and dump just a simple string like "Hello World" (rather than do anything involving URLs), it seems to work fine too which I found interesting. (I expected it to break, but it didn't.) Not sure what to make of that just yet.

Not sure if this helps or if it muddies it more, but either way thanks for posting this so I'm sure never to name arguments URL.

Not sure I would use an argument name of URL either, but did you try explicitly defining the scope?

<cfdump var="#GetEncoding(ARGUMENTS.URL)#" />

@Steve,

The URL being passed to the method is not a scope. The URL being passed in is a string being used in a remote API request (my example is a paired down version of the code). Also, GetEncoding() takes the name of a scope, not a reference to it (according to documentation - this is the first time I've ever used this method :)).

@Angela,

Please please please don't take away the lesson that you need to restrict your method argument naming. Naming conventions should always be intuitive. If you have to pass in a URL, then name it URL :) That is not the problem here. I have named my arguments after scopes all the time, especially if I'm actually passing in a scope, ex:

MethodCall( URL = Duplicate( URL ) )

Nothing wrong with this. And, as long as you scope your argument references, there is no ambiguity in the execution of said code. If there is a problem, it's in the internal execution of UrlDecode(), not in the argument naming.

I have to cast my vote for this not being a bug. It looks like it is simply the result of asking CF to guess whether you mean URL, the built-in scope, or arguments.URL, the local variable. As was suggested, being explicit about which scope to look in is best (arguments.URL) and should work every time.

@Ben, well when you put it that way... Maybe I won't take that away after all. Personally I don't have a an issue using names like URLscope or strURL. I find them intuitive enough but I can see your point.

I've named arguments URL in the past, and I'm pretty sure that I even dealt with encoding in the function but I'm not finding any examples to say for sure. (I could be wrong though.) Maybe there's more to this that we're not getting yet.

I'll hold off on deciding which way to go with naming conventions once this plays out some more. After all, there's a workaround which may be sufficient enough to keep the argument named as URL.

Thanks again. :-)

@Chris,

I don't think there is anything ambiguous about it. When you call GetEncoding(), ColdFusion expects a scope name. When you pass in URL or FORM as a scope reference, ColdFusion complains that it cannot convert it to a simple value (name of scope).

Additionally, ColdFusion does not have to guess at which scopes you are referring to. It keep clear internal track of all the built-in scopes:

<!--- Get all built-in scopes. --->
<cfdump var="#GetPageContext().GetBuiltInScopes()#" />

<!--- Find scope by name. --->
<cfdump var="#GetPageContext().SymTab_findBuiltinScope( 'URL' )#" />

I can't think of any reason, other than a bug, that ColdFusion would have to guess which scope you are referring to. Also, remember that once you are inside the context of a method call (ie GetEncoding()), the internal workings of the function to not have access to the local variables of its calling context.

If anyone comes away from this post thinking they have to be careful about their argument naming (especially when the naming is maximally intuitive), then this post will have done more of a disservice than a service :(

This month marks 9 years since I started doing CF development. And I am still more than a little grumpy that you can't use "url" as an identifier. I'm not hating on the Fusion here, I'm just saying...

@Ben

Call me Cutter brother. I had to start using the rest of the name in communications because the CF product team kept passing me up on the ACE program because they didn't know who 'Steve Blades' was;)

I understand that you are only passing in a string. ColdFusion doesn't (in the context of that function), because your argument has the same name as a scope. I'm saying, in your function, explicitly tell CF which scope to look at. By explicitly referencing the variable as being a part of the ARGUMENTS scope, it won't get confused and think you are referencing the entire URL scope, but go straight to the ARGUMENTS scope to resolve 'URL'.

Ben,

Maybe I am confused as to this code.

Are you attempting to URLDecode the entire URL structure? (ie All passed parameters in the URL (GET) scope?

@Cutter,

Sorry about that - I know you've brought it up before :) But yes, I I absolutely reference the CFArgument URL via the ARGUMENTS scope when necessary. In general, I try to scope all my variable references for optimal clarity.

@Jeff,

I am not trying to decode the URL scope. I am trying to decode an encoded value passed back from an API. In the demo, I am pasting in a sample URL that was returned:

http%3A%2F%2Fwww.bennadel.com

@Ben

I understand your desire for simplified naming, but there are times when the implicit scope precedence does not always work as expected.

In the case of arguments such as this, I have started prefixing the variables with in. So for this, I'd us inURL. What is nice about this methodology is that if I process the URL in someway, I can assign it to a new variable outURL. Especially useful for long, complex code blocks that require referring back to the original argument. This eliminates the need for explicit scope reference. However, I only see this as a potential problem with variables named the same as the global scopes (URL, FORM, CLIENT, etc), basically anything you can do a cfdump on without defining it first.

@Jeff,

I can appreciate that you shouldn't use these for variable names. But defining these values with CFArgument tags is what has me on the other side of the fence. Is it a variable? Or is really a struct key in the ARGUMENTS scope? It states nothing about collection keys.

But, the argument here is silly since it clearly breaks unless you specify the character encoding in the UrlDecode() method :)

@Jeff,

I don't want you to think I'm a total jackass. I would never do things like this:

<cfset URL = { Name = "Value" } />

... cause clearly, there is going to be guessing that ColdFusion is gonna have to do.

However, I still think it would be totally reasonable to do something like:

<cfset REQUEST.URL = { Name = "Value" } />

To me, struct-keys exist in a world of their own.

Now, you could say that unscoped variables are *really* just keys in the implicit context (ie. VARIABLES scope on a page, THIS scope in a CFC); but, something about the unscoped nature of them feels so different.

Arrrrrg! Now I am getting frustrated with myself :)

@Ben -

In my day job, there are dozens of variables in our 3k template app that read 'URL' or 'FORM'. They are so numerous, renaming them would be a nightmare. We've been able to avoid the conflicts by, when finding them, putting them in their proper scope:

VARIABLES.url
ARGUMENTS.url
REQUEST.url
FORM.url
VARIABLES.GetSite.url (a query column reference)

It's not the ideal fix, but it does avoid the conflicts.

@Ben

I understand completely with what you are saying.

However, in my current situation, I have a deep appreciation of these restrictions. I am coming in on an established application that has had many "generations" of development and many different developers - and therefor styles.

I have come across a few situations where there was implied logic put in place that, once explained (or figured out), is really quite elegant. However, to a fresh set of eyes, it is confusing at times and convoluted and frustrating at others.

Adobe (or Macromedia) choosing to make the system defined scopes be reserved words does actually make sense at this level. It is like any other language with reserved words.

It would be great if the most recent scope took precedence, but there is an issue with the eample you gave.

Let's say you can use a variable named URL. Now, in your procedure, you want to make a comparison with passed parameters in the URL structure? Is it If (URL eq URL.Variable)? I have not tried this, but the only place that could ever possibly be true, if URL as a variable was allowed, would possibly be if there was only one value in the URL structure and it also automatically dereferenced that one value.

@Jeff,

I guess really what it comes down to is how do we officially define "variable" :) Because, a variable name can't start with a number, but a struct key *can*. So, does that mean that a struct key and a variable are very different things?

Very thin line.

I ran into this EXACT issue - fought with it for a good 1/2 hour before turning to google and finding this helpful post.

RSS = Subscribed!!

Thanks for saving me further frustration!

B.

Did you try passing in some other scope name like 'form' instead of 'url' to getEncoding?

Definitely a bug; just curious as to it's nature.

Strike my last comment. A delete feature would be nice to quell some noise :).

My comment was regarding a red herring of sorts in the post: URL is a reserved word; all scope names in CF are reserved.

@Danny,

I don't think it has much to do with "reserved" words. I think it must be the way the underlying code searches for the structure that you are looking to encode.

Yep - too strange since the second arg in urldecode is optional. I did run across the error and googled it and landed safely here. Thanks, Ben!

@Ben,

I absolutely agree with you on all points. This idea that "oh, I better not call my argument this or that because it is a scope", is absolutely rediculous. People that use that ideology don't understand programming and certinaly don't understand the underlying mechanisms. There is nothing wrong at all using scope names as argument names, and yes, you should always refer to your arguments as Arguments.Foo, since that is percisely the reference you are dealing with, any other way does not make sense to me (perhaps because I come from a C++ background).

As for the urlDecode method, yesterday I upgraded one of my installation to ColdFusion 9 (from ver. 8) and all of my urlDecode calls broke with exactly the error referenced here.

Thank you all so much for your efforts and contributions to this thread. I will now be able to easily correct for this, what I consider, very odd behavior. I believe this to be a bug myself. This is NOT expected behavior (ie: bug).

???? ????? ????????? ?????? ??????? ????? ? ?????? ?????? ???????. ????????? ?????? ??????, ??? ??? ????? ??????? ?? ????? ?? ???????? ???????.