Strange ColdFusion Component Shell Behavior

Posted August 22, 2007 at 2:29 PM

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:

 Launch code in new window » Download code as text file »

  • <!--- 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:

 Launch code in new window » Download code as text file »

  • <!--- 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:

 Launch code in new window » Download code as text file »

  • <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:

 Launch code in new window » Download code as text file »

  • <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 :)

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

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 »
6,516 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 »
97 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 »
6,516 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 »
97 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 Comment  |  Ask Ben

Recent Blog Comments
aha
Nov 22, 2009 at 7:42 AM
Using A Name Suffix In ColdFusion's CFMail Tag
Why not? ... read »
Nov 22, 2009 at 7:37 AM
Using A Name Suffix In ColdFusion's CFMail Tag
asd ... read »
Nov 22, 2009 at 4:30 AM
jQuery Live() Method And Event Bubbling
dasegtezr ... read »
Nov 22, 2009 at 4:03 AM
jQuery Live() Method And Event Bubbling
C_fieri ... read »
Nov 22, 2009 at 1:56 AM
Learning ColdFusion 9: Using CFQuery In CFScript Can Enable SQL Injection Attacks
Why adobe would give you script equivalent of cfquery is beyond me. I love cfquery tag because it helps me wriite clean sql, and get away from the horrible jdbc queries If I wanted to write javali ... read »
Nov 22, 2009 at 1:45 AM
Streaming Text Using ColdFusion's CFContent Tag And The Variable Attribute
The reason you would want to do this is to stream. Ack json/xml files to ria clients I used thus technique before because putting json in response stream causes debugging info to come thru As well a ... read »
Nov 21, 2009 at 6:47 PM
Hal Helms - Real World Object Oriented Development, Sarasota - Day Five
@charlie griefer, Thank you.. ... read »
Nov 21, 2009 at 5:15 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jose Galdamez, Oh heh yeah I didn't paste the whole code. I should have defined the vars -- my bad. It's fixed thou. Thanks. ... read »