Posted on Leave a comment

Flip 0 and 1

As a programmer you often have to track the state of something. Let’s say your application shows a report for night and a different report for day.You would want a variable that lets you know if you are looking at the nighttime report or the daytime report. Lengthy programming discourse follows.

<cfset reporttype = "night">

Adding comments is essential to good code

<cfset reporttype = "night"> <!--- possible values: night, day --->

When you enter your template, let’s call it foo.cfm, your variable reporttype is not going to be defined. We can assume that you have come to foo.cfm from a form (method post) or perhaps a url (method get). You may be expecting form.reporttype to contain the value of "night" or "day" or perhaps you are expecting url.reporttype.

sidebar

Scoping variables is very important to quality code. ColdFusion is typeless but that is another discussion. ColdFusion (CF) is very forgiving when it comes to misusing variables and variable scopes. When setting a variable without a scope, such as
<cfset bar = 3>, CF assumes you are creating a local variable. The local variable scope in CF is called variables. So the cfset statement above is equivalent to
<cfset variables.bar = 3>.

Variables should always be scoped to reduce errors and improve performance; however, there one exception (demonstrated within the article) which can be employed to take advantage of CF’s forgiveness of unscoped code to allow a template to accept multiple points of entry for flexibility and code reuse. The caveat to this technique is being aware of security issues.

Barry Moore of Peachpit offers some further, yet dated, discussion in his artilce Using Variables in ColdFusion. See also AdoMedia’s Reserved Words and Variables and, more relevantly, Using ColdFusion Variables.

Unscoped variables are searched in the following order:

  1. Function local (UDFs and CFCs only)
  2. Arguments
  3. Variables (local scope)
  4. CGI
  5. Cffile
  6. URL
  7. Form
  8. Cookie
  9. Client

Because CF has to search through each of these scopes until finding your unscoped variable, you can increase performance by scoping the variable.

With your current approach you are going to have to test for the existence of the form input and test for the existence of url input then define the variable appropriately.

<cfif IsDefined("form.reporttype")>
   <cfset variables.reporttype = form.reporttype>
<cfelseif IsDefined("url.reporttype")>
   <cfset variables.reporttype = url.reporttype>
<cfelse>
   <cfset variables.reporttype = "day">
</cfif>

I’ve intentionally left that code uncommented. So far this code is looking pretty ugly and that’s without even digressing into a discussion on McCabe’s Cyclomatic Complexity.

Let’s assume our goal is to reuse the code in foo.cfm for both night reports and day reports. That means the only thing that will change between the two is the variable reporttype. So we can create a hyperlink to foo.cfm to make the change.

<a href="foo.cfm?reporttype=day">Show Day Report</a>

That assumes we are already on the night report. So to be able to view either the night or day report requires a second url.

<a href="foo.cfm?reporttype=day">Show Day Report</a>
<a href="foo.cfm?reporttype=night">Show Night Report</a>

Of course if we are looking at the day report it does not make much sense to show a link to that same report.

<cfif reporttype EQ "night">
   <a href="foo.cfm?reporttype=day">Show Day Report</a>
<cfelse>
   <a href="foo.cfm?reporttype=night">Show Night Report</a>
</cfif>

Let’s take a moment to look at the logic flaw in that code. We test for the reporttype of "day" and make the asumption that if reporttype is not day then it must be "night." If a malicious or curious user were to enter a different word into the url our report may fail. This statement assumes a query or decision will be based upon reporttype.

http://test.com/foo.cfm?reporttype=breakcode

That brings us to number one on The Open Web Application Security Project‘s Top Ten: Unvalidated Input but that is a different topic.

The code we have written, in theory, if coupled with some additional code to actually output a report, will now switch from the daytime report to the nighttime report when one of the links is clicked or a form has the action of foo.cfm and an input field named reporttype. I contend the above code is written as a "scripter" would write. One line leads to the next. Logic is created as needed without regard to the overall picture. A "programmer" is going to examine the overall logic and look to reduce complexity whenever possible.

I think IsDefined() is a horrible function that should only be used in rare exceptions. A programmer should be in control of the application and know what is defined. By defining variables and assigning defaults at the top of a template, code becomes more readible and easier to debug. When variables are haphazardly instantiated randomly within a template and logic is decided based upon whether or not a variable exists, coding, maintenance and testing takes much greater effort. That said, <cfparam> is our friend!

cfparam – Tests for the existence of a parameter (that is, a variable), validates its data, and, if a default value is not assigned, optionally provides one.
Syntax:
<cfparam
   name = “param_name”
   type = “data_type”
   default = “value”
   max = “value”
   min = “value”
   pattern = “regular expression”
>
Note: min, max and pattern attributes were added to CFMX 7
Note: Data type validations of creditcard, email, eurodate, float, integer, range, regex, regular_expression, ssn, social_security_number, time, URL, USdate, XML, and zipcode were added to CFMX 7.

Let’s re-write our foo.cfm application as bar.cfm. Since we know our form will submit a variable called reporttype to bar.cfm, we can anticipate this and predefine a value incase bar.cfm is accessed directly.

<cfparam name="form.reporttype" default="day">

note

Since 0 and 1 can arbitarily represent day or night, I like to derive meaning from the potential values. In the programming world, zero typically represents an off state (or false) while 1 represent an on state (or true). Since night is dark, like with the lights off, I choose 0 for night.

That line of code assures that if a form is submitted to bar.cfm, or bar.cfm is accessed directly, that a variable named reporttype with the scope form exists.

Now, let us start thinking about this as a programmer. We know we want to use the same template (bar.cfm) to show the daytime report and the nighttime report. We know that we want to use the variable reporttype to determine which of the two reports to display. We know that computers process numbers rather than text more easily. So let’s designate our reports with 0 and 1. 0 will represent night and 1 will represent day.

<cfparam name="form.reporttype" default="1">
<!--- 0- night; 1(default)- day --->

That cfparam is expecting our template to be the action of a form (method = post). If we come to our template via url (method = get), our application will fail. By eliminating the variable scope, we can access our application by both form and url.

<cfparam name="reporttype" default="1">

This is the only time I would recommend ever using unscoped variables! Please see the sidebar for more information.

Since we are using numbers, we can think mathematically. We know that if we subtract one from day we get night (1 – 1 = 0) and that if we add one to night we get day (0 + 1 = 1). We do not want to have to resort to a conditional statement to change reports; ie. no if-then-else statement. If we restate the equation in words instead of numbers, a solution quickly presents itself. "Take the current state then change it by one unit to get new state." If we subtract from night instead of adding we almost have our result (0 – 1 = -1). All that is needed is the absolute value and we can change reports with a single line of code.

<a href="foo.cfm?reporttype=<cfoutput>#Abs(reporttype-1)#</cfoutput>"> Show Other Report </a>

Of course, "Show Other Report" is not very clear so we will end up with one conditional statement to show the appropriate text. I advocate a switch statement over an if-then-else statement.

Final Code

<cfparam name="reporttype" default="1">
<!--- 0- night; 1(default)- day --->
<a href="foo.cfm?reporttype=<cfoutput>#abs(reporttype-1)#</cfoutput>">
Show
   <cfswitch expression="#reporttype#">
      <cfcase value="0"> <!--- night --->
         Night
      <cfcase>
      <cfcase value="1"> <!--- day --->
         Day
      <cfcase>
   </cfswitch>
Report
</a>

In the end, we have a very functional application with testable code that has minimal complexity.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.