Dynamic And Unexpected ColdFusion GetCurrentTemplatePath() Behavior

Posted July 13, 2007 at 8:28 AM

Tags: ColdFusion

ColdFusion has two methods that help you determine which templates are being executed:

  • GetCurrentTemplatePath()
  • GetBaseTemplatePath()

Normally, the GetCurrentTemplatePath() method will return the path of the template in which the method call resides. However, I just came across some unexpected behavior; if the method call is inside of a ColdFusion user defined function, then the GetCurrentTemplatePath() returns the path of the template that binds dynamically at runtime to the parent function. We're not even talking about the template that houses the UDF - we're talking about the template that binds to the UDF.

However, that is not even 100% consistent. If the GetCurrentTemplatePath() is inside of a ColdFusion template that is then included into a function, the GetCurrentTemplatePath() function returns the path of the housing template, not of the page that binds to the ColdFusion UDF at runtime.

Take a look at this example. I have two mixins. One is a mixin that contains a ColdFusion UDF that simply returns the current template path (GetPath.cfm):

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

  • <cffunction
  • name="GetMixinPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the current template's (this) file path.">
  •  
  • <!---
  • Return the path of the currently executing
  • template (this mixin function).
  • --->
  • <cfreturn GetCurrentTemplatePath() />
  • </cffunction>

The other mixin is a simple ColdFusion template which just outputs the current template path:

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

  • <cfoutput>
  • #GetCurrentTemplatePath()#
  • </cfoutput>

Then, I have two ColdFusion components. I have my Base.cfc which has a defined method, GetBaseComponentPath(), which returns the current template path. It also has a method, GetBaseComponenMixintPath(), which includes the GetCurrentTemplatePath.cfm mixin and returns the outputted value:

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

  • <cfcomponent
  • output="false"
  • hint="Base component to be extended">
  •  
  • <cffunction
  • name="GetBaseComponentPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the base component's (this) file path.">
  •  
  • <!---
  • Return the path of the currently executing
  • template (this component).
  • --->
  • <cfreturn GetCurrentTemplatePath() />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetBaseComponenMixintPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the base component's (this) file path.">
  •  
  • <cfset var strPath = "" />
  •  
  • <!---
  • Include the template that outputs the
  • current template path.
  • --->
  • <cfsavecontent variable="strPath">
  • <cfinclude template="GetCurrentTemplatePath.cfm" />
  • </cfsavecontent>
  •  
  • <!--- Return the path of the mixin template. --->
  • <cfreturn Trim( strPath ) />
  • </cffunction>
  •  
  • </cfcomponent>

Then, I have the Extender.cfc ColdFusion component which extends the Base.cfc and includes the mixin GetPath.cfm:

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

  • <cfcomponent
  • extends="Base"
  • output="false"
  • hint="The extending component.">
  •  
  • <cffunction
  • name="GetExtenderComponentPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the current component's (this) file path.">
  •  
  • <!---
  • Return the path of the currently executing
  • template (this component).
  • --->
  • <cfreturn GetCurrentTemplatePath() />
  • </cffunction>
  •  
  •  
  • <!---
  • Include the mixin file that defines a function
  • for getting the current template path.
  • --->
  • <cfinclude template="./GetPath.cfm" />
  •  
  • </cfcomponent>

Ok, so now, I am gonna instantiate the Extender.cfc ColdFusion component and test all the various method returns for GetCurrentTemplatePath():

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

  • <!--- Create our extender CFC instance. --->
  • <cfset objCFC = CreateObject( "component", "Extender" ) />
  •  
  • <h4>
  • CFC-Based Methods
  • </h4>
  •  
  • <p>
  • Getting the path via the Base.cfc method:<br />
  • #objCFC.GetBaseComponentPath()#
  • </p>
  •  
  • <p>
  • Getting the path via the Exnteder.cfc method:<br />
  • #objCFC.GetExtenderComponentPath()#
  • </p>
  •  
  • <p>
  • Getting the path via the mixin method:<br />
  • #objCFC.GetMixinPath()#
  • </p>
  •  
  • <p>
  • Getting the path via the Base.cfc mixin method:<br />
  • #objCFC.GetBaseComponenMixintPath()#
  • </p>
  •  
  •  
  • <!---
  • The above have all been called on methods of
  • a CFC. What happens if we include the mixin
  • method directly into the current template
  • (index.cfm) and then execute it that way.
  • --->
  •  
  • <h4>
  • Template-Based Methods
  • </h4>
  •  
  • <!--- Now, let's include the mixin into this template. --->
  • <cfinclude template="./GetPath.cfm" />
  •  
  • <p>
  • Getting the path via TEMPLATE mixin method:<br />
  • #GetMixinPath()#
  • </p>

Running the above, we get the following output:

CFC-Based Methods

Getting the path via the Base.cfc method:
D:\....\testing\app_extend\Extender.cfc

Getting the path via the Exnteder.cfc method:
D:\....\testing\app_extend\Extender.cfc

Getting the path via the mixin method:
D:\....\testing\app_extend\Extender.cfc

Getting the path via the Base.cfc mixin template/method:
D:\....\testing\app_extend\GetCurrentTemplatePath.cfm

Template-Based Methods

Getting the path via TEMPLATE mixin method:
D:\....\testing\app_extend\index.cfm

As you can see, the Base.cfc method, GetBaseComponentPath(), the Extender.cfc method, GetExtenderComponentPath(), and the Extender.cfc mixin method, GetMixinPath(), all dynamically bound themselves to the Extender.cfc at runtime, returning the Extender.cfc as the current template path (regardless of where GetCurrentTemplatPath() was defined). Even the index.cfm-based mixin method, GetPath(), bound dynamically to the index page at runtime.

The only thing that didn't seem to bind dynamically at runtime was the GetCurrentTemplatePath.cfm mixin which was a straight-up ColdFusion template, not a mixin method.

Something about this just rubs me the wrong way. I understand that function binding is highly dynamic, and this is a very powerful thing because it allows us to evaluate the THIS and VARIABLES scope at the point of reference, which, in turn, allows us to add, delete, and share methods across ColdFusion components. However, I don't feel that this is the intent of methods like GetBaseTemplatePath() and GetCurrentTemplatePath(). In fact, if you look at the documentation for GetCurrentTemplatePath(), it reads:

Returns: The absolute path of the page that contains the call to this function, as a string.

It doesn't mention anything here about dynamic runtime binding. Now, you might argue that the documentation then states:

If the function call is made from a page included with a cfinclude tag, this function returns the page path of an included page.

Here, at least it mentions the CFInclude usage. However, if that was the requirement, then using it within a CFModule of CFImport tag would not meet this criteria. Of course, if we invoke the GetCurrentTemplatePath.cfm as a module from WITHIN the index.cfm page:

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

  • <cfmodule template="./GetCurrentTemplatePath.cfm">

... we get the following output:

D:\....\testing\app_extend\GetCurrentTemplatePath.cfm

Here, the GetCurrentTemplatePath() method is binding to the tag, not to the index page. This proves that CFInclude is NOT a requirement for the GetCurrentTemplatePath() method to work correctly.

So, long story short, I think the dynamic binding of the GetCurrentTemplatePath() to its housing ColdFusion component is not the expected behavior and seems to go against the ColdFusion documentation.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




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

Reader Comments

Jul 16, 2007 at 2:38 AM // reply »
2 Comments

Another interesting behavior i just notice a month a go with thegetTemplatePath() function.

If you add a call to this function in the application.cfm and on the url your accessing a cfm file the function return the actual path of the cfm page - but if your accessing a remote method of a cfc file the getTemplatePath() return the path to the application.cfm file.

note that this was not the behavior of the Coldfusion mx 6.1. In 6.1 the function returns the path to the cfc file as well.


Jul 16, 2007 at 7:14 AM // reply »
5,406 Comments

@Kris,

That is very interesting. I haven't done much yet with direct CFC calls. I am surprised that is has changed from version to version.


Jul 17, 2007 at 6:01 PM // reply »
123 Comments

I ran into a problem with this behavior while working on a project recently. I needed to get the path to the calling page inside a custom tag, which proved to be pretty complicated.

For anyone who's curious about the internals of the CF engine this sheds a lot of light on how function binding and includes/cfcs/custom tags really work inside the engine.

http://enfinitystudios.thaposse.net/blog/2007/07/17/getting-the-expected-results-for-getcurrenttemplatepath-in-a-custom-tag/


Post Comment  |  Ask Ben

Recent Blog Comments
Isnogood
Jul 3, 2009 at 7:16 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
Watch this http://www.nsca-lift.org/videos/default.shtml ... read »
Aaron
Jul 3, 2009 at 7:13 PM
Project HUGE: Get Big, Phase One (Chat Waterbury - Huge In A Hurry)
I've just finished the 3rd week of phase 3, and have to agree that the overhead squats are hard. I think this is most due to the wide grip on which places more pressure on your upper back. Only this ... read »
Isnogood
Jul 3, 2009 at 7:11 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
Very good, there were some near perfect reps, and there were some dodgy ones, but you're getting there your body position is good. Work on your depth and do not let the bar move forward or backward, ... read »
Martin Mädler
Jul 3, 2009 at 6:48 PM
Create A Running Average Without Storing Individual Values
Nice dodge! I dig the idea to force out the last bit of performance out of a chunk of code, even though it's such a minor thing. Heard of this kinda approach in connection with "running sums". @Rola ... read »
Murray
Jul 3, 2009 at 6:23 PM
ColdFusion POIUtility.cfc Updates And Bug Fixes
Re: Element TYPENAME is undefined in a CFML structure referenced as part of an expression error. I experienced this because I created the query from JSON data using an arrayOfStructuresToQuery funct ... read »
Roland Collins
Jul 3, 2009 at 6:03 PM
Create A Running Average Without Storing Individual Values
Just thought I'd add a bit to the discussion :) The only caveat with this approach is that it becomes less accurate over time because you're storing the results with limited decimal precision. It' ... read »
Jul 3, 2009 at 5:32 PM
Project HUGE: Get Big, Phase One (Chat Waterbury - Huge In A Hurry)
@Isnogood, I don't think I could ever get that many reps! Dang! I don't know how much weight I'm gaining, but I've got my fingers crossed. OMG - I just tried overhead squats for the first time: ... read »
Jul 3, 2009 at 5:24 PM
Adobe Announces That HomeSite Is Officially Dead
It'd be nice if they open-sourced HS. I'm sure someone could do some nice things with it. No point in putting something out to pasture that people still actively develop in. ... read »