Dynamic And Unexpected ColdFusion GetCurrentTemplatePath() Behavior
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):
<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:
<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:
<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:
<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():
<!--- 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.cfcGetting the path via the Exnteder.cfc method:
D:\....\testing\app_extend\Extender.cfcGetting the path via the mixin method:
D:\....\testing\app_extend\Extender.cfcGetting the path via the Base.cfc mixin template/method:
D:\....\testing\app_extend\GetCurrentTemplatePath.cfmTemplate-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:
<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.
Want to use code from this post? Check out the license.
Reader 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.
@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.
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/