Strange ColdFusion Component Shell Behavior

Posted August 22, 2007 at 2:29 PM by Ben Nadel

Tags: ColdFusion

I have known for a while that you can use mixins to create dynamic functionality in a ColdFusion component, but for yucks, this morning I tried to CFInclude the entire ColdFusion component code (CFComponent tags and all) as a mixin. And, much to my surprise, it worked! Well, sort of. Here is the directory structure I am working with:

./cfc_include/index.cfm
./cfc_include/Test.cfc

./cfc_include2/BaseComponent.cfc
./cfc_include2/cfc_code.cfm

Notice that the last two files, BaseComponent.cfc and cfc_code.cfm, are in a separate, parallel directory (cfc_include2). In my index file, I am merely instantiating and using the Test.cfc ColdFusion component:

  • <!--- Create the Test ColdFusion component. --->
  • <cfset objTest = CreateObject( "component", "Test" ) />
  •  
  • <p>
  • #objTest.Echo( "I'm bringing sexy back" )#
  • </p>

Running this code, we get the following output:

I'm bringing sexy back

Ok, nothing interesting about that, until we look at how it is all coming together at run time. Let's look at the Test.cfc ColdFusion component:

  • <!--- Include the CFC code from another directory. --->
  • <cfinclude template="../cfc_include2/cfc_code.cfm" />

All that Test.cfc does is include a file from the parallel directory, cfc_include2. It doesn't even define CFComponent tags. It doesn't have anything but the CFInclude. Crazy stuff.

And, here is the cfc_code.cfm that is being included into the Test.cfc:

  • <cfcomponent
  • extends="BaseComponent"
  • output="false">
  •  
  •  
  • <cffunction
  • name="Echo"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Echos back the argument that was passed in.">
  •  
  • <!--- Define argument. --->
  • <cfargument
  • name="Value"
  • type="any"
  • required="true"
  • />
  •  
  • <!--- Return the value. --->
  • <cfreturn ARGUMENTS.Value />
  • </cffunction>
  •  
  • </cfcomponent>

This ColdFusion template defines the ColdFusion component, including the CFComponent tags. As you can see, the Echo user defined function here is being access properly from the index.cfm file listed above. But, also, notice that the CFComponent tag for this template defines an EXTENDS component, BaseComponent. As you can see from the directory structure, BaseComponent.cfc is in the same directory as cfc_code.cfm, NOT in the same directory as Test.cfc.

Here is BaseComponent.cfc:

  • <cfcomponent
  • output="false">
  •  
  • <!--- Give CFC instance a unique ID. --->
  • <cfset THIS.InstanceID = CreateUUID() />
  •  
  • </cfcomponent>

But, wait, there's more, if you CFDump out the Test.cfc instance, you get this output:


 
 
 

 
Test.cfc CFDump Using CFInclude  
 
 
 

Notice that the Echo() method is defined in the THIS scope, but the InstanceID from the BaseComponent.cfc is NOT defined anywhere. Does this mean that it could NOT find the BaseComponent.cfc? No, if that were the case, ColdFusion would have thrown the following error:

Could not find the ColdFusion Component BaseComponent. Please check that the given name is correct and that the component exists.

However, NO error is being thrown. Yet, at the same time, it seems to be completely ignoring the Extends directive.

So, what does this all mean? I am not exactly sure. I am not even sure WHAT I would expect to happen. Frankly, I am surprised this worked at all. Here's what we know:

  1. The entire content of a CFC can be included at runtime (include CFComponent tags).
  2. The included CFC cannot seem to Extend another CFC.
  3. Extends attribute does not work, but does NOT throw an error.

I don't think I would EVER use anything like this, but I think the idea of creating "Shell" CFCs for a centralized repository for actual CFC code is vaguely interesting. Anyone have any thoughts on this?

I thought maybe the name, BaseComponent, was a special name, but it didn't matter. If I changed the CFC name I kept getting the same outcome. I was also surprised by the CFDump output label, "component WEB-INF.cftags.component". This is NOT where the Test.cfc is located. Something VERY STRANGE is going on here :)




Reader Comments

Aug 22, 2007 at 4:34 PM // reply »
42 Comments

Just for the pure oddness of what you are finding I decided to mess around with this a little, this is what I found.

1. If you add <cfcomponent> around your cfinclude it "fixes" the WEB-INF.cftags.component thing.

2. It seems that coldfusion ignores the <cfcomponent> inside the included cfm files. So, I changed them to cfc and alas, you get the same result.

It seems it will ignore any cfcomponent tags within an included file. This explains why we arn't getting the uuid in the output. Also, it explains why we are getting the WEB-INF.cftags.component in the dump. I'm assuming coldfusion is looking at the file ext and it knows that it's supposed to be a cfc, but it can't find the component declaration so it likely isn't getting instantiated properly and gets the super class definition.

Of course if you move your <cfcomponent> and the extends attribute to the test.cfc it reports everything as would be expected.


Aug 22, 2007 at 4:40 PM // reply »
10,640 Comments

@Dustin,

Nice detective work! I feel like something, somewhere should be throwing an error on this??? What you are saying makes a lot of sense as to why it goes to the install folder to find the base CFC.


Aug 22, 2007 at 4:50 PM // reply »
42 Comments

Thanks!

I was getting the same feeling as I was testing it as well. I even tested it with <cfcomponent> within both the .cfc and the .cfm pages. I could have swore if you tried to declare a component within another component it would throw an error.


Aug 22, 2007 at 7:52 PM // reply »
105 Comments

Dustin is pretty much on the mark.

Here's a key clue: inside a CFC you can omit <cfcomponent> and it will still work.

CFCs compile to "regular" pages (which is why they can be included) but the magic of being an object is handled by how you interact with that page (class). <cfcomponent> adds metadata to the page that is only used if the page (.cfc) is used in an "object-like" context.

In your example, Test.cfc has no <cfcomponent> tag so it gets the default "object" metadata - and the <cfcomponent> inside the included file is ignored (because you're not using that file like an object).

It might be interesting to dump getMetadata(objTest) in your index.cfm...


Aug 23, 2007 at 7:18 AM // reply »
10,640 Comments

@Sean,

The meta data gives me this:

NAME - WEB-INF.cftags.component
PATH - D:\....\testing\cfc_include\Test.cfc
TYPE - component


Aug 23, 2007 at 11:37 AM // reply »
105 Comments

No functions, right?

Because the metadata is all about the Test.cfc file rather than what a dynamic object of type Test contains.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »