Using "return" To Short-Circuit A CFML Template In ColdFusion
At work, we use Framework One (FW/1) to route and render our ColdFusion requests. As such, our "controller layer" is implemented as a series of ColdFusion components (CFCs). And, since each request maps to a method invocation on said components, I'm used to using a return
statement when short-circuiting my controller actions. Yesterday, when working on my Hotwire + ColdFusion demos - which uses simple CFML templates as its controller layer - I accidentally used a return
statement to short-circuit the control flow. And it worked! This was unexpected; and, I wanted to see if it worked in both Adobe ColdFusion and Lucee CFML.
Normally, when short-circuiting a CFML template, I use the CFExit
tag. This tag can be used on its own (with no attributes); or, when used within a ColdFusion custom tag, may included a method
attribute to define the desired control-flow. The CFReturn
tag, on the other hand, is normally reserved for User Defined Functions (UDFs).
To demonstrate that the CFReturn
tag appears to work in a CFML template, I'm going to set up a simple ColdFusion demo in which we CFInclude
two different files. Each file is going to write two strings to the output buffer, separated by a CFReturn
tag:
<cfscript>
writeOutput( "A-1 <br />" );
return;
writeOutput( "A-2 <br />" );
</cfscript>
And, the other one - which just changes the letter from "A" to "B":
<cfscript>
writeOutput( "B-1 <br />" );
return;
writeOutput( "B-2 <br />" );
</cfscript>
My test script is then going to include both of these templates in order to see how the CFReturn
statement is scoped: does it affect just the currently executing template? Or, does it affect the entire request (somehow)?
<cfscript>
writeOutput( server.coldfusion.productName & " - " );
writeOutput( server.coldfusion.productVersion & "<br />" );
include "a.cfm";
include "b.cfm";
writeOutput( "Done <br />" );
</cfscript>
Now, if we run this demo in both Adobe ColdFusion and Lucee CFML, we get the following output:
As you can see, both Adobe ColdFusion and Lucee CFML successfully rendered A-1
and B-1
. And, both runtimes omitted A-2
and B-2
because they appeared after the CFReturn
tag. In other words, both runtimes used the CFReturn
tag to short-circuit the CFML template execution.
As I mentioned above, the CFReturn
tag is normally used to short-circuit method invocations. As such, I wanted to quickly run another test to see if we get the same behavior when executing inside a UDF:
<cfscript>
(() => {
writeOutput( "UDF Context <br />" );
writeOutput( server.coldfusion.productName & " - " );
writeOutput( server.coldfusion.productVersion & "<br />" );
include "a.cfm";
include "b.cfm";
writeOutput( "Done <br />" );
})();
</cfscript>
As you can see, this is the same exact code as before - we're just executing it inside an immediately-invoked function expression (IIFE). And, when we run this though both Adobe ColdFusion and Lucee CFML, we get the same exact output as before. Which means, even though the CFML templates are being included from within a Function, the embedded return
statements are scoped to the CFML template in which they are defined!
Honestly, I kind of like this. I'd be perfectly happy if CFExit
was actually just an alias for CFReturn
(or vise-versa) under the hood. When I stumbled upon the fact that the CFContinue
tag can short-circuit .each()
iterators, I suspected it was a bug. In this case, I'm not sure if it's a bug, something intentional, or just a byproduct of the way the templates get compiled into "page contexts". But, clearly, both runtimes agree on the expected outcome.
Want to use code from this post? Check out the license.
Reader Comments
Very cool and certainly shower to type than
cfexit method='exittemplate'
:-) But I wonder if it is kosher enough to use in production code? I looked quickly at Adobe's online docs but didn't find anything.@Angeli,
Yeah, definitely a tough call 😳 That said, I believe the default
method
isexitTemplate
; so, you should just be able to omit the attribute and just haveexit
in most cases (unless your using Custom tags).I'd been using
cfexit method='exittemplate'
cuz I was under the impression that cfexit without any atributes functions the same as cfabort. As a matter of fact, the docs at Adobe for cfexit says, "If this tag is encountered outside the context of a custom tag, for example in the base page or an included page, it executes in the same way as cfabort." But based on your and my experiments. the bit about the the included template may be wrong. But I think I'll just use cfexit without attributes instead of cfreturn as that's more kosher. Have a good weekend.@Angeli,
Yeah, that language is definitely confusing. If the request only had a single CFML template to execute, then I guess you could say calling
exit
inside that template would be the same as callingabort
. But, most request process more than one template, and now-a-days, also have anApplication.cfc
running, that would likely call the subsequent event-handlers (such asonRequestEnd()
). So, yeah, Adobe's wording there is confusing.Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →