Orange is my favorite color

Weird behavior I just uncovered:

<cffunction name="onMissingMethod" output="false" returntype="any">
  <cfset var output = "" />

  <cfinvoke component="#getPayment()#"
                method="#arguments.MissingMethodName#"
                argumentCollection="#arguments.MissingMethodArguments#"
                returnVariable="output" />
  <cfreturn output />
</cffunction>

I was building a new Transfer decorator and using onMissingMethod as a proxy to a composite object. As I worked on my unit tests, I suddenly started getting errors “OUTPUT is not defined”. Strange, it’s definitely defined at the beginning of the function.

It turns out that if the method being called returns void, cfinvoke actually destroys the output variable. I’ve never seen this kind of behavior in ColdFusion before, but it’s like PHP’s “unset” in that the variable no longer exists.

It was straightforward to work around:

<cffunction name="onMissingMethod" output="false" returntype="any">
  <cfset var output = "" />

  <cfinvoke component="#getPayment()#"
                method="#arguments.MissingMethodName#"
                argumentCollection="#arguments.MissingMethodArguments#"
                returnVariable="output" />
  <cfif isDefined("output")>
	<cfreturn output />
  <cfelse>
	<cfexit />
  </cfif>
</cffunction>

Something to keep your eyes open for as you’re working with CFINVOKE.

7 Comments

  1. Mark Mandel said:

    on January 27, 2009 at 5:16 pm

    That is because a ‘void’ function will return ‘null’.

    Any value in CF that is ‘null’ doesn’t technically exist, as far as CF is concerned, so you get interesting stuff like the above.

    You can test this my assigning a variable the value of Javacast(“null”, “”) and watch it dissapear

    Fun, no?

  2. brian said:

    on January 27, 2009 at 5:45 pm

    Agreed – in retrospect it makes sense but it’s strange to have your variable, already defined, disappear.

    Technical curiosity: we all know that we need to var scope our function variables. Could you accomplish the same thing by, instead of var-ing them at the top of the function, JavaCast-ing them to NULL at the end?

  3. Ben Nadel said:

    on January 28, 2009 at 5:20 am

    Brian, the reason to var scope a variable is to make sure the one you are using is meant to be local to the method only. If you do NOT var and simply destroy variables, you run the risk of destroying a globally-accessible variable.

  4. brian said:

    on January 29, 2009 at 10:55 am

    @Ben – understood – I’m referring to the memory leak issues that people experience with CFCs and un-var’d variables eating memory. Would making them NULL accomplish the same thing? This is really just a technical curiosity on my part… wondering how CF is working under the covers.

  5. Ben Nadel said:

    on January 30, 2009 at 5:20 am

    @Brian,

    Hmmm, not sure on that. Maybe.

  6. Brian Kotek said:

    on February 24, 2009 at 7:38 am

    CF has done this since CFCs were introduced, actually. It isn’t specific to cfinvoke. If you call any CFC method, in any way, that returns void and attempt to assign it to a variable, you’ll get an error if you try to reference that variable.

    I’m also not sure what using cfexit does within a method call like that, I’d use a void cfreturn or, better, throw an exception.

    I would modify your code to omit IsDefined(), since that can easily give you unexpected results because it implicitly checks multiple scopes looking for the variable. Do something like

  7. Brian Kotek said:

    on February 24, 2009 at 7:40 am

    Hmm, no tags allowed, apparently? In pseudocode then:

    set var local = StructNew()

    cfinvoke …. returnVariable=”local.output”

    if( StructKeyExists( local, ‘output’ )
    return output
    else
    return

{ RSS feed for comments on this post}