One of the things that sometimes trips me up is the whole idea of references. I'm not talking about stuffy books in the library. I'm talking about the idea that setting one variable to another can sometimes create a pointer to that item rather than a copy. Now before we chat about it any further we should get square on the difference between a "primitive" or "simple" data type and a "complex" or "emotionally involved" data type.
I like to think of "primitive" data types as "one level" members of any scope. I also like to think of them as little cave men in loin cloths running around and clubbing defenseless mammoths - but that is perhaps too much information. There's actually a pretty short list of primitive data types. In fact, "short" actually is one of the data types I believe. In the CF world a primitive Datatype would be a string, number or possibly a date. Of course the actual list is more like "short, long, float, double, int, string, byte" - but it is probably more useful (for the purpose of this post) to consider String, Number and Date. Check out this example.
The "isSimpleValue()" function can help you sort out the type of variable. If it returns "YES" then you have a primitive (congratulations you can begin to look for remedial schooling). So, if we change our variable to a structure (for example):
Now we have a non-primitive value. Notice the "isStruct()" above. It's worth noting that there are a bunch of additional "is" functions for object typing (isQuery, isArray etc).
Ok, here's the final piece of the puzzle. When you use cfset to create a variable on the left side that is assigning to another variable on the right one of 2 things happens. If the right side variable is a primitive the value is copied from the right to the left. If it is not a primitive but rather a complex data type or object then the value is not copied into your new variable container. Instead, CF assigns a pointer to it - a book mark - based on your new variable name.
All righty, we get what is happening with primitives. A simple value is being passed back and forth. But what is happening with the complex types? Actually the data doesn't move at all. Instead, there are now 2 variables pointed to the same spot in memory and the members of one belong to the other (sort of like when Alamo and National merged). Try this:
Now admittedly the example above is probably not very useful - but there are some cases when it really comes into play. Consider the Application scope for example. Now be patient, this example requires some set up. First, we have the following Application scope code - note, I'm using application.cfm for simplicity here.
This code will work and return users to the logged in user. Under a light load it will even return the correct users for a time. But as more and more requests are received some users are going to see other users information. Why? Simply because the variable "getusers" is going to persist in the application scope and be overwritten with each new request. Since the code testobj = application.testobj really just returns a reference to an object in the application scope, this will mean (eventually) that 2 users will hit at nearly the same time and get the same information (whichever thread was last to write to "result").
The fix in this case is to make the "getusers" variable a member of the function so that it goes out of scope as soon as the function returns - like so:
The lesson here is greater than the example however. When you set variables be sure you know what the underlying consequences are likely to be. Especially when dealing with the application and server scopes you should evaluate very carefully whether this reference approach has merit and is needed. If it is needed you will need to thoroughly test under load to be sure you have not created holes where you data can seep into unintended areas. Hope this helps - happy coding :).