diff --git a/framework/aop.cfc b/framework/aop.cfc index 95991adf..6653124a 100644 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -119,7 +119,7 @@ component extends="framework.ioc" { { // build the interceptor array: var beanName = listLast(arguments.dottedPath, "."); - var beanNames = getAliases(beanName); + var beanNames = getAliases(arguments.dottedPath); var beanTypes = ""; var interceptDefinition = ""; var interceptedBeanName = ""; @@ -128,6 +128,8 @@ component extends="framework.ioc" { arrayPrepend(beanNames, beanName); + // Removing duplicate beanNames + beanNames = listToArray(listRemoveDuplicates(arrayToList(beanNames),",",true) ); // Grab all name based interceptors that match. for (interceptedBeanName in beanNames) @@ -185,7 +187,7 @@ component extends="framework.ioc" { var interceptedBeanName = ""; var interceptorDefinition = {}; var beanName = listLast(arguments.dottedPath, "."); - var beanNames = getAliases(beanName); + var beanNames = getAliases(arguments.dottedPath); var beanTypes = ""; @@ -234,29 +236,21 @@ component extends="framework.ioc" { } - /** Finds all aliases for the given beanName. */ - private array function getAliases(string beanName) + /** Finds all aliases for the given dottedPath. */ + private array function getAliases(string dottedPath) { var aliases = []; var beanData = ""; var key = ""; - - if (structKeyExists(variables.beanInfo, arguments.beanName)) + for (key in variables.beanInfo) { - beanData = variables.beanInfo[arguments.beanName]; - - for (key in variables.beanInfo) + // Same cfc dotted path, must be an alias. + if ( + structKeyExists(variables.beanInfo[key], "cfc") && + variables.beanInfo[key].cfc == arguments.dottedPath) { - // Same cfc dotted path, must be an alias. - if ( - key != arguments.beanName && - structKeyExists(variables.beanInfo[key], "cfc") && - structKeyExists(variables.beanInfo[arguments.beanName], "cfc") && - variables.beanInfo[key].cfc == variables.beanInfo[arguments.beanName].cfc) - { - arrayAppend(aliases, key); - } + arrayAppend(aliases, key); } } diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 11e73eff..e995e756 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -535,6 +535,7 @@ component { } catch ( any e ) { // assume bad path - ignore it, cfcs is empty list } + local.beansWithDuplicates = ""; for ( var cfcOSPath in cfcs ) { var cfcPath = replace( cfcOSPath, chr(92), '/', 'all' ); // watch out for excluded paths: @@ -561,18 +562,26 @@ component { if ( structKeyExists( metadata.metadata, "type" ) && metadata.metadata.type == "interface" ) { continue; } - if ( structKeyExists( variables.beanInfo, beanName ) ) { - if ( variables.config.omitDirectoryAliases ) { - throw '#beanName# is not unique (and omitDirectoryAliases is true)'; + + if ( variables.config.omitDirectoryAliases ) { + if ( structKeyExists( variables.beanInfo, beanName ) ) { + throw '#beanName# is not unique'; } - structDelete( variables.beanInfo, beanName ); - variables.beanInfo[ beanName & singleDir ] = metadata; - } else { variables.beanInfo[ beanName ] = metadata; - if ( !variables.config.omitDirectoryAliases ) { - variables.beanInfo[ beanName & singleDir ] = metadata; + } else { + if ( listFindNoCase(local.beansWithDuplicates, beanName) ) {} + else if ( structKeyExists( variables.beanInfo, beanName ) ) { + structDelete( variables.beanInfo, beanName ); + local.beansWithDuplicates = listAppend(local.beansWithDuplicates, beanName); + } else { + variables.beanInfo[ beanName ] = metadata; + } + if ( structKeyExists( variables.beanInfo, beanName & singleDir ) ) { + throw '#beanName & singleDir# is not unique'; } + variables.beanInfo[ beanName & singleDir ] = metadata; } + } catch ( any e ) { // wrap the exception so we can add bean name for debugging // this trades off any stack trace information for the bean name but diff --git a/tests/CombinedInterceptorsTestIssue518.cfc b/tests/CombinedInterceptorsTestIssue518.cfc new file mode 100644 index 00000000..789c8f26 --- /dev/null +++ b/tests/CombinedInterceptorsTestIssue518.cfc @@ -0,0 +1,289 @@ +component extends="mxunit.framework.TestCase" { + + function TestBeforeAroundAfterInterception() { + //Putting it all together What happens when you call all of them? + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + //add an Interceptor + + bf.intercept("ReverseService", "BeforeInterceptor"); + bf.intercept("ReverseService", "AroundInterceptor"); + bf.intercept("ReverseService", "AfterInterceptor"); + + rs = bf.getBean("ReverseService"); + result = rs.doReverse("Hello!"); + + + AssertEquals("around," & Reverse("beforeHello!") & ",around", result); + AssertEquals(4, arrayLen(request.callstack)); + AssertEquals("before,around,doReverse,after", arrayToList(request.callstack)); + } + + + function TestInitMethods() { + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {initMethod = "configure"}); + + bf.intercept("advReverse", "BeforeInterceptor"); + + rs = bf.getBean("advReverseService"); + result = rs.doWrap("Hello!"); + + // First test does not intercept the (init, set..., or initMethod) methods. + AssertEquals(9, arrayLen(request.callstack)); + AssertEquals( "init,setStackLog,configure,before,dowrap,before,dofront,before,dorear", + arrayToList(request.callstack), + "This test shows that the (init, set..., and configure) methods are by default ignored."); + + + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {initMethod = "configure"}); + + bf.intercept("advReverse", "BeforeInterceptor", "init,configure,setStackLog,doWrap"); + + rs = bf.getBean("advReverseService"); + result = rs.doWrap("Hello!"); + + // Explicitly intercept the (init, set..., or initMethod) methods. + AssertEquals(10, arrayLen(request.callstack)); + AssertEquals( "before,init,before,setStackLog,before,configure,before,dowrap,dofront,dorear", + arrayToList(request.callstack), + "This test shows that the (init, set..., and configure) methods can be explicitly intercepted."); + } + + + function TestInterceptOnRegex() { + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {initMethod = "configure"}); + + //add an Interceptor + bf.intercept("/^reverse.*$/", "BeforeInterceptor"); + + ars = bf.getBean("advReverseService"); + rs = bf.getBean("reverse"); + as = bf.getBean("array"); + + + result = ars.doWrap("Hello!"); + result2 = rs.doReverse("Hello!"); + result3 = as.doListToArray("dog,cat,mouse"); + + + AssertEquals("front-Hello!-rear", result); + AssertEquals("!olleHerofeb", result2); + AssertTrue(isArray(result3)); + AssertEquals("dog,cat,mouse", arrayToList(result3)); + + AssertEquals(9, arrayLen(request.callstack)); + AssertEquals("init,setStackLog,configure,doWrap,doFront,doRear,before,doReverse,doListToArray", arrayToList(request.callstack)); + } + + + function TestInterceptOnType() { + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {initMethod = "configure"}); + + //add an Interceptor + bf.interceptByType("string", "BeforeInterceptor", "doReverse,doForward,doWrap"); + + ars = bf.getBean("advReverseService"); + rs = bf.getBean("reverse"); + as = bf.getBean("array"); + + + result = ars.doWrap("Hello!"); + result2 = rs.doReverse("Hello!"); + result3 = as.doListToArray("dog,cat,mouse"); + + + AssertEquals("front-beforeHello!-rear", result); + AssertEquals("!olleHerofeb", result2); + AssertTrue(isArray(result3)); + AssertEquals("dog,cat,mouse", arrayToList(result3)); + + AssertEquals(10, arrayLen(request.callstack)); + AssertEquals("init,setStackLog,configure,before,doWrap,doFront,doRear,before,doReverse,doListToArray", arrayToList(request.callstack)); + } + + + function TestMultipleBeforeInterceptions() { + //Multiple Before Advisors + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + + //Need to create different Before interceptors + bf.declareBean("BeforeInterceptorA", "tests.issue518.interceptors.aop.BeforeInterceptor", true, {name = "beforeA"}); + bf.declareBean("BeforeInterceptorB", "tests.issue518.interceptors.aop.BeforeInterceptor", true, {name = "beforeB"}); + bf.declareBean("BeforeInterceptorC", "tests.issue518.interceptors.aop.BeforeInterceptor", true, {name = "beforeC"}); + + bf.intercept("ReverseService", "BeforeInterceptorA"); + bf.intercept("ReverseService", "BeforeInterceptorB"); + bf.intercept("ReverseService", "BeforeInterceptorC"); + + rs = bf.getBean("ReverseService"); + result = rs.doReverse("Hello!"); + + AssertEquals(reverse("beforebeforebeforeHello!"), result); + AssertEquals(4, arrayLen(request.callstack)); + AssertEquals("beforeA,beforeB,beforeC,doReverse", arrayToList(request.callstack)); + } + + + function TestMultipleAfterInterceptors() { + //Multiple After Advisors + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + + //Need to create different After interceptors + bf.declareBean("AfterInterceptorA", "tests.issue518.interceptors.aop.AfterInterceptor", true, {name = "afterA"}); + bf.declareBean("AfterInterceptorB", "tests.issue518.interceptors.aop.AfterInterceptor", true, {name = "afterAlterResultB"}); + bf.declareBean("AfterInterceptorC", "tests.issue518.interceptors.aop.AfterInterceptor", true, {name = "afterC"}); + + + bf.intercept("ReverseService", "AfterInterceptorA"); + bf.intercept("ReverseService", "AfterInterceptorB"); + bf.intercept("ReverseService", "AfterInterceptorC"); + + + rs = bf.getBean("ReverseService"); + result = rs.doReverse("Hello!"); + + AssertEquals(reverse("Hello!") & ",afterAlterResultB", result); + AssertEquals(4, arrayLen(request.callstack)); + AssertEquals("doReverse,afterA,afterAlterResultB,afterC", arrayToList(request.callstack)); + } + + + function TestMultipleAroundInterceptors() { + //Multiple Around Advisors + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + + + //Need to create different After interceptors + bf.declareBean("AroundInterceptorA", "tests.issue518.interceptors.aop.AroundInterceptor", true, {name = "aroundA"}); + bf.declareBean("AroundInterceptorB", "tests.issue518.interceptors.aop.AroundInterceptor", true, {name = "aroundB"}); + bf.declareBean("AroundInterceptorC", "tests.issue518.interceptors.aop.AroundInterceptor", true, {name = "aroundC"}); + + + bf.intercept("ReverseService", "AroundInterceptorA"); + bf.intercept("ReverseService", "AroundInterceptorB"); + bf.intercept("ReverseService", "AroundInterceptorC"); + rs = bf.getBean("ReverseService"); + + result = rs.doReverse("Hello!"); + + AssertEquals("aroundA,aroundB,aroundC," & reverse("Hello!") & ",aroundC,aroundB,aroundA", result); + AssertEquals(4, arrayLen(request.callstack)); + AssertEquals("aroundA,aroundB,aroundC,doReverse", arrayToList(request.callstack)); + } + + + function TestMethodMatches() { + bf = new framework.ioc('/tests/issue518', {}); + rs = bf.getBean("Reverse"); + + proxy = new framework.beanProxy(rs, [], {}); + makePublic( proxy, "methodMatches" ); + + AssertFalse(proxy.methodMatches("doForward", "doReverse")); + AssertTrue(proxy.methodMatches("doForward", "")); + AssertTrue(proxy.methodMatches("doForward", "*")); + AssertFalse(proxy.methodMatches("doForward", "doReverse,")); + AssertTrue(proxy.methodMatches("doForward", "doReverse,doForward")); + } + + + function TestNamedMethodInterceptions() { + //Named Method Interceptions + + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + //add an Interceptor + bf.intercept("ReverseService", "BeforeInterceptor", "doReverse"); + + rs = bf.getBean("ReverseService"); + + // This should be intercepted. + result = rs.doReverse("Hello!"); + + // This shoud not be intercepted. + result2 = rs.doForward("Hello!"); + + + // This should be intercepted. + result3 = rs.doReverse("Hello!"); + + AssertEquals(reverse("beforeHello!"), result); + AssertEquals("hello!", result2); + AssertEquals(reverse("beforeHello!"), result3); + AssertEquals(5, arrayLen(request.callstack)); + AssertEquals("before,doReverse,doForward,before,doReverse", arrayToList(request.callstack)); + } + + + function TestOnErrorInterceptors() { + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + rs = bf.getBean("ReverseService"); + result2 = rs.doForward("Hello!"); + + + AssertEquals("Hello!", result2); + AssertEquals(1, arrayLen(request.callstack)); + AssertEquals("doForward", arrayToList(request.callstack)); + + + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + //add an Interceptor + bf.intercept("ReverseService", "ErrorInterceptor", "throwError"); + + rs = bf.getBean("ReverseService"); + rs.throwError(); + + AssertEquals(2, arrayLen(request.callstack)); + AssertEquals("throwError,onError", arrayToList(request.callstack)); + } + + + function TestPrivateMethodInterceptors() { + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {initMethod = "configure"}); + + //add an Interceptor + bf.intercept("advReverseService", "BeforeInterceptor", "doFront"); + + rs = bf.getBean("advReverseService"); + + result = rs.doWrap("Hello!"); + + AssertEquals("front-beforeHello!-rear", result); + AssertEquals(7, arrayLen(request.callstack)); + AssertEquals("init,setStackLog,configure,doWrap,before,doFront,doRear", arrayToList(request.callstack)); + } + + + function TestSingleInterceptorOnMultipleObjects() { + //Multiple Around Advisors + request.callstack = []; //reset + bf = new framework.aop('/tests/issue518', {}); + + + //Need to create different After interceptors + bf.declareBean("AroundInterceptorA", "tests.issue518.interceptors.aop.AroundInterceptor", true, {name = "aroundA"}); + + + bf.intercept("advReverse", "AroundInterceptorA", "doWrap"); + bf.intercept("ReverseService", "AroundInterceptorA", "doReverse"); + + rs = bf.getBean("ReverseService"); + ars = bf.getBean("advReverseService"); + + result = ars.doWrap(rs.doReverse("Hello!")); + + AssertEquals("aroundA,front-aroundA," & reverse("Hello!") & ",aroundA-rear,aroundA", result); + AssertEquals(8, arrayLen(request.callstack)); + AssertEquals("init,setStackLog,aroundA,doReverse,aroundA,doWrap,doFront,doRear", arrayToList(request.callstack)); + } +} diff --git a/tests/issue518/Log.cfc b/tests/issue518/Log.cfc new file mode 100644 index 00000000..92f22c9d --- /dev/null +++ b/tests/issue518/Log.cfc @@ -0,0 +1,11 @@ +component{ + + function init(){ + return this; + } + + function logMessage(message, severity="information"){ + writelog(message, severity); + } + +} \ No newline at end of file diff --git a/tests/issue518/daos/advReverse.cfc b/tests/issue518/daos/advReverse.cfc new file mode 100644 index 00000000..cfe9c8b5 --- /dev/null +++ b/tests/issue518/daos/advReverse.cfc @@ -0,0 +1,46 @@ +component displayname="advReverseService" extends="tests.issue518.services.Reverse" accessors="true" output="false" { + + + // PUBLIC METHODS + public function configure() { + getStackLog().log("wrong-configure"); + return this; + } + + + public function doWrap(string input) { + getStackLog().log("wrong-doWrap"); + return doRear(doFront(arguments.input)); + } + + + public function init() { + // stackLog does not exist at this point. + arrayAppend(request.callStack, "wrong-init"); + + return super.init(); + } + + + public function setStackLog(any stackLog) { + // stackLog does not exist at this point. + arrayAppend(request.callStack, "wrong-setStackLog"); + + variables.stackLog = arguments.stackLog; + } + + + + + // PRIVATE METHODS + private function doFront(string input) { + getStackLog().log("wrong-doFront"); + return "front-" & arguments.input; + } + + + private function doRear(string input) { + getStackLog().log("wrong-doRear"); + return arguments.input & "-rear"; + } +} diff --git a/tests/issue518/interceptors/BasicInterceptor.cfc.test b/tests/issue518/interceptors/BasicInterceptor.cfc.test new file mode 100644 index 00000000..9573ff5c --- /dev/null +++ b/tests/issue518/interceptors/BasicInterceptor.cfc.test @@ -0,0 +1,39 @@ +/** +* +* @author @markdrew +* @description This is a demo interceptor +* +*/ +component output="false" displayname="BasicInterceptor" { + + this.name = "A"; + + public function init(name="A"){ + this.name = name; + return this; + } + + + //basically it's onMissingMethod! + function before(method, args, target){ + param name="request.callstack" default="#[]#"; + + arguments.args.1 = "before" & arguments.args.1 + } + + function after(){ + param name="request.callstack" default="#[]#"; + arguments.result = arguments.result & "after"; + + } + + function onMethod(){ + param name="request.callstack" default="#[]#"; + dump(var=arguments, label="onMethod"); + } + + function onError(){ + param name="request.callstack" default="#[]#"; + dump(var=arguments, label="onError"); + } +} \ No newline at end of file diff --git a/tests/issue518/interceptors/aop/AfterInterceptor.cfc b/tests/issue518/interceptors/aop/AfterInterceptor.cfc new file mode 100644 index 00000000..1afc9395 --- /dev/null +++ b/tests/issue518/interceptors/aop/AfterInterceptor.cfc @@ -0,0 +1,18 @@ +component displayname="AfterInterceptor" extends="interceptor" accessors="true" output="false" { + + + function init(name="after") { + this.name=name; + } + + + function after(target, method, args, result) { + getStackLog().log(this.name); + + // Demonstrate that we can alter the result. + if (findNoCase("alter", this.name) && structKeyExists(arguments, "result") && !isNull(arguments.result)) + { + return arguments.result & "," & this.name; + } + } +} \ No newline at end of file diff --git a/tests/issue518/interceptors/aop/AroundInterceptor.cfc b/tests/issue518/interceptors/aop/AroundInterceptor.cfc new file mode 100644 index 00000000..e4ad95fa --- /dev/null +++ b/tests/issue518/interceptors/aop/AroundInterceptor.cfc @@ -0,0 +1,27 @@ +component displayname="AroundInterceptor" extends="interceptor" accessors="true" output="false" { + + + function init(name="around") { + this.name=name; + } + + + function around(target, method, args) { + getStackLog().log(this.name); + + local.result = proceed(arguments.target, arguments.method, arguments.args); + + // This runs on 'set...' methods as well for properties. Limit to simple result calls. + if (structKeyExists(local, "result") && !isNull(local.result) && isSimpleValue(local.result)) + { + return this.name & "," & local.result & "," & this.name; + } + else + { + writeDump(var = isNull(arguments.target)); + writeDump(var = isNull(arguments.target.getStackLog())); + writeDump(var = arguments.method); + writeDump(var = structKeyList(arguments.target), abort = true); + } + } +} diff --git a/tests/issue518/interceptors/aop/BeforeInterceptor.cfc b/tests/issue518/interceptors/aop/BeforeInterceptor.cfc new file mode 100644 index 00000000..f3099685 --- /dev/null +++ b/tests/issue518/interceptors/aop/BeforeInterceptor.cfc @@ -0,0 +1,20 @@ +component displayname="BeforeInterceptor" extends="interceptor" accessors="true" output="false" { + + + function init(name="before") { + this.name=name; + } + + + function before(target, method, args) { + getStackLog().log(this.name); + + translateArgs(target, method, args, true); + + // Demonstrate that we can alter the arguments before the method call. + if (structKeyExists(arguments.args, "input")) + { + arguments.args.input = "before" & arguments.args.input; + } + } +} \ No newline at end of file diff --git a/tests/issue518/interceptors/aop/ErrorInterceptor.cfc b/tests/issue518/interceptors/aop/ErrorInterceptor.cfc new file mode 100644 index 00000000..f51908db --- /dev/null +++ b/tests/issue518/interceptors/aop/ErrorInterceptor.cfc @@ -0,0 +1,12 @@ +component output="false" { + + this.name = "onError"; + function init(name="onError"){ + this.name=name; + } + + function onError(method,args,target, error){ + ArrayAppend(request.callstack, this.name); + + } +} \ No newline at end of file diff --git a/tests/issue518/interceptors/aop/interceptor.cfc b/tests/issue518/interceptors/aop/interceptor.cfc new file mode 100644 index 00000000..a1f987a0 --- /dev/null +++ b/tests/issue518/interceptors/aop/interceptor.cfc @@ -0,0 +1,8 @@ +component displayname="interceptor" accessors="true" output="false" { + + + property name="stackLog"; + + + this.name = ""; +} diff --git a/tests/issue518/interceptors/example/Logger.cfc b/tests/issue518/interceptors/example/Logger.cfc new file mode 100644 index 00000000..098be17e --- /dev/null +++ b/tests/issue518/interceptors/example/Logger.cfc @@ -0,0 +1,14 @@ +component { + + function init(LogService){ + this.logService = logService; + return this; + } + function before(methodname, args, target){ + this.logService.logMessage("Before:" & arguments.args.input); + + } + function after(result, methodname, args, target){ + this.logService.logMessage("After:" & arguments.result); + } +} \ No newline at end of file diff --git a/tests/issue518/service.cfc b/tests/issue518/service.cfc new file mode 100644 index 00000000..97465f72 --- /dev/null +++ b/tests/issue518/service.cfc @@ -0,0 +1,15 @@ +component displayname="service" accessors="true" output="false" { + + + property name="stackLog"; + + + public function getServiceName() { + return listLast(getMetadata(this)); + } + + + public function init() { + return this; + } +} diff --git a/tests/issue518/services/Reverse.cfc b/tests/issue518/services/Reverse.cfc new file mode 100644 index 00000000..06c2b3b5 --- /dev/null +++ b/tests/issue518/services/Reverse.cfc @@ -0,0 +1,22 @@ +component displayname="reverseService" extends="tests.issue518.string" accessors="true" output="false" { + + + public function doForward(string input) { + //I double reverse a string... i.e. do nothing! + getStackLog().log("doForward"); + return reverse(reverse(arguments.input)); + } + + + public function doReverse(string input) { + getStackLog().log("doReverse"); + return reverse(arguments.input); + } + + + public function throwError() { + //This is just to throw an error + getStackLog().log("throwError"); + throw "I AM AN EVIL ERROR YOU WANT TO TRAP!"; + } +} diff --git a/tests/issue518/services/advReverse.cfc b/tests/issue518/services/advReverse.cfc new file mode 100644 index 00000000..21bd3d4d --- /dev/null +++ b/tests/issue518/services/advReverse.cfc @@ -0,0 +1,46 @@ +component displayname="advReverseService" extends="Reverse" accessors="true" output="false" { + + + // PUBLIC METHODS + public function configure() { + getStackLog().log("configure"); + return this; + } + + + public function doWrap(string input) { + getStackLog().log("doWrap"); + return doRear(doFront(arguments.input)); + } + + + public function init() { + // stackLog does not exist at this point. + arrayAppend(request.callStack, "init"); + + return super.init(); + } + + + public function setStackLog(any stackLog) { + // stackLog does not exist at this point. + arrayAppend(request.callStack, "setStackLog"); + + variables.stackLog = arguments.stackLog; + } + + + + + // PRIVATE METHODS + private function doFront(string input) { + getStackLog().log("doFront"); + return "front-" & arguments.input; + } + + + private function doRear(string input) { + getStackLog().log("doRear"); + return arguments.input & "-rear"; + } +} diff --git a/tests/issue518/services/array.cfc b/tests/issue518/services/array.cfc new file mode 100644 index 00000000..2e6a4880 --- /dev/null +++ b/tests/issue518/services/array.cfc @@ -0,0 +1,9 @@ +component displayname="arrayService" extends="tests.issue518.service" accessors="true" output="false" { + + + public array function doListToArray(string list) { + //I double reverse a string... i.e. do nothing! + getStackLog().log("doListToArray"); + return listToArray(arguments.list); + } +} diff --git a/tests/issue518/stackLog.cfc b/tests/issue518/stackLog.cfc new file mode 100644 index 00000000..a9232aef --- /dev/null +++ b/tests/issue518/stackLog.cfc @@ -0,0 +1,17 @@ +component displayname="stackLog" extends="service" output="false" { + + + public function init() { + if (!structKeyExists(request, "callStack")) + { + request["callStack"] = []; + } + + return super.init(); + } + + + public function log(string message) { + arrayAppend(request.callStack, message); + } +} diff --git a/tests/issue518/string.cfc b/tests/issue518/string.cfc new file mode 100644 index 00000000..e7bdf787 --- /dev/null +++ b/tests/issue518/string.cfc @@ -0,0 +1,10 @@ +component displayname="string" extends="service" accessors="true" output="false" { + + + property name="stackLog"; + + + public function init() { + return this; + } +}