G'day:
I could put this in a gist or just post the code directly, but it gives me something to write about today. Be wary of
structCopy()
. There's a bug been raised about it not working on ColdFusion scopes, which Adobe don't quite seem to be able to understand. The ticket is
3815793.
Firstly: some background. Don't use
structCopy()
. It's a shitty function, which doesn't do anything useful, coherently. Consider this demonstration:
// demo.cfm
test("Baseline");
test("Original updated", function(){
original.colours.black = "*****mangu*****";
});
test("Original added to @ top level", function(){
original.numbers.three = "*****toru*****";
});
test("Original added to @ inner level", function(){
original.colours.green = "*****kakariki*****";
});
function test(label,process){
writeOutput("<h3>#label#<h3>");
makeStructures();
isDefined("process") ? process() : false;
writeDump(var={original=original,ref=ref,copy=copy,dupe=dupe});
writeOutput("<hr>");
}
function makeStructures(){
original = {
colours = {
black = "pangu"
}
};
ref = original;
copy = structCopy(original);
dupe = duplicate(original);
}
Notes:
- I have a function
test()
…
- …which creates a test struct…
- …and performs an operation on it…
- …dumping out the results.
-
I perform a number of tests:
- A baseline
- Changing a value in a sub-struct
- Adding a value at the top level
- Adding a value in a sub-struct
And here are the results:
Baseline
struct |
COPY |
|
DUPE |
|
ORIGINAL |
|
REF |
|
Original updated
struct |
COPY |
struct |
COLOURS |
struct |
BLACK | *****mangu***** |
|
|
DUPE |
|
ORIGINAL |
struct |
COLOURS |
struct |
BLACK | *****mangu***** |
|
|
REF |
struct |
COLOURS |
struct |
BLACK | *****mangu***** |
|
|
Original added to @ top level
struct |
COPY |
|
DUPE |
|
ORIGINAL |
struct |
COLOURS |
|
NUMBERS |
struct |
THREE | *****toru***** |
|
|
REF |
struct |
COLOURS |
|
NUMBERS |
struct |
THREE | *****toru***** |
|
|
Original added to @ inner level
struct |
COPY |
struct |
COLOURS |
struct |
BLACK | pangu |
GREEN | *****kakariki***** |
|
|
DUPE |
|
ORIGINAL |
struct |
COLOURS |
struct |
BLACK | pangu |
GREEN | *****kakariki***** |
|
|
REF |
struct |
COLOURS |
struct |
BLACK | pangu |
GREEN | *****kakariki***** |
|
|
The reference copy and the
duplicate()
works as one would expect:
- the reference copy results in two references pointing to the same struct, so any changes to one is reflected in the other;
- the duplicate is a completely new struct, so changes to the original have no effect on the duplicate.
However
structCopy()
does something completely useless: it duplicates the
top level of the struct, but what it copies are the references, not the values. So this means if one alters the top level of the original, it's
not reflected in the copy; if one alters any of the internal structs, then that
is reflected in the copy.
Fence-sitting functionality like this serves no purpose, and simply causes confusion.
But that's not even the problem here. This is documented behaviour. It's how Allaire decided
structCopy()
ought to work.
The issue is that
structCopy()
doesn't even copy the
top level keys when using it on (some?) ColdFusion scopes.
Repro:
// urlBug.cfm
URL.testKey = "value set in URL scope";
writeDump(var=URL, label="URL scope");
writeOutput('<br><div style="padding-left: 20px;">');
copy = structCopy(URL);
writeDump(var=copy, label="initial state of copy of URL scope");
copy.testKey = "value set in copy";
writeDump(var=copy, label="updated state of copy");
writeOutput('</div><br>');
writeDump(var=URL, label="URL scope after change made to copy");
Output on ColdFusion (on Railo it works correctly):
URL scope - struct |
TESTKEY | value set in URL scope |
initial state of copy of URL scope - struct |
TESTKEY | value set in URL scope |
updated state of copy - struct |
TESTKEY | value set in copy |
URL scope after change made to copy - struct |
TESTKEY | value set in copy |
Note how the change to the the
value in the URL scope. Let's just compare that to using a struct instead of the URL scope:
// withStruct.cfm
struct.testKey = "value set in struct";
writeDump(var=struct, label="struct");
writeOutput('<br><div style="padding-left: 20px;">');
copy = structCopy(struct);
writeDump(var=copy, label="initial state of copy of struct");
copy.testKey = "value set in copy";
writeDump(var=copy, label="updated state of copy");
writeOutput('</div><br>');
writeDump(var=struct, label="struct after change made to copy");
The only difference there is that i'm using a struct instead of the URL scope. And the output is now as we'd expect:
struct - struct |
TESTKEY | value set in struct |
initial state of copy of struct - struct |
TESTKEY | value set in struct |
updated state of copy - struct |
TESTKEY | value set in copy |
struct after change made to copy - struct |
TESTKEY | value set in struct |
Changes to the copy at that top level do not impact the original struct. So that demonstrates the bug, Adobe. I can't help but think the original guidance offered should have been enough:
Steps to Reproduce:
1) do a form post
2) make a structcopy
3) update form key of copy
4) dump / output form
I don't see how that's unclear. Although it does depend on them actually reading what's in front of them, which I am finding to not be one of their strengths.
So I dunno what the chances are of them reading all this, but hey... as the saying goes "I can lead the horse to water (I just can't hold it's head under until it stops struggling)".
--
Adam