ColdFusion9 UDF nested invoke - coldfusion
I have a cfm file that is created in CF9. It has 7 <cfinvoke> statements all being the same except for the method and returnvariable. Is there a way to put this into a function or loop that would shorten my code and still work?
Example:
<cfsilent>
<cfinvoke component="financial.financial" method="getExecSummary" returnvariable="qExecSummary">
<cfinvokeargument name="level" value="#URL.level#" />
<cfinvokeargument name="stateGM" value="#URL.stateGM#" />
</cfinvoke>
<!---Added this to test if I can get more than one sheet to the Workbook--->
<cfinvoke component="financial.financial" method="getExecSummary331" returnvariable="qExecSummary331">
<cfinvokeargument name="level" value="#URL.level#" />
<cfinvokeargument name="stateGM" value="#URL.stateGM#" />
</cfinvoke>
</cfsilent>
This doesn't work:
<cffunction name="getSummary" output=true>
<cfargument name="method" required="true">
<cfargument name="returnvariable" required="true">
<cfargument name="level" required="true">
<cfargument name="stateGM" required="true">
<cfinvoke component="financial.financial" method="#method#" returnvariable="#returnvariable#">
<cfinvokeargument name="level" value="#level#" />
<cfinvokeargument name="stateGM" value="#stateGM#" />
</cfinvoke>
<cfreturn #returnvariable#>
</cffunction>
<cfset getSummary("getExecSummary","qExecSummary","#URL.level#","#URL.stateGM#")>
If someone could point me in the right direction? If this is even possible. I've been trying to find information on doing this, but I don't see anything yet.
I've read through this and this looking for help. I've also found several posts that didn't help either.
It is simpler to use createObject("component"), than cfinvoke. Just create an instance of the component. Then invoke the proper method and capture the result in the desired variable:
<!--- separated calls for readability -->
<cfset comp = createObject("component", "path.to.YourComponent")>
<cfset result = comp.firstMethod( "value1", "value2")>
IF the methods are all stateless (and properly scoped) you could simply reuse the same instance for all of the method calls:
<cfset comp = createObject("component", "path.to.YourComponent")>
<cfset result1 = comp.firstMethod( "value1", "value2" )>
<cfset result2 = comp.secondMethod( "value1", "value2" )>
<cfset result3 = comp.thirdMethod( "value1", "value2" )>
Also, as John Wish mentioned in the comments:
In CF9+ you can also use the new operator if you prefer like so:
<cfset comp = new path.to.YourComponent()>
It's worth noting that the new operator will also try and call an
init method in your CFC if you have one - although it doesn't need one
to work, other than that it works the same as:
createObject("component", "path.to.YourComponent")
Related
Convert Special Characters to HTML - ColdFusion
I need to convert a lot of special characters to their html format and I am trying to do this with a function that is using ReplaceList but something is wrong with the function or the values I am passing to it. This is the function <cffunction name="HtmlUnEditFormat" access="public" returntype="string" output="no" displayname="HtmlUnEditFormat" hint="Undo escaped characters"> <cfargument name="str" type="string" required="Yes" /> <cfscript> var lEntities = "&##xE7;,&##xF4;,&##xE2;,Î,Ç,È,Ó,Ê,&OElig,Â,«,»,À,É,≤,ý,χ,∑,′,ÿ,∼,β,⌈,ñ,ß,„,´,·,–,ς,®,†,⊕,õ,η,⌉,ó,,>,φ,∠,,α,∩,↓,υ,ℑ,³,ρ,é,¹,<,¢,¸,π,⊃,÷,ƒ,¿,ê, ,∅,∀, ,γ,¡,ø,¬,à,ð,ℵ,º,ψ,⊗,δ,ö,°,≅,ª,‹,♣,â,ò,ï,♦,æ,∧,◊,è,¾,&,⊄,ν,“,∈,ç,ˆ,©,á,§,—,ë,κ,∉,⌊,≥,ì,↔,∗,ô,∞,¦,∫,¯,½,¤,≈,λ,⁄,‘,…,œ,£,♥,−,ã,ε,∇,∃,ä,μ,¼, ,≡,•,←,«,‾,∨,€,µ,≠,∪,å,ι,í,⊥,¶,→,»,û,ο,‚,ϑ,∋,∂,”,℘,‰,²,σ,⋅,š,¥,ξ,±,ℜ,þ,〉,ù,√,,∴,↑,×, ,θ,⌋,⊂,⊇,ü,’,ζ,™,î,ϖ,,〈,˜,ú,¨,∝,ϒ,ω,↵,τ,⊆,›,∏,",,♠"; var lEntitiesChars = "ç,ô,â,Î,Ç,È,Ó,Ê,Œ,Â,«,»,À,É,?,ý,?,?,?,Ÿ,?,?,?,ñ,ß,„,´,·,–,?,®,‡,?,õ,?,?,ó,,>,?,?,?,?,?,?,?,?,³,?,é,¹,<,¢,¸,?,?,÷,ƒ,¿,ê,?,?,?,?,?,¡,ø,¬,à,ð,?,º,?,?,?,ö,°,?,ª,‹,?,â,ò,ï,?,æ,?,?,è,¾,&,?,?,“,?,ç,ˆ,©,á,§,—,ë,?,?,?,?,ì,?,?,ô,?,¦,?,¯,½,¤,?,?,?,‘,…,œ,£,?,?,ã,?,?,?,ä,?,¼, ,?,•,?,«,?,?,€,µ,?,?,å,?,í,?,¶,?,»,û,?,‚,?,?,?,”,?,‰,²,?,?,š,¥,?,±,?,þ,?,ù,?,?,?,?,×,?,?,?,?,?,ü,’,?,™,î,?,?,?,˜,ú,¨,?,?,?,?,?,?,›,?,"",?,?"; </cfscript> <cfreturn ReplaceList(arguments.str, lEntities, lEntitiesChars) /> </cffunction> This is how I am calling it: <cfoutput> <cfloop query="local.q" startrow="2"> #HtmlUnEditFormat(consultServiceType)# <br /> </cfloop> </cfoutput> These are the strings I am passing to it: Security? Security Guard® Alarm System© Private Investigator; I am not getting any errors back (I had a cftry in the function before) and the strings come back the same EDIT: I've tried using #FindNoCase('©',consultServiceType)# and is returning 0 so I guess something is wrong with the string I am passing in?
You're using CF11, did you try EncodeForHTML() ?
The accepted answer is the better approach (don't reinvent the wheel), but your function isn't working because you have lEntities and lEntitiesChars mixed up. <cffunction name="HtmlUnEditFormat" access="public" returntype="string" output="no" displayname="HtmlUnEditFormat" hint="Undo escaped characters"> <cfargument name="str" type="string" required="Yes" /> <cfscript> var lEntities = "&##xE7;,&##xF4;,&##xE2;,Î,Ç,È,Ó,Ê,&OElig,Â,«,»,À,É,≤,ý,χ,∑,′,ÿ,∼,β,⌈,ñ,ß,„,´,·,–,ς,®,†,⊕,õ,η,⌉,ó,,>,φ,∠,,α,∩,↓,υ,ℑ,³,ρ,é,¹,<,¢,¸,π,⊃,÷,ƒ,¿,ê, ,∅,∀, ,γ,¡,ø,¬,à,ð,ℵ,º,ψ,⊗,δ,ö,°,≅,ª,‹,♣,â,ò,ï,♦,æ,∧,◊,è,¾,&,⊄,ν,“,∈,ç,ˆ,©,á,§,—,ë,κ,∉,⌊,≥,ì,↔,∗,ô,∞,¦,∫,¯,½,¤,≈,λ,⁄,‘,…,œ,£,♥,−,ã,ε,∇,∃,ä,μ,¼, ,≡,•,←,«,‾,∨,€,µ,≠,∪,å,ι,í,⊥,¶,→,»,û,ο,‚,ϑ,∋,∂,”,℘,‰,²,σ,⋅,š,¥,ξ,±,ℜ,þ,〉,ù,√,,∴,↑,×, ,θ,⌋,⊂,⊇,ü,’,ζ,™,î,ϖ,,〈,˜,ú,¨,∝,ϒ,ω,↵,τ,⊆,›,∏,",,♠"; var lEntitiesChars = "ç,ô,â,Î,Ç,È,Ó,Ê,Œ,Â,«,»,À,É,?,ý,?,?,?,Ÿ,?,?,?,ñ,ß,„,´,·,–,?,®,‡,?,õ,?,?,ó,,>,?,?,?,?,?,?,?,?,³,?,é,¹,<,¢,¸,?,?,÷,ƒ,¿,ê,?,?,?,?,?,¡,ø,¬,à,ð,?,º,?,?,?,ö,°,?,ª,‹,?,â,ò,ï,?,æ,?,?,è,¾,&,?,?,“,?,ç,ˆ,©,á,§,—,ë,?,?,?,?,ì,?,?,ô,?,¦,?,¯,½,¤,?,?,?,‘,…,œ,£,?,?,ã,?,?,?,ä,?,¼, ,?,•,?,«,?,?,€,µ,?,?,å,?,í,?,¶,?,»,û,?,‚,?,?,?,”,?,‰,²,?,?,š,¥,?,±,?,þ,?,ù,?,?,?,?,×,?,?,?,?,?,ü,’,?,™,î,?,?,?,˜,ú,¨,?,?,?,?,?,?,›,?,"",?,?"; </cfscript> <cfreturn ReplaceList(arguments.str, lEntitiesChars, lEntities) /> </cffunction> <cfoutput>#htmluneditformat("Company?")#</cfoutput> Further, #ReplaceList()# in both ACF and Railo/Lucee recurse through the list, which means the order of the lists matter. With the fix I suggest, ? becomes ≤. A fix to this would be to move & and the code for it to the beginning of each list. Consider this simple piece of code <cfoutput>#replacelist("abc","a,b","b,c")#</cfoutput> You would probably expect the output to be "bcc", but that's not how ReplaceList works, it works something more like this <cfset sx = "abc"> <cfset listf = "a,b"> <cfset listr = "b,c"> <cfloop from="1" to="#listlen(listf)#" index="i"> <cfset sx = replace(sx,listgetat(listf,i),listgetat(listr,i),"ALL")> <!--- iteration one replaces a with b to make bbc ---> <!--- iteration two replaces b with c to make ccc ---> </cfloop> I'm not suggesting that someone use this code when CF has the built in functionality, I'm merely explaining why it doesn't work and a pitfall of ReplaceList().
How to pass a struct to Coldfusion CFC using CFINVOKE?
I have a CFC file which handles all of the emails I'm sending form an application (using Coldfusion8). I was using CFINVOKE to call the respective function inside this CFC and passed a struct with all user data along like so: <cfscript> var User.data = {}; User.data.name = "John Doe"; User.data.email = "john#doe.com"; ... </cfscript> // call mailer <cfinvoke component="mailer_user" method="say_hi"> <cfinvokeargument name="userData" value="#User.data#"> </cfinvoke> And inside my mailer.cfc <cffunction name="say_hi" access="public" output="false"> <cfargument name="userData" type="struct" required="true" /> .... For some reason this now fails and I can only get it to work if I pass fields seperately as cfargument, which is a a pain, since I'm passing a lot of data. Question: How can I get this to work using argumentCollection. Even if I CFINVOKE like this: <cfinvoke component="mailer_user" argumentcollection="#User.data#" method="say_hi"></cfinvoke> it still doesn't do a thing. I'm setting output flags right before the cfinvoke and after, as well as inside the "say_hi" function going in and out. I'm only getting the flag before CFINVOKE. Side note: This is all done through AJAX and I'm only getting back success="false" if my CFC has an error somewhere. I only work remotely on the system, so I can't set AJAX debugging in CFADMIN
As I typed the comment above it occurred to me what the problem is likely to be. You are passing in a structure to your function. You pass User.data which has name,email,blah,etc as keys in that structure. Those keys need to match the arguments in your function <cffunction name="say_hi" access="public" output="false"> <cfargument name="name" type="struct" required="true" /> <cfargument name="email" type="struct" required="true" /> <cfargument name="blah" type="struct" required="true" /> <cfargument name="etc" type="struct" required="true" /> If you want to pass in the structure as a argument, you would need to have a user.userData as your structure of user data and your function should be <cffunction name="say_hi" access="public" output="false"> <cfargument name="userData" type="struct" required="true" /> When you pass the collection as argumentCollection you should do argumentCollection="#user#", so that the userData part matches your cfargument in the function. Clear as mud?
I think you should stay in cfscript style by writing // call mailer mailUser = createObject("component", "mailer_user"); // or new mailer_user(); for CF9+ mailUser.say_hi(User.data); That should work, if it doesn't, it's somewhere else in your code. Try looking at the error log.
You should map the variable to the data you pass, then no problem sending a struct. Do it this way <cfset objMailer = createObject("component","mailer_user") /> <cfset objMailer.say_hi(userData:user.data)/> This works even in CF7.
Ok. There was a typo inside my mailer CFC, where I had a variable with "##". As is was inside my email text it went unnoticed... So you can pass a struct allright using this: <cfinvoke component="mailer_user" method="say_hi"> <cfinvokeargument name="userData" value="#User.userdata#"> </cfinvoke> and grab it inside your called function like so: <cffunction name="say_hi" access="public" output="false" hint=""> <cfargument name="userData" type="struct" required="true" hint="user data passed" /> <cfscript> var internalInfo = ""; var User = {}; User.userdata = userData; </cfscript> ... Maybe someone else can use the snippet.
Decode Numeric HTML Entities in ColdFusion?
I need a way to transform numeric HTML entities into their plain-text character equivalent. For example, I would like to turn the entity: é into the character: é Through some googling around I found a function called HtmlUnEditFormat, but this function only transforms named entities. Is there a way to decode numeric entities in ColdFusion?
Updated Answer: Thanks to Todd Sharp for pointing out a very simple way to do this, using the Apache Commons StringEscapeUtils library, which is packaged with CF (and Railo), so you can just do: <cfset Entity = "&##0233;" /> <cfset StrEscUtils = createObject("java", "org.apache.commons.lang.StringEscapeUtils") /> <cfset Character = StrEscUtils.unescapeHTML(Entity) /> Original Answer: That linked function is icky - there's no need to name them explicitly, and as you say it doesn't do numerics. Much simpler is to let CF do the work for you - using the XmlParse function: <cffunction name="decodeHtmlEntity" returntype="String" output="false"> <cfargument name="Entity" type="String" hint="&##<number>; or &<name>;" /> <cfreturn XmlParse('<xml>#Arguments.Entity#</xml>').XmlRoot.XmlText /> </cffunction> That one works with Railo, I can't remember if CF supports that syntax yet though, so you might need to change it to: <cffunction name="decodeHtmlEntity" returntype="String" output="false"> <cfargument name="Entity" type="String" hint="&##<number>; or &<name>;" /> <cfset var XmlDoc = XmlParse('<xml>#Arguments.Entity#</xml>') /> <cfreturn XmlDoc.XmlRoot.XmlText /> </cffunction>
Here's another function that will decode all the numeric html character entities in a string. It doesn't rely on xml parsing so it will work on strings that contain unbalanced xml tags. It's not efficient if the string has a large number of entities, but it's pretty good if there are none/few. I have only tested this on Railo, not AdobeCF. <cffunction name="decodeHtmlEntities" returntype="String" output="false"> <cfargument name="s" type="String"/> <cfset var LOCAL = {f = ReFind("&##([0-9]+);", ARGUMENTS.s, 1, true), map={}}> <cfloop condition="LOCAL.f.pos[1] GT 0"> <cfset LOCAL.map[mid(ARGUMENTS.s, LOCAL.f.pos[1], LOCAL.f.len[1])] = chr(mid(ARGUMENTS.s, LOCAL.f.pos[2], LOCAL.f.len[2]))> <cfset LOCAL.f = ReFind("&##([0-9]+);", ARGUMENTS.s, LOCAL.f.pos[1]+LOCAL.f.len[1], true)> </cfloop> <cfloop collection=#LOCAL.map# item="LOCAL.key"> <cfset ARGUMENTS.s = Replace(ARGUMENTS.s, LOCAL.key, LOCAL.map[LOCAL.key], "all")> </cfloop> <cfreturn ARGUMENTS.s /> </cffunction>
It should be quite easy to code one up yourself. Just edit the HtmlUNEditFormat() func you found, to include them to the end of the lEntities & lEntitiesChars.
I found this question while working with a method that, by black-box principle, can't trust that an incoming string is either HTML entity encoded or that it is not. I've adapted Peter Boughton's function so that it can be used safely on strings that haven't already been treated with HTML entities. (The only time this seems to matter is when loose ampersands - i.e. "Cats & Dogs" - are present in the target string.) This modified version will also fail somewhat gracefully on any unforseen XML parse error. <cffunction name="decodeHtmlEntity" returntype="string" output="false"> <cfargument name="str" type="string" hint="&##<number>; or &<name>;" /> <cfset var XML = '<xml>#arguments.str#</xml>' /> <cfset var XMLDoc = '' /> <!--- ampersands that aren't pre-encoded as entities cause errors ---> <cfset XML = REReplace(XML, '&(?!(\##\d{1,3}|\w+);)', '&', 'all') /> <cftry> <cfset XMLDoc = XmlParse(XML) /> <cfreturn XMLDoc.XMLRoot.XMLText /> <cfcatch> <cfreturn arguments.str /> </cfcatch> </cftry> </cffunction> This would support the following use case safely: <cffunction name="notifySomeoneWhoCares" access="private" returntype="void"> <cfargument name="str" type="string" required="true" hint="String of unknown preprocessing" /> <cfmail from="process#domain.com" to="someoneWhoCares#domain.com" subject="Comments from Web User" format="html"> Some Web User Spoke Thus:<br /> <cfoutput>#HTMLEditFormat(decodeHTMLEntity(arguments.str))#</cfoutput> </cfmail> </cffunction> This function is now incredibly useful for ensuring web-submitted content is entity-safe (think XSS) before it's sent out by email or submitted into a database table. Hope this helps.
Slurp an INI file in CFML
Could anyone find a way of improving this code a bit? I want to read in an INI file in one felt swoop and create a corresponding data structure. <cfset INIfile = expandPath(".") & "\jobs.ini"> <cfset profile = GetProfileSections(INIfile)> <cfloop collection="#profile#" item="section"> <cfloop list="#profile[section]#" index="entry"> <cfset app.workflow[section][entry]=GetProfileString(INIfile, section, entry) > </cfloop> </cfloop>
I don't believe you can improve this using CFML power. Do you need to parse huge ini files? If not, why would you like to improve your code, it looks pretty straightforward for me. Other possible (though common for CF) solution is to try pure Java. See this SO thread for pure Java examples. P.S. BTW, in case of special performance needs you should consider using another storage for configuration. Simple SELECT query to the old good MySQL can be much faster for large datasets.
To expand on ryber's comment, you might consider using this approach instead. I'm assuming you're using CF8.01 or later, as I make use of nested implicit structure notation. This could easily be converted to CF7/6/etc syntax, but wouldn't be as clean or concise. Again, this only applies if your ini file isn't used by any other applications or people, and doesn't need to be in ini format. settings.cfm: <cfset variables.settings = { fooSection = { fooKey = 'fooVal', fooNumber = 2, }, fooSection2 = { //... }, fooSection3 = { //... } } /> Application.cfc: (only the onApplicationStart method) <cffunction name="onApplicationStart"> <cfinclude template="settings.cfm" /> <cfset application.workflow = variables.settings /> <cfreturn true /> </cffunction> In addition, I've use the CFEncode application to encrypt the contents of settings.cfm. It won't protect you from someone who gets a copy of the file and wants to see what its encrypted contents are (the encryption isn't that strong, and there are ways to see the contents without decrypting it), but if you just want to keep some nosy people out, it adds a little extra barrier-to-entry that might deter some people. Update: Since you just left a comment that says you are on CF7, here's native CF7 syntax: settings.cfm: <cfset variables.settings = StructNew() /> <cfset variables.settings.fooSection = StructNew() /> <cfset variables.settings.fooSection.fooKey = 'fooVal' /> <cfset variables.settings.fooSection.fooNumber = 2 /> <!--- ... ---> Alternatively, you could use JSONUtil and CFSaveContent to continue to use a JSON-looking approach (similar to my original syntax), but on CF7: <cfsavecontent variable="variables.jsonSettings"> { fooSection = { fooKey = 'fooVal', fooNumber = 2, }, fooSection2 = { //... }, fooSection3 = { //... } }; </cfsavecontent> <cfset variables.settings = jsonUtil.deserializeFromJSON(variables.jsonSettings) />
I created a CFC that I use in a bunch of apps. You give it an ini filepath when you init it and it creates a structure based on the ini file. It also optionally keeps the structure flat or creates sub-structures based on the [Sections] in the ini file. You can then either use its getSetting() method to get individual methods or getAllSettings() to return the entire structure. You may find it helpful. <cfcomponent hint="converts INI file to a structure"> <cfset variables.settings=structNew() /> <cffunction name="init" access="public" output="false" returntype="any"> <cfargument name="configurationFile" type="string" required="yes" /> <cfargument name="useSections" default="false" type="boolean" /> <cfset var local=structNew() /> <cfif fileExists(arguments.configurationFile)> <!--- Get the [sections] in the .INI file ---> <cfset local.sectionStruct=getProfileSections(arguments.configurationFile) /> <!--- Loop over each of these sections in turn ---> <cfloop collection="#local.sectionStruct#" item="local.item"> <cfset local.workingStruct=structNew() /> <cfloop list="#local.sectionStruct[local.item]#" index="local.key"> <!--- Loop over the keys in the current section and add the key/value to a temporary structure ---> <cfset local.workingStruct[local.key]=getProfileString(arguments.configurationFile,local.item,local.key) /> </cfloop> <cfif arguments.useSections> <!--- Copy the temporary structure to a key in the setting structure for the current section ---> <cfset variables.settings[local.item]=duplicate(local.workingStruct) /> <cfelse> <!--- Append the temporary structure to the setting structure ---> <cfset structAppend(variables.settings,local.workingStruct,"yes") /> </cfif> </cfloop> <cfelse> <cfthrow message="Configuration file not found. Must use fully-qualified path." extendedinfo="#arguments.configurationFile#" /> </cfif> <cfreturn this> </cffunction> <cffunction name="getAllSettings" access="public" output="false" returntype="struct"> <cfreturn variables.settings> </cffunction> <cffunction name="getSetting" access="public" output="false" returntype="string"> <cfargument name="settingName" required="yes" type="string" /> <cfset var returnValue="" /> <cfif structKeyExists(variables.settings,arguments.settingName)> <cfset returnValue=variables.settings[arguments.settingName] /> <cfelse> <cfthrow message="No such setting '#arguments.settingName#'." /> </cfif> <cfreturn returnValue> </cffunction> </cfcomponent>
Can a ColdFusion cfc method determine its own name?
I am creating an API, and within each method I make a call to a logging method for auditing and troubleshooting. Something like: <cffunction name="isUsernameAvailable"> <cfset logAccess(request.userid,"isUsernameAvailable")> ...... </cffunction> I'd like to avoid manually repeating the method name. Is there a way to programatically determine it? I've looked at GetMetaData() but it only returns info about the component (including all the methods) but nothing about which method is currently being called.
So now 3 ways. If you are using ColdFusion 9.0 or higher there is now a function named GetFunctionCalledName(). It will return what you are looking for. http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WS7cc222be8a31a47d-6e8b7083122cebfc8f2-8000.html OR Use ColdSpring and Aspect Oriented Programming (http://www.coldspringframework.org/coldspring/examples/quickstart/index.cfm?page=aop) to handle this for you. OR Use a cfthrow to generate a stack trace that has the information for you: <cffunction name="determineFunction" output="FALSE" access="public" returntype="string" hint="" > <cfset var functionName ="" /> <cfset var i = 0 /> <cfset var stackTraceArray = "" /> <cftry> <cfthrow /> <cfcatch type="any"> <cfset stacktraceArray = ListToArray(Replace(cfcatch.stacktrace, "at ", " | ", "All"), "|") /> <!---Rip the right rows out of the stacktrace ---> <cfloop index ="i" to="1" from="#ArrayLen(stackTraceArray)#" step="-1"> <cfif not findNoCase("runFunction", stackTraceArray[i]) or FindNoCase("determineFunction", stackTraceArray[i])> <cfset arrayDeleteAt(stackTraceArray, i) /> </cfif> </cfloop> <!---Whittle down the string to the func name ---> <cfset functionName =GetToken(stacktraceArray[1], 1, ".") /> <cfset functionName =GetToken(functionName, 2, "$")/> <cfset functionName =ReplaceNoCase(functionName, "func", "", "once")/> <cfreturn functionName /> </cfcatch> </cftry></cffunction> My recommendation would be use getFunctionCalledName, or if not on CF 9 ColdSpring, as it will probably buy you some other things.
I agree w/ tpryan. ColdSpring makes this very easy. However, here is another alternative. Instead of parsing the stack trace, you can parse the CFC file itself. <cffunction name="foo" displayname="foo" hint="this is just a test function" access="public" returntype="string"> <cfset var test = getFunctionName(getMetaData().path, getPageContext().getCurrentLineNo()) /> <cfreturn test /> </cffunction> <cffunction name="getFunctionName" hint="returns the function name based on the line number" access="public" returntype="string"> <cfargument name="filepath" type="string" required="true" /> <cfargument name="linenum" type="any" required="true" /> <cfset var line = "" /> <cfset var functionName = "" /> <cfset var i = 1 /> <!---- loop over CFC by line ----> <cfloop file="#ARGUMENTS.filepath#" index="line"> <cfif findNoCase('cffunction', line, 1)> <cfset functionName = line /> </cfif> <cfif i EQ ARGUMENTS.linenum><cfbreak /></cfif> <cfset i++ /> </cfloop> <!---- parse function name ----> <cfset functionName = REMatchNoCase("(\bname=[""|'])+[a-z]*[""|']", functionName) /> <cfset functionName = REMatchNoCase("[""']+[a-z]*[""']", functionName[1]) /> <cfset functionName = ReReplaceNoCase(functionName[1], "[""']", "", "all") /> <!---- return success ----> <cfreturn functionName /> </cffunction> The above is written for ColdFusion 8. CFLOOP added support for looping over files line by line (and doesn't read the entire file into memory). I did a few tests comparing the stack trace method vs. file parsing. Both performed equally well on a small CFC being called directly from a single CFM template. Obviously if you have very large CFCs the parsing method might be a bit slower. On the other hand, if you have a large stack trace (like if you are using any of the popular frameworks) then file parsing may be faster. -= Viva ColdFusion =-
Well you might try this: <cffunction name="getFunctionName" returntype="any"> <cfset meta =getMetaData(this)> <cfreturn meta.functions[numberOfFunction].name> </cffunction> I've tried various things, and this is not accurate as the functions seem to be added to the array of functions in reverse alphabetical order. This seems limiting (and not solving the problem). I would imagine some native java code could be invoked, but i'm going to need to look into that. This and This look like interesting reading on related internal functions. Re: The other answer on coldspring. I found this in depth article on function metadata with coldspring. Related question : How to get the name of the component that’s extending mine in ColdFusion?
I thought of another way that could work. Setup an OnMissingMethod something like this: <cffunction name="onMissingMethod"> <cfargument name="missingMethodName" type="string"> <cfargument name="missingMethodNameArguments" type="struct"> <cfset var tmpReturn = ""> <cfset var functionToCallName = "Hidden" & Arguments.missingMethodName> <cfset arguments.missingMethodArguments.calledMethodName = Arguments.missingMethodName> <cfinvoke method="#functionToCallName#" argumentcollection="#Arguments.missingMethodArguments#" returnvariable="tmpReturn" /> <cfreturn tmpReturn> </cffunction> Then name each of the regular methods with a prefix ("Hidden" in this example), and mark them as private. So my initial example would become: <cffunction name="HiddenisUsernameAvailable" access="private"> <cfset logAccess(request.userid,Arguments.calledMethodName)> ...... </cffunction> Now all the calls will be intercepted by onMissingMethod, which will add the method name to the arguments that get passed to the real method. The downsides I see to this are that introspection no longer works properly, and you must be using named arguments to call all your functions. If you are not using named arguments, the args will randomly change order in the missingMethodNameArguments structure.
getFunctionCalledName() gives you the name of the active method.