While browsing the CF Forums today, I found out that another Duplicate bug exists. This is a bit sad as Duplicate() has had issues in the past and I thought it was "perfect" now (ignoring the fact that it breaks on CFCs). It looks like you should carefully consider what you are duplicating now until a hotfix is (hopefully!) released. The following example demonstrates the bug:
<cfset s2 = StructNew()>
<cfset s2.numb = 12>
<cfset s2.string = "hello2">
<cfdump var="#s2#" label="s2 struct">
<p>
<cfset s = StructNew()>
<cfset s.array = ArrayNew(1)>
<cfset s.array[1] = 22>
<cfset s.array[2] = "my array">
<cfset s.array[3] = Duplicate(s2)>
<cfset s.numb = 11>
<cfset s.string = "hello">
<cfdump var="#s#" label="S struct">
<p>
<cfset s3 = Duplicate(s)>
<cfdump var="#s3#" label="S3 struct - duplicate from S">
<p>
<cfset StructDelete(s3.array[3],"numb")>
<cfdump var="#s3#" label="s3 after deleting s3.array[3].numb">
<p>
<cfdump var="#s#" label="S struct after deleting from S3 (deleted s3.array[3].numb)">
<cfset s2.numb = 12>
<cfset s2.string = "hello2">
<cfdump var="#s2#" label="s2 struct">
<p>
<cfset s = StructNew()>
<cfset s.array = ArrayNew(1)>
<cfset s.array[1] = 22>
<cfset s.array[2] = "my array">
<cfset s.array[3] = Duplicate(s2)>
<cfset s.numb = 11>
<cfset s.string = "hello">
<cfdump var="#s#" label="S struct">
<p>
<cfset s3 = Duplicate(s)>
<cfdump var="#s3#" label="S3 struct - duplicate from S">
<p>
<cfset StructDelete(s3.array[3],"numb")>
<cfdump var="#s3#" label="s3 after deleting s3.array[3].numb">
<p>
<cfdump var="#s#" label="S struct after deleting from S3 (deleted s3.array[3].numb)">
Archived Comments
I've had problems with duplicate as well. I really wanted to use it to copy my application and session scopes to the request scope. Problem is when any new vars added to request.Ses. When using duplicate() to create a deep copy of request.Ses it will not copy any new vars...only ones that exsisted in the session scope first. Below is my work around however...bad.
<cflock scope="application" type="exclusive" timeout="10">
<cfscript>
StructClear(application);
application.foo = StructNew();
structinsert(application.foo, "Test1", 1111);
structinsert(application.foo, "Test2", 2222);
structinsert(application.foo, "Test3", 3333);
</cfscript>
<cfdump label="Application Scope - BEFORE" var="#application#">
<cfset request.app = Duplicate(application)>
<cfscript>
request.app.bar = StructNew();
structinsert(request.app.bar, "Test1", 1111);
structinsert(request.app.bar, "Test2", 2222);
structinsert(request.app.bar, "Test3", 3333);
StructUpdate(request.app.foo, "Test1", 9999);
</cfscript>
<cfdump label="Request.App Scope" var="#request.App#">
<cfset StructAppend(application, Duplicate(request.App), "yes")>
<cfdump label="Application Scope - After" var="#application#">
<cfscript>
StructUpdate(request.App.bar, "Test1", "ha");
</cfscript>
<cfdump label="Application - After Change - Is by value or reference" var="#application#">
<cfdump label="Request.App Scope - After Change - Is by value or reference" var="#request.App#">
</cflock>
I'm afraid I don't see what you mean. Your last update to request.app.bar does NOT modify Application so the duplicate looks to be working ok.
I apologize for my horribly written comment.
Without writting the locks around this, this is what I used to do.
In Application.cfm -
<cfset request.Ses = Duplicate(session)>
<cfset request.App = Duplicate(application)>
In the OnRequestEnd.cfm -
<cfset session = StructCopy(request.Ses)>
<cfset session = StructCopy(request.App)>
I actually learned this trick from you, Mr. Camden in Hewitt's Core ColdFusion 5 book. Thanks for the tip.
I was just wondering why you can't use Duplicate() instead of StructCopy() in the OnRequestEnd. I've tried duplicate() in the OnRequestEnd. Say you copy your session into the request.Ses scope and on your template you create a few new variables in the request.Ses that don't exist yet in the session scope. Using Duplicate will not "copy" them into the session scope unless that structure key exists in it. Using Duplicate() is pretty useless because any new variables you want to add have to be added in the session scope...which sort of defeats the purpose of not having to lock around everything.
The following code is an example:
<!--- Set a var --->
<cfset session.foo = "foo">
<!--- Dump the session scope --->
<cfdump label="Session Scope - Before Duplicate" var="#session#">
<!--- Now dup it to the request.ses --->
<cfset request.Ses = Duplicate(session)>
<!--- Add a new var that doesn't exist in the session scope yet ---->
<cfset request.Ses.bar = "bar">
<!--- Dump the request.Ses scope --->
<cfdump label="Request.Ses Scope - Dump" var="#request.Ses#">
<!--- Dup the request.Ses --->
<cfset session = Duplicate(request.Ses)>
<!--- Dump the session scope --->
<cfdump label="Session Scope - After Duplication" var="#session#">
As you can see request.Ses.bar has not been copied back to the session scope. I am curious why this happends.
StructCopy() doesn't produce a "deep copy" of nested structures which can end up being pretty annoying because it would force me to use arrays a lot.
In my previous post, I showed my "solution" for this...however I don't know if it impacts performance a lot.
I usually use:
<cfset StructAppend(session, Duplicate(request.Ses), "yes")>
Thanks for hearing me out,
PJF
I believe the issue you are seeing is that Session is more than a struct, its a scope. I noticed if you change your last duplicate to
cfset structAppend(session, request.Ses, true)
then it works correctly.