What Function Called My Function
Join the DZone community and get the full member experience.
Join For FreeI recently came across a question on the Adobe forums about being able to tell who called a certain function in a base component (the "super"). The first thing that came to mind was: is this even possible? The answer is: Yes of course it's possible!
The full question by Spike H:
I have written a utility component (test1 below) and want to keep track of what external functions call the functions within my component. Other programmers create a component (e.g. test2 below) that extends my utility component and then their functions call my functions. I want to record metric data to determine how much use my functions are getting and from where. I've used the getMetaData() function inside my functions, but that only gives me the name of the component that extends my component (in this case test2), not the specific function that called my function (in this case functionAtest2).
That piqued my interest, I figured there had to be a way. I know when you get an error you also get a stack dump which gives you the entire call stack, so the information is available (getting warmer). In ColdFusion when you do error handling there is also a cfcatch variable called tagContext that contains a quasi call stack (looking better). So that means that we need to have access to the tagContext, and as far as I know it is only available when an error occurs, so lets cause one (now we are cooking!).
What we will do then in order to achieve our purpose is create a utility function that forces an error, at the same time we will wrap the cfthrow
The entire component looks something like this:
<cfcomponent>
<cffunction name="functionToCall" returntype="void">
<cfoutput>called from #calling()#<br></cfoutput>
</cffunction>
<cffunction name="calling" output="true" returntype="string">
<cftry>
<cfthrow type="Application">
<cfcatch type="any">
<!--- the current template is always the template that actually does the calling, since this cfc is extended the current template
will be the template that extended this component --->
<cfset thispage = getcurrenttemplatepath()>
<!--- I put the tag context in a local variable not exactly necessary but ... --->
<cfset variables.tagcontext= cfcatch.tagContext>
<cfloop from="1" to="#arraylen(variables.tagcontext)#" index="i">
<cfif variables.tagcontext[i].template eq thispage>
<!--- return the calling function or store the information somewhere --->
<Cfreturn #mid(variables.tagcontext[i].raw_trace,findnocase("$func",variables.tagcontext[i].raw_trace)+5,(findnocase(".runFunction",variables.tagcontext[i].raw_trace)-(findnocase("$func",variables.tagcontext[i].raw_trace)+5)))#>
</cfif>
</cfloop>
</cfcatch>
</cftry>
</cffunction>
</cfcomponent>
I saved the component as test.cfc and then created a second component (test2.cfc) that extends my first test component:
<cfcomponent extends="test">
<cffunction name="callTheFunctionInSuper" returntype="void">
<cfset functionToCall()>
</cffunction>
<cffunction name="callTheCalling" returntype="void">
<cfset callTheFunctionInSuper()>
</cffunction>
</cfcomponent>
Just to make things a bit more interesting and to see how my example code handles the tagContext I created two functions. callTheCalling calls callTheFunctionInSuper which then calls functionToCall.
I then created a cfm page to actually do the calling.
<cfscript>
mytest = createObject("component","com.gg.test1");
mytest.callTheCalling();
</cfscript>
And voila! The output looked like:
called from CALLTHEFUNCTIONINPARENT
Cautionary Note:
While an interesting idea to track the usage of a component I would not recommend deploying this to a production system without some serious thought. You would need to decide on the best approach as to provide the best response time (perhaps in a separate thread) so that execution would continue without interuption.
Published at DZone with permission of Gary Gilbert. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments