When upgrading Legacy Code, part of the fun is moving from a much older procedural process of coding to a more Object Oriented way of coding. For those who have little experience with OO development, it can be confusing, and even daunting. ColdFusion doesn't require an application to be OO, but it is a smart idea to reuse code whenever possible. With the invention of CFCs, many versions ago, ColdFusion came into a new age of reusability.

But ColdFusion has allowed reusable code for some time. Aside from the ability to <cfinclude> a file as often in page flow as you might want, it has also had Custom Tags for quite a while now. In this post, let's explore the usage of the scopes available within Custom Tags.

ATTRIBUTES

In our last post we talked about this "passalong" scope. To enforce encapsulation, and manage dependencies, it is best to "pass in" any outside variables that your Custom Tag might need, rather than directly accessing that scope from inside the Custom Tag. A tag's ATTRIBUTES are only available within the tag, or within any nested tag of the tag as part of it's CALLER scope (yes, you can nest tags, and more on the CALLER scope in a moment), though it is again best to "pass" attributes to a nested tag as part of its attributes. Best practice is to <cfparam> each attribute at the beginning of your custom tag, declaring it's type and a default value. This allows your code to be somewhat self documenting, and helps to identify any inherited dependencies.

VARIABLES

This is where I will undoubtedly confuse a lot of people, because usage of the VARIABLES scope is confusing. Many examples exist out there showing the use of VARIABLES just about anywhere and everywhere, but I will tell you that the only places you should use the VARIABLES scope (IMO) is within Custom Tags and CFCs. While possible to use the VARIABLES scope within a standard CFM, it is better to have those VARIABLES converted to the REQUEST scope, because there is a documented use and reasoning for the VARIABLES scope, and that's not it.

The VARIABLES scope, within Custom Tag usage, is a protected "local"scope of that Custom Tag. Within a Custom Tag, the VARIABLES scope is often used for internal business logic, such as looping iterators or temporary variables. Once tag execution is complete, any variables used by the tag's VARIABLES scope are gone, being cleared from memory. You can not access a tag's VARIABLES scope outside of that tag, excepting a nested tag's CALLER scope (more in a minute), because those variables will no longer exist.

CALLER

Custom Tags contain a unique scope in the CALLER scope. This scope is used to access certain variables of the template that requested the Custom Tag. If, within a nested tag, you were to <cfdump> the tag's CALLER scope, you would see the following items in the output:

  • ATTRIBUTES - the ATTRIBUTES scope of the tag that called the nested tag
  • CALLER - the VARIABLES scope of the template that called the tag that called your nested tag (yeah, that's not confusing)
  • variables - individual name/value pairs of any variables assigned to the calling tag's local VARIABLES scope
  • THISTAG - the THISTAG scope of the tag that called the nested tag
If you are passing in all of the variables you need to utilize in your tag (as you should) then the only time you would reference the CALLER scope is to set some value to a variable in the template that called the tag. In a nested tag, this is a way of changing a value in the calling tag's VARIABLES scope.

Now, I told you a moment ago that you shouldn't use the VARIABLES scope in a standard CFM. So how would you change a variable value in that standard template from within a base tag? I proposed that you change those variables to the REQUEST scope, but I also said you should not access the persistent scope directly. You can use the tag's CALLER scope, using the following notation, to change that request's variables.

view plain print about
1CALLER["REQUEST.myVar"] = "foo";

You can use the same notation to address any of the persistent scopes, remembering that, outside of REQUEST, you will need to use <cflock> to avoid race conditions for any concurrent sessions.

THISTAG

This is another scope completely unique to Custom Tags. You will never set variables to this scope, as any variable held here are generated by ColdFusion for your use. They give you details on the current ExecutionMode of the tag, whether an end tag exists (HasEndTag), and others.

Conclusion

Custom Tags are confusing, yet extremely powerful, constructs of the CFML programming framework. They are a way for you, the developer, to create your own CFML tags. You can even <cfimport> a library of Custom Tags into your pages for simpler access.

view plain print about
1<cfimport prefix="ui" taglib="/CustomTags/ui">
2<ui:siteHeader><!--- opening html, header, meta, css, and opening body tags --->
3<p>My page content here</p>
4<ui:siteFooter><!--- javascript and closing body and html tags --->

This post only scratches the surface of Custom Tag development, trying to demystify the usage of the scopes that they use. For a full explanation of their usage, you should review the ColdFusion Documentation on creating and using Custom Tags. As I've said in the past, Custom Tags are ideal for creating reusable bits of display code. CFCs are intended for reusable components of data access and business logic, and very-rarely-almost-never for display. We'll discuss scope usage of CFC's in our next post.

As a side note, my usage of the VARIABLES scope stems from some evidence that, in the past, usage of the VARIABLES scope within standard CFM templates did not always properly get cleared out of memory. This may or may not have been corrected by now, but the use cases for the VARIABLES scope hasn't changed any, and I've found that the changes in how I've used it have always improved my applications' overall memory utilization. So, I've continued to code in this way, and it's always proved effective.

This article is the twelfth in a series of articles on bringing life back to your legacy ColdFusion applications. Follow along in the Legacy Code category.