Before ColdFusion 9, there was never really a sense of NULL values in ColdFusion. If you had a NULL value returned from a Java method, it simply destroyed the variable into which it was stored. If you had a NULL value come back from a query, it was presented as an empty string. While that hasn't really changed, ColdFusion 9 now has a method that might make dealing with null values a bit easier. The new IsNull() method takes a variable and returns a boolean as to whether or not that variable is null.
Seeing as this is a completely new concept in ColdFusion 9, I think we need to do some thorough testing. To make this testing easier, I defined a ColdFusion function that returns VOID (NOTE: Remember, VOID is NULL in the ColdFusion world):
<cffunction name="returnNull" access="public" returntype="void" output="false" hint="I simply return void / null."> <!--- Return null. ---> <cfreturn /> </cffunction>
As you can see, this method returns without a given value. This will return a NULL result. With this in place, I ran my first test:
<!--- Get a null value. ---> <cfset request.value = returnNull() /> <!--- Test to see if value is null. ---> Null: #isNull( request.value )#<br /> <!--- Test to see if the key exists. ---> StructKeyExists: #structKeyExists( request, "value" )#<br />
Our variable, REQUEST.Value, contains the NULL result of the ReturnNull(). And, when we run the above code, we get the following output:
That's pretty cool. But, I wondered if ColdFusion NULL values were any different than Java NULL values. As such, in my next test, I decided to see how this worked with JavaCast():
<!--- Get a Java null value. ---> <cfset javaNull = javaCast( "null", 0 ) /> <!--- Test to see if value is null. ---> Null: #isNull( javaNull )#<br /> <!--- Test to see if the key exists. ---> StructKeyExists: #structKeyExists( variables, "javaNull" )#<br />
Here, I am using JavaCast() to create a Java NULL value. And, when we run the above code, we get the following output:
Pretty slick! When I saw this result, I started to think - if the StructKeyExists() keeps returning NO, then how I am even referring to the variable being tested? I mean, it seems like a bit of a weird chicken-and-egg situation: a variable needs to exist to be referred to but I need to check a variable which I might not be able to refer to because it might not exist? After thinking about this for a second, I tried referring to a completely made up variable:
<!--- Test to see if value is null. ---> Null: #isNull( request.whatIsUpMyMan )#<br /> <!--- Test to see if the key exists. ---> StructKeyExists: #structKeyExists( request, "whatIsUpMyMan" )#<br />
Here, I'm referring to a variable name which I didn't even create previously. And, when we run the above code, we get the following output:
So, I guess we can pass any kind of variable name into this new IsNull() method. Because this action would traditionally trigger an exception, I wonder if it is doing some sort of behind-the-scenes error handling. And, if so, can we pass array-notation values to it. Let's try using array notation with a struct:
<!--- Test to see if value is null. ---> Null: #isNull( request[ "whatIsUpMyMan" ] )#<br /> <!--- Test to see if the key exists. ---> StructKeyExists: #structKeyExists( request, "whatIsUpMyMan" )#<br />
Unfortunately, this time, we get the following ColdFusion error:
Element whatIsUpMyMan is undefined in a Java object of type class coldfusion.runtime.RequestScope.
This doesn't work with a struct, but, what about an array with an undefined value:
<!--- Create an array of simple vaules. ---> <cfset data = [ 1, 2, 3 ] /> <!--- Destroy one of the values. ---> <cfset data[ 2 ] = returnNull() /> <!--- Check the existence of that array index. ---> Null: #isNull( data[ 2 ] )#
This time we get the ColdFusion error:
Element 2 is undefined in a Java object of type class coldfusion.runtime.Array.
OK, so we can't use array notation with structs or arrays. It looks like we can only use valid "variable names" or those that would pass the "variablename" validation in ColdFusion's CFParam tag. But, I wonder, are there any limitations to the domain of the base object in question? Meaning, does this work only on ColdFusion objects? Or, can I use this on Java objects as well? Let's have a look:
<!--- Create a java object. ---> <cfset javaObject = createObject( "java", "java.lang.StringBuffer" ) /> <!--- Check for a null key. ---> Null: #isNull( javaObject.madeUpKey )#<br />
Here, I am creating a Java object and then referring to an undefined property of it. And, when we run this code, we get the following output:
Ok cool - so it looks like we can use this with any type of object as long as it's stored in a ColdFusion variable.
But, what about queries? Null values have always been massaged by ColdFusion, presenting themselves as strings. I wanted to see if that has changed and IsNull() might work with queries where NULL values are present:
<!--- Create our query object. ---> <cfset friends = queryNew( "name", "cf_sql_varchar" ) /> <!--- Add two rows. ---> <cfset queryAddRow( friends, 2 ) /> <!--- Set a valid value only on the second row. ---> <cfset friends[ "name" ][ 2 ] = javaCast( "null", 0 ) /> <cfset friends[ "name" ][ 2 ] = javaCast( "string", "Tricia" ) /> <!--- Loop over the query to check for null values. ---> <cfloop query="friends"> Null( #friends.currentRow# ): #isNull( friends.name )#<br /> </cfloop>
After we manually construct our query, our first record value should be null and our second record value should contain, "Tricia." When we run the above code, we get the following output:
Null( 1 ): NO
Null( 2 ): NO
It looks like IsNull() does not interact with query values in any special way - NULL column data is still presented as empty strings.
As a final test, I wondered how IsNull() interacted with scope searching, which has always been a huge part of ColdFusion. As you all know, when you refer to an unscoped variable in ColdFusion, the engine searches through a number of scopes to find it. For example, if you refer to "foo", ColdFusion will search through the VARIABLES, FORM, URL, COOKIE, ARGUMENTS, LOCAL, Query, and a number of other scopes looking for it. So, the question becomes, is IsNull() smart enough to know that. To test this, let's try using one of the most common scope searching scenarios - the FORM scope:
<!--- Store a value in the FORM scope. ---> <cfset FORM.tricia = "Awesome!" /> <!--- First, to test, let's just output the value to make sure ColdFusion knows how to find it (searching scopes). ---> Tricia: #tricia#<br /> <!--- Now, let's check to see if the form value is null according to InNull(). ---> Null: #isNull( tricia )#
As you can see, we are simply referring to Tricia without the FORM scope. When we run the above code, we get the following output:
Hmm, ok. While at first, you might consider this a bug, it actually isn't. Because of the way ColdFusion searches through scopes, we can refer to the value contained within FORM.Tricia without using the FORM scoping; however, we're not truly referring to the variable, "tricia". As such, I think the above behavior is what is to be expected. That said, be sure you fully understand scope searching before you use IsNull() on variables without scopes. Even better, always scope your non-page-scope variables!
Ultimately, it looks like ColdFusion 9's new IsNull() method is nothing more than a short-hand notation for StructKeyExists(). But, I'm OK with that. Most of what I've blogged about so far in ColdFusion 9 has been about fairly small changes that will result in large performance and contentment increases among ColdFusion programmers. I believe that IsNull() is no different and will, in fact, being hugely appreciated.
Want to use code from this post? Check out the license.