Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Martin Schaible
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Martin Schaible

Using "return" To Short-Circuit A CFML Template In ColdFusion

By
Published in Comments (4)

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:

Page output shows that A-1 and B-1 were logged but A-2 and B-2 were omitted.

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

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

15,841 Comments

@Angeli,

Yeah, definitely a tough call 😳 That said, I believe the default method is exitTemplate; so, you should just be able to omit the attribute and just have exit in most cases (unless your using Custom tags).

7 Comments

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.

15,841 Comments

@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 calling abort. But, most request process more than one template, and now-a-days, also have an Application.cfc running, that would likely call the subsequent event-handlers (such as onRequestEnd()). So, yeah, Adobe's wording there is confusing.

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel