Monday, June 29, 2009

That which we call a structure, by any other name does not smell as sweet

I came across an interesting question on the forums today about a quirk of the FORM scope. On the off chance you have not already encountered this one, the poster discovered that StructCount() does not return the correct value after keys are deleted using StructDelete.

To demonstrate, create a simple form with one or more fields. When the form is submitted, delete the keys one by one. Check the StructCount() value on each iteration. You will notice the resulting counts are incorrect. The size of the FORM structure always remains the same. Even after keys are deleted.


<cfif structKeyExists(FORM, "testThis")>
<cfoutput>
<cfloop list="#form.fieldNames#" index="key">
<cfset StructDelete(FORM, key)>
Deleted key #key#. New StructCount(FORM) = #StructCount(FORM)#<br>
</cfloop>
</cfoutput>
</cfif>

<cfoutput>
<h3>Test Form</h3>
<form action="#CGI.SCRIPT_NAME#" method="post">
<cfloop from="1" to="10" index="x">
<input type="text" name="field#x#" value="#x#">
</cfloop>
<input type="submit" name="testThis">
</form>
</cfoutput>
Interestingly, if you copy the form structure with duplicate(), then run the same code on the copied object, the results are correct. The reason is duplicate() returns a slightly different type of object. If you check the underlying class names, you will see that duplicate returns a coldfusion.runtime.Struct object. Whereas form is actually a coldfusion.filter.FormScope object.

<cfset copy = duplicate(FORM)>
<cfoutput>
FORM object type = #form.getClass().name#<br>
COPY object type = #copy.getClass().name#<br><br>
</cfoutput>

Out of curiosity, I used several different methods to verify the structure counts (some less than efficient). Since ColdFusion structures are java.util.Map objects, I also used the undocumented size() method to confirm the results. It too returned the wrong value. Though I do not know how StructCount() works internally, I suspect it uses size(). Which would explain the StructCount() bug.



It turns out the same problem applies to the URL scope. At least with ColdFusion 8. So while we tend to think of URL and FORM as indistinguishable from "regular" CF structures .. they are not. In this case, neither one came out smelling like a rose ;)

Complete Test Code
<cfif structKeyExists(FORM, "testThis")> 
<!--- create a deep copy of the FORM scope --->
<cfset copy = duplicate(FORM)>

<cfoutput>
<!--- Display the object types --->
<h3>Object Types:</h3>
FORM object type = #form.getClass().name#<br>
COPY object type = #copy.getClass().name#<br><br>

<table>
<tr><th rowspan="2">Action</th>
<th colspan="4" class="one">FORM Object</th>
<th colspan="4" class="two">Copy</th>
</tr>
<tr>
<td class="one">StructCount</td>
<td class="one">Size()</td>
<td class="one">ArrayLen()</td>
<td class="one">ListLen()</td>
<td class="two">StructCount</th>
<td class="two">Size()</th>
<td class="two">ArrayLen()</th>
<td class="two">ListLen()</td>
</tr>
<!--- Display the counts as each key is deleted --->
<cfloop list="#form.fieldNames#" index="key">
<cfset StructDelete(COPY, key)>
<cfset StructDelete(FORM, key)>
<tr>
<td>Deleted key #key#</td>
<td>#StructCount(FORM)#</td>
<td>#FORM.size()#</td>
<td>#ArrayLen(StructKeyArray(FORM))#</td>
<td>#ListLen(StructKeyList(FORM))#</td>
<td>#StructCount(COPY)#</td>
<td>#COPY.size()#</td>
<td>#ArrayLen(StructKeyArray(COPY))#</td>
<td>#ListLen(StructKeyList(COPY))#</td>
</tr>
</cfloop>
</table>
</cfoutput>
</cfif>


<cfoutput>
<h3>Test Form</h3>
<form action="#CGI.SCRIPT_NAME#" method="post">
<cfloop from="1" to="10" index="x">
<input type="text" name="field#x#" value="#x#">
</cfloop>
<input type="submit" name="testThis">
</form>
</cfoutput>

0 comments:

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep